Packages
mob_dev
0.3.35
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.server.ex
defmodule Mix.Tasks.Mob.Server do
use Mix.Task
@shortdoc "Start the Mob dev server (localhost:4040)"
@moduledoc """
Starts the Mob dev server and opens it in the browser.
mix mob.server
mix mob.server --port 4040 # default port
The server provides:
- Live device status cards (Android + iOS simulator)
- Per-device deploy buttons ("Update" and "First Deploy")
- Streaming log panel (logcat / iOS simulator console)
The server runs until you press Ctrl+C.
For an interactive IEx session alongside the dashboard:
iex -S mix mob.server
## Under the hood
`mix mob.server` starts a Phoenix + Bandit supervision tree directly in the
Mix process — equivalent to `iex -S mix phx.server` for a Phoenix app, except
it starts the supervisor inline rather than through the application callback:
Application.ensure_all_started(:bandit)
Application.ensure_all_started(:phoenix_live_view)
Supervisor.start_link([
{Phoenix.PubSub, name: MobDev.PubSub},
MobDev.Server.Endpoint, # Bandit HTTP server on port 4040
MobDev.Server.DevicePoller, # polls adb + xcrun simctl
MobDev.Server.LogStreamerSupervisor, # logcat / simctl log streams
MobDev.Server.WatchWorker, # optional file-watch loop
...
], strategy: :one_for_one)
open "http://localhost:4040" # macOS: open, Linux: xdg-open
The endpoint uses `Bandit.PhoenixAdapter` instead of Cowboy, so there is no
`:plug_cowboy` dependency. Everything else is standard Phoenix LiveView.
"""
@default_port 4040
@impl Mix.Task
def run(args) do
{opts, _, _} = OptionParser.parse(args, switches: [port: :integer])
port = opts[:port] || @default_port
lan_ip = MobDev.Network.lan_ip()
configure_endpoint(port, lan_ip)
{:ok, _} = Application.ensure_all_started(:bandit)
{:ok, _} = Application.ensure_all_started(:phoenix_live_view)
children = [
{Phoenix.PubSub, name: MobDev.PubSub},
MobDev.Server.LogBuffer,
MobDev.Server.ElixirLogBuffer,
MobDev.Server.Endpoint,
MobDev.Server.DevicePoller,
MobDev.Server.LogStreamerSupervisor,
MobDev.Server.WatchWorker,
{Task.Supervisor, name: MobDev.Server.TaskSupervisor}
]
{:ok, sup} =
Supervisor.start_link(children, strategy: :one_for_one, name: MobDev.Server.Supervisor)
# Attach the Elixir logger handler now that PubSub and the buffer are up
MobDev.Server.ElixirLogger.attach()
local_url = "http://localhost:#{port}"
IO.puts("")
IO.puts("#{IO.ANSI.cyan()}=== Mob Dev Server ===#{IO.ANSI.reset()}")
IO.puts(" #{IO.ANSI.green()}#{local_url}#{IO.ANSI.reset()}")
if lan_ip do
lan_url = "http://#{:inet.ntoa(lan_ip)}:#{port}"
IO.puts(" #{IO.ANSI.green()}#{lan_url}#{IO.ANSI.reset()} ← open on phone")
IO.puts("")
IO.puts(MobDev.QR.render(lan_url))
end
IO.puts("")
open_browser(local_url)
if IEx.started?() do
# Unlink the supervisor from this task process so it survives after run/1 returns.
# Without this the supervisor exits when the Mix task process exits.
Process.unlink(sup)
IO.puts(
" #{IO.ANSI.green()}IEx ready.#{IO.ANSI.reset()} Elixir log output appears in the dashboard → Elixir panel."
)
IO.puts("")
else
IO.puts(
" Tip: run #{IO.ANSI.cyan()}iex -S mix mob.server#{IO.ANSI.reset()} for an interactive terminal."
)
IO.puts(" Press Ctrl+C to stop.")
IO.puts("")
Process.sleep(:infinity)
end
end
defp configure_endpoint(port, lan_ip) do
lan_url = if lan_ip, do: "http://#{:inet.ntoa(lan_ip)}:#{port}", else: nil
Application.put_env(:mob_dev, :dashboard_lan_url, lan_url)
Application.put_env(:mob_dev, MobDev.Server.Endpoint,
adapter: Bandit.PhoenixAdapter,
http: [ip: {0, 0, 0, 0}, port: port],
url: [host: "localhost", port: port],
server: true,
live_view: [signing_salt: "mob_dev_server_salt"],
secret_key_base: String.duplicate("mob_dev_secret_key_base_not_for_production_", 2)
)
end
defp open_browser(url) do
cmd =
case :os.type() do
{:unix, :darwin} -> "open"
{:unix, _} -> "xdg-open"
{:win32, _} -> "start"
end
Task.start(fn ->
# brief pause so the server is up before the browser hits it
:timer.sleep(500)
System.cmd(cmd, [url], stderr_to_stdout: true)
end)
end
end