Packages
mob_dev
0.3.28
0.6.23
0.6.22
0.6.21
0.6.20
0.6.19
0.6.18
0.6.17
0.6.16
0.6.15
0.6.14
0.6.13
0.6.12
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.17
0.5.16
0.5.15
0.5.14
0.5.13
0.5.12
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.0
0.3.37
0.3.35
0.3.34
0.3.33
0.3.28
0.3.26
0.3.23
0.3.21
0.3.19
0.3.18
0.3.17
0.3.16
0.3.15
0.3.14
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.18
0.2.17
0.2.15
0.2.14
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.0
Development tooling for the Mob mobile framework
Current section
Files
Jump to
Current section
Files
lib/mix/tasks/mob.release.ex
defmodule Mix.Tasks.Mob.Release do
use Mix.Task
@shortdoc "Build a signed iOS .ipa for App Store / TestFlight"
@moduledoc """
Builds a release-signed iOS `.ipa` ready to upload to App Store Connect.
mix mob.release
## Output
`_build/mob_release/<App>.ipa`
Use `mix mob.publish` to upload it to TestFlight.
## Prerequisites
1. Apple Developer Program membership (paid, $99/yr)
2. An "Apple Distribution" certificate in your keychain
(Xcode → Settings → Accounts → Manage Certificates → +)
3. An App Store provisioning profile for your bundle ID, downloaded
to `~/Library/Developer/Xcode/UserData/Provisioning Profiles/`.
`mix mob.provision --distribution` automates the profile download.
## What it does
1. Resolves a distribution signing identity + App Store profile
(auto-detect, or `:ios_dist_sign_identity` / `:ios_dist_profile_uuid`
in `mob.exs`)
2. Generates `ios/release_device.sh` and runs it:
- Compiles BEAMs and copies them into the OTP runtime
- Builds native sources with `-DMOB_RELEASE` so `mob_beam.m`
drops EPMD + the distribution BEAM args
- Links the iOS device binary, no EPMD object files
- Signs the `.app` with the distribution identity (no `get-task-allow`)
- Packages as `Payload/<App>.app` zipped into `<App>.ipa`
The shipped `.ipa` runs the full BEAM but with no Erlang distribution
surface — `Mob.Dist.ensure_started/1` no-ops at runtime when
`MOB_RELEASE=1`.
"""
@impl Mix.Task
def run(_args) do
case :os.type() do
{:unix, :darwin} -> :ok
_ -> Mix.raise("mix mob.release is only supported on macOS.")
end
unless File.dir?("ios") do
Mix.raise("No ios/ directory found. Run from the root of a mob iOS project.")
end
Mix.Task.run("compile")
case MobDev.Release.build_ipa() do
{:ok, path} ->
Mix.shell().info("")
Mix.shell().info("#{green()}✓ Release build complete#{reset()}")
Mix.shell().info(" IPA: #{cyan()}#{path}#{reset()}")
Mix.shell().info(" Size: #{file_size_human(path)}")
Mix.shell().info("")
Mix.shell().info("Next: #{cyan()}mix mob.publish#{reset()} to upload to TestFlight.")
{:error, reason} ->
Mix.raise(reason)
end
end
defp file_size_human(path) do
case File.stat(path) do
{:ok, %{size: bytes}} ->
cond do
bytes >= 1024 * 1024 ->
:io_lib.format("~.1fM", [bytes / (1024 * 1024)]) |> List.flatten()
bytes >= 1024 ->
:io_lib.format("~.1fK", [bytes / 1024]) |> List.flatten()
true ->
"#{bytes}B"
end
|> to_string()
_ ->
"?"
end
end
defp green, do: IO.ANSI.green()
defp cyan, do: IO.ANSI.cyan()
defp reset, do: IO.ANSI.reset()
end