Packages
mob_new
0.1.2
0.4.20
0.4.19
0.4.18
0.4.17
0.4.16
0.4.15
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
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.0
0.1.45
0.1.44
0.1.43
0.1.42
0.1.40
0.1.33
0.1.30
0.1.29
0.1.28
0.1.27
0.1.26
0.1.25
0.1.24
0.1.23
0.1.21
0.1.20
0.1.19
0.1.18
0.1.17
0.1.16
0.1.15
0.1.14
0.1.12
0.1.11
0.1.10
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
Project generator for the Mob mobile framework
Current section
Files
Jump to
Current section
Files
lib/mix/tasks/mob.new.ex
defmodule Mix.Tasks.Mob.New do
use Mix.Task
@shortdoc "Create a new Mob mobile app project"
@moduledoc """
Creates a new Mob project with Android and iOS boilerplate.
mix mob.new APP_NAME [--no-install] [--dest DIR]
## Options
* `--no-install` — skip running `mix deps.get` after generation
* `--dest DIR` — create project in DIR (default: current directory)
## What gets generated
APP_NAME/
mix.exs
lib/APP_NAME/app.ex
lib/APP_NAME/hello_screen.ex
android/
settings.gradle
build.gradle
app/
build.gradle
src/main/
AndroidManifest.xml
java/com/mob/APP_NAME/MainActivity.java
ios/
beam_main.m
Info.plist
After generation, run:
cd APP_NAME
mix mob.install # icon generation + any first-run setup
"""
@switches [no_install: :boolean, dest: :string]
@impl Mix.Task
def run(argv) do
{opts, args, _} = OptionParser.parse(argv, strict: @switches)
app_name =
case args do
[name | _] -> name
[] -> Mix.raise("Usage: mix mob.new APP_NAME")
end
unless valid_app_name?(app_name) do
Mix.raise("App name must be lowercase letters, digits, and underscores only (e.g. my_app).")
end
dest_dir = opts[:dest] || "."
project_dir = Path.join(dest_dir, app_name)
Mix.shell().info([:green, "* creating ", :reset, project_dir])
case MobNew.ProjectGenerator.generate(app_name, dest_dir) do
{:error, reason} ->
Mix.raise(reason)
{:ok, project_dir} ->
print_created_files(project_dir, app_name)
unless opts[:no_install] do
fetch_deps(project_dir)
end
print_next_steps(app_name, opts[:no_install])
end
end
# ── private ──────────────────────────────────────────────────────────────────
defp fetch_deps(project_dir) do
Mix.shell().info("")
Mix.shell().info("Fetching dependencies...")
mix = System.find_executable("mix")
case System.cmd(mix, ["deps.get"], cd: project_dir, into: IO.stream()) do
{_, 0} -> :ok
{_, _} -> Mix.shell().info([:yellow, "* deps.get failed — run it manually inside #{project_dir}", :reset])
end
end
defp print_created_files(project_dir, app_name) do
files = [
"mix.exs",
"lib/#{app_name}/app.ex",
"lib/#{app_name}/hello_screen.ex",
"android/settings.gradle",
"android/build.gradle",
"android/app/build.gradle",
"android/app/src/main/AndroidManifest.xml",
"android/app/src/main/java/com/mob/#{app_name}/MainActivity.java",
"ios/beam_main.m",
"ios/Info.plist"
]
Enum.each(files, fn f ->
Mix.shell().info([:green, "* creating ", :reset, Path.join(project_dir, f)])
end)
end
defp print_next_steps(app_name, no_install) do
install_hint =
if no_install,
do: "\n mix deps.get",
else: ""
Mix.shell().info("""
Your Mob app #{app_name} is ready!
#{install_hint}
cd #{app_name}
mix mob.install # generates app icon + first-run setup
First deploy — edit mob.exs and android/local.properties with your local
paths, then build native binaries (APK + iOS app), install on device,
and push BEAMs:
mix mob.deploy --native # first time, or after native code changes
Day-to-day development — just push changed BEAMs, no native rebuild needed:
mix mob.deploy # fast push + restart
mix mob.watch # auto-push on file save
""")
end
defp valid_app_name?(name), do: Regex.match?(~r/^[a-z][a-z0-9_]*$/, name)
end