Phoenix 1.7とElixirDesktopでスマホアプリを作る セットアップ編

# はじめに
本記事はPhoenix1.7をベースとして、Phoenixプロジェクトをネイティブアプリ化するライブラリElixirDesktopを使用し、
スマホアプリを作成する手順を紹介する記事になります

この記事では、環境セットアップと、iOS,Androidのビルド環境を整えます

ElixirDesktopでスマホアプリ作成シリーズ
1. [Phoenix1.7とElixirDesktopでスマホアプリ開発 セットアップ編](https://qiita.com/the_haigo/items/fa3906e45cf805ca38fa)
2. [Phoenix1.7とElixirDesktopでスマホアプリ開発 認証機能編](https://qiita.com/items/c831ff92b06b207d0dbf)
3. [Phoenix1.7とElixirDesktopでスマホアプリ開発 CRUD編](https://qiita.com/the_haigo/items/7472274168939bccbe91)
4. [Phoenix1.7とElixirDesktopでスマホアプリ開発 GPSと地図アプリ編](https://qiita.com/the_haigo/items/7d0e3e24ef215fba13c1)

※2023/3/1 Phoenix 1.7が正式版がリリースされたので修正しました
# ElixirDesktopについて
ElixirDesktopはネイティブ環境(iOS, Android)でPhoenixサーバーを起動し、WebViewで画面を表示するするアーキテクチャをとっています

![スクリーンショット 2022-11-15 15.04.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/06bfbd25-13dd-18a9-c17d-d498b205c8e8.png)

# Phoenix 1.7について
大きな変更として以下がありました
- デフォルトCSSがTawilwindになった
- phx.gen.auth のLiveView化
- .form以外のデフォルトのHeexのコンポーネントの追加
- パスのヘルパー機能にverified_routesが追加

verified_routesについては以下で記事にしています

https://qiita.com/the_haigo/items/bc848d80ff8fdf6ffa79

# 実行環境

Elixir 1.14.1
Elrang 25.0.4
Phoenix 1.7.0
ElixirDesktop 1.4.2

# setup
## Erlang
こちらにそって ElixirDesktopにopensslをあわせたErlangをインストールします

https://github.com/elixir-desktop/ios-example-app

一緒にxcodeでパッケージ管理ライブラリのcarthageもインストールします
```
brew install carthage　openssl@1.1
export DED_LDFLAGS_CONFTEST="-bundle"
export KERL_CONFIGURE_OPTIONS="--without-javac --with-ssl=$(brew --prefix openssl@1.1)"
asdf install erlang 25.0.4
```


## Phoenix
~~こちらに沿ってPhoenixを1.7.0-rcにします~~
1.7.0がリリースされたので以下で最新版がインストールされます

```
mix archive.install hex phx_new
```

# アプリ構成
- 認証付き
- CRUD
- Headerコンポーネント
- BottomTabコンポーネント
- DBはPostgresql

# Project作成
開発はiOS, Android, Phoenixの３つのプロジェクトを１つのフォルダで分けて開発します

わかりやすくするために一度phx.newしたあとにphoenixにリネームします

```
mkdir spotties
cd spotties
mix phx.new spotties
mv spotties phoenix
cd phoenix
mix ecto.create
```
gitはプロジェクト毎にしたほうが管理が楽なのでphoenix以下でgit initします
```
git init
git add .
git commit -m 'init'
```

```
iex -S mix phx.server
```

localhost:4000にアクセスしてPhoenix 1.7.0が無事起動することが確認できました

![スクリーンショット 2022-11-15 15.15.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/28d4c8da-5a6b-d695-1eae-f8806349dde2.png)



# Desktop化
こちらに沿ってやっていきます

https://qiita.com/the_haigo/items/0fd907d24fdde1905aa2
depsにdesktopとスマホ用にwxを追加します

```mix.exs
defmodule Spotties.MixProject do
  use Mix.Project
  defp deps do
    [
     ...,
     {:desktop, "~> 1.5"},
     {:wx, "~> 1.1", hex: :bridge, targets: [:android, :ios]}
    ]
  end
end
```

```
mix deps.get
```

もとからあるapplication.exを残して起動時のsupervisor周りを設定します
サンプルに習って設定ファイル等を保存するconfig_dirを掘る処理も追加します

```lib/spotties.ex
defmodule Spotties do
  use Application

  def config_dir() do
    Path.join([Desktop.OS.home(), ".config", "spotties"])
  end

  @app Mix.Project.config()[:app]
  def start(:normal, []) do
    File.mkdir_p!(config_dir())
    # DB周り
    {:ok, sup} = Supervisor.start_link([Spotties.Repo], name: __MODULE__, strategy: :one_for_one)
    # PubSubとEndpoint,session周り
    {:ok, _} = Supervisor.start_child(sup, SpottiesWeb.Sup)
    # Desktop周り
    port = :ranch.get_port(SpottiesWeb.Endpoint.HTTP)
    {:ok, _} =
      Supervisor.start_child(sup, {
        Desktop.Window,
        [
          app: @app,
          id: SpottiesWindow,
          title: "Spotties",
          size: {400, 800},
          url: "http://localhost:#{port}"
        ]
      })
  end
end
```
※ 2023/3/2追記
urlのport部分を4000にするとElixirDesktopのPhoenixではなく、ローカルのPhoenixにアクセスしてくれるのでエミュレータ等での開発はこちらが良いかと思います


```lib/spotties_web/sup.ex
defmodule SpottiesWeb.Sup do
  use Supervisor

  def start_link([]) do
    Supervisor.start_link(__MODULE__, [], name: __MODULE__)
  end

  def init([]) do
    children = [
      {Phoenix.PubSub, name: Spotties.PubSub},
      SpottiesWeb.Endpoint
    ]

    :session = :ets.new(:session, [:named_table, :public, read_concurrency: true])
    Supervisor.init(children, strategy: :one_for_one, max_restarts: 1000)
  end
end
```

~~EndpointをPhoenixからDesktopに変えて~~ Endpoint.urlがなくなったのでEndpointはそのままにして、session管理をcookieからetsに変更します
```lib/spotties_web/endpoint.ex
defmodule SpottiesWeb.Endpoint do
  use Phoenix.Endpoint, otp_app: :spotties

  @session_options [
    store: :ets,
    key: "_spotties_key",
    table: :session
  ]
  ...
end
```

作成したら mix.exsで起動させるモジュールを変更します

```diff_elixir:mix.ex
defmodule Spotties.MixProject do
  use Mix.Project

  def application do
    [
-      mod: {Spotties.Application, []},
+      mod: {Spotties, []},
      extra_applications: [:logger, :runtime_tools]
    ]
  end
end
```


# Desktopで起動
開発を行うdev環境として、wxwidgetsの設定をします
server: trueを追加します
```config/dev.exs
config :spotties, SpottiesWeb.Endpoint,
  # Binding to loopback ipv4 address prevents access from other machines.
  # Change to `ip: {0, 0, 0, 0}` to allow access from other machines.
  http: [ip: {127, 0, 0, 1}, port: 4000],
  ...,
  server: true # 追加
```
以下で起動します
```
iex -S mix
```
ホットリロードはできますが、リロードボタンがないのでホットリロードされないエラーになった場合は再起動が必要です

![スクリーンショット 2022-11-15 23.32.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/16cbcf7d-49d6-9834-7f14-0130b76d0c39.png)

# iOS,Android共通設定
runtime.exがあると起動に失敗するので runtime_disable.exにリネームします

スマホは prod環境で動かすので config/prod.exにDBとEndpointの設定を追加します

```config/prod.exs
config :spotties, Spotties.Repo,
  username: "postgres",
  password: "postgres",
  database: "spotties_dev",
  stacktrace: true,
  show_sensitive_data_on_connection_error: true,
  pool_size: 10

if Mix.target() == :ios do
  config :spotties, Spotties.Repo, hostname: "127.0.0.1"
end

if Mix.target() == :android do
  config :spotties, Spotties.Repo, hostname: "10.0.2.2"
end

config :spotties, SpottiesWeb.Endpoint,
  http: [ip: {127, 0, 0, 1}, port: 0],
  live_view: [signing_salt: "nt9pr/6W"],
  secret_key_base: "cVZaz98CBZNGr4dk7XhVqYpW49We6OKdzORLS20peE0ZK8Qv3ex6uOLujx/mXS80",
  server: true,
  cache_static_manifest: "priv/static/cache_manifest.json"
```

# iOS Simulatorで動かす
プロジェクトフォルダにios-exampleをcloneします
cloneしたらxcodeのライブラリもインストールします
```
git clone https://github.com/elixir-desktop/ios-example-app.git ios
cd ios
carthage update --use-xcframeworks
```

Librariesの liberlangがembedされないので、 Embed & Signに変更します
![スクリーンショット 2022-11-15 23.53.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/4263eab6-5c4a-2b2b-471a-183e38ab3d4b.png)

run_mixがビルドスクリプトなのでphoenixプロジェクトに合わせて変更します
```diff_shell
#!/bin/bash
...
BASE=`pwd`
export MIX_ENV=prod
export MIX_TARGET=ios

mix local.hex --force --if-missing
mix local.rebar --force --if-missing

- if [ ! -d "elixir-app" ]; then
-  git clone https://github.com/elixir-desktop/desktop-example-app.git elixir-app
- fi

- # using the right runtime versions
- if [ ! -f "elixir/.tool-versions" ]; then
-  cp .tool-versions elixir-app/
- fi

- cd elixir-app
+ cd ../phoenix

if [ ! -d "deps/desktop" ]; then
  mix deps.get
fi

# 追加したライブラリが無いのでコメントアウト
# if [ ! -d "assets/node_modules" ]; then
#   cd assets && npm i && cd ..
# fi

if [ -f "$BASE/todoapp/app.zip" ]; then
  rm "$BASE/todoapp/app.zip"
fi

mix assets.deploy && \
  mix release --overwrite && \
-  cd _build/ios_prod/rel/todo_app && \
+  cd _build/ios_prod/rel/spotties && \
  zip -9r "$BASE/todoapp/app.zip" lib/ releases/ --exclude "*.so"
```

xcodeの実行ボタンを押してビルドします
ビルドエラーがでて、原因がわからない場合は以下を実行してログをみて見ましょう
```
./run_mix 
```

![スクリーンショット 2022-11-16 0.09.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/849f3133-a497-cd33-f819-89fd5609e717.png)

# Android Emunlatorで動かす

```
git clone https://github.com/elixir-desktop/android-example-app.git android
```

AndroidStudioで cloneしたandroidを開きます、gradle syncがちょっとかかるので待ちましょう
ビルドスクリプトを書き換えます

```diff_shell
#!/bin/bash
set -e

BASE=`pwd`
APP_FILE="$BASE/src/main/assets/app.zip"
export MIX_ENV=prod
export MIX_TARGET=android

- if [ ! -d "elixir-app" ]; then
-  git clone https://github.com/elixir-desktop/desktop-example-app.git elixir-app
- fi

# using the right runtime versions
- if [ ! -f "elixir-app/.tool-versions" ]; then
-  cp .tool-versions elixir-app/
- fi

- cd elixir-app
+ cd ../../phoenix

if [ ! -d "deps/desktop" ]; then
  mix local.hex --force
  mix local.rebar
  mix deps.get
fi

# ライブラリを追加していないのでコメントアウト
# if [ ! -d "assets/node_modules" ]; then
#  cd assets && npm i && cd ..
# fi

if [ -f "$APP_FILE" ]; then
  rm "$APP_FILE"
fi

mix assets.deploy && \
  mix release --overwrite && \
-  cd "_build/${MIX_TARGET}_${MIX_ENV}/rel/todo_app" && \
+  cd "_build/${MIX_TARGET}_${MIX_ENV}/rel/spotties" && \
  zip -9r "$APP_FILE" lib/ releases/ --exclude "*.so"
```

ビルド時に以下のエラーが出たらJavaのバージョンが古いと怒られているので、
メニューバーの Android Studio > preferences > Build,Execution > Build Tools > Gradle を開き Gradle JDKを11以上にしてください
```
Android Gradle plugin requires Java 11 to run. You are currently using Java 1.8
```

それでもビルドに失敗した場合は run_mixがあるフォルダに移動してビルドを実行してログを見ましょう
```
cd android/app
./run_mix
```

![スクリーンショット 2022-11-16 0.21.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/42589c8c-5fe5-615c-580c-7f6e93b2f484.png)


# 最後に
無事　WxWidget, iOS, Androidそれぞれでアプリとして起動できました

開発を進める場合はデベロッパーコンソールがあるので、PhoenixをWebアプリとして起動したほうが楽かもしれなません
Phoenixもmix_targetによって起動できるようにする構築手順は以下なります

https://qiita.com/the_haigo/items/27bf18acd28971af31e9#desktop%E3%82%92%E7%B5%84%E3%81%BF%E8%BE%BC%E3%82%80

本記事は以上になります、ありがとうございました
次は実際にアプリを作っていこうと思います

# 参考ページ
https://qiita.com/the_haigo/items/27bf18acd28971af31e9
https://qiita.com/the_haigo/items/bc848d80ff8fdf6ffa79
https://github.com/phoenixframework/phoenix/tree/master/installer
https://github.com/elixir-desktop/desktop
https://github.com/elixir-desktop/ios-example-app
https://github.com/elixir-desktop/android-example-app