ElixirDesktop クラサバ構成 DB共有でAPIレス開発

# はじめに
この記事は Elixirアドベントカレンダーのシリーズ４の17日目の記事です

このシリーズでは内部DBをSQlite3にしたスタンドアローン、Phoenix APIサーバーとクライアントElixirDesktopアプリと解説してきました

今回はクラサバ構成 DB共有でAPIレス開発に付いて解説します

# アプリケーション構成

今回はWebアプリまた管理ダッシュボードとしてPhoenixサーバーを構築し、そこで使用しているDBにアクセスして読み書きを行う構成です


```
 [Phoenix Dashboard App] -- [Postgres] -- [ElixirDesktop App]
```

# メリデメ
メリット
- phx.gen.liveやauthで作成したコードをそのまま使える
- APIを作る必要がない
- umbrella構成の知見があると単一レポジトリでWeb,スマホの開発ができる

デメリット
- ElixirDesktopでCのライブラリ等nifを使用するライブラリが動かない
phx.gen.authで暗号化を行う BcryptElixirがこれに該当し、AI/MLのライブラリも多く該当します。
- ストアに通るのかよくわからない
スタンドアローンとAPIクラサバ構成のアプリはストアに多くあるので問題ないが、DBに直接通信するタイプのアプリがあまりきかないので大丈夫だとは思うが出してないのでわからない

デメリットに対してAPIを作る必要がないのメリットがでかすぎるので、無事審査に通るといいなと思います

# プロジェクト作成
GooglePlaces APIとGoogleMapを使用した、スポット登録とGPSロガーを機能として持つSpotterというアプリを作っていきます

DBはpostgresqlなのでそのままで、webサーバーをcowboyからbanditに変更してみましょう

```
mix phx.new spotter --adapter bandit
```

プロジェクトの作成が完了したら、ログに従ってプロジェクトフォルダに移動してDBを作成しましょう

```
cd spotter
mix ecto.create
```

# ライブラリの追加
SQLite3は使わないのでdektop関連とGoogleのAPIを使うのでReqを追加します

```mix.exs
  defp deps do
    [
      ...
      {:desktop, "~> 1.5"},
      {:wx, "~> 1.1", hex: :bridge, targets: [:android, :ios]},
      {:req, "~> 0.3"}
    ]
  end
```

追加したら以下のコマンドを実行します

```
mix deps.get
```

# config修正
configファイルをElixirDesktopに合わせて変更します
Endpointの設定を以下のように変更します

```diff_elixir:config/config.exs
# Configures the endpoint
config :spotter, SpotterWeb.Endpoint,
-  url: [host: "localhost"],
+  http: [ip: {127, 0, 0, 1}, port: 10_000 + :rand.uniform(45_000)],
  adapter: Bandit.PhoenixAdapter,
  render_errors: [
    formats: [html: SpotterWeb.ErrorHTML, json: SpotterWeb.ErrorJSON],
    layout: false
  ],
  pubsub_server: Spotter.PubSub,
- live_view: [signing_salt: "2TfGmFjF"]
+ live_view: [signing_salt: "2TfGmFjF"],
+ secret_key_base: :crypto.strong_rand_bytes(32),
+ server: true

```

portの設定はiOSのバックグラウンドモードからの復帰時に必要です
server: trueとすることで、スマホ内で起動したPhoenixアプリケーションをWebViewから閲覧ができます

config/runtime.exsがあるとエラーになるのでruntime_disable.exsにリネームします

# Ednpoint
Banditを使う場合は use のモジュールはそのままで、
セッション管理をcookieから EralangビルドインインメモリDBのetsに変更します

```diff_elixir:lib/spotter_web/endpoint.ex
defmodule SpotterWeb.Endpoint do
  use Phoenix.Endpoint, otp_app: :spotter

  @session_options [
-   store: :cookie,
+   store: :ets,
    key: "_spotter_key",
+   table: :session    
-   signing_salt: "CQS/fw0h",
-   same_site: "Lax"
  ]
  ...
end
```

# アプリケーション起動時の設定

アプリケーション起動時に実行される、プロセス監視を行うsuperviserの設定を行います
SQLiteは使わないので、DB initiaizeは行わないのと port番号の取得方法が変わります

```lib/spotter.ex
defmodule Spotter do
  use Application

  @moduledoc """
  dekstop logic
  """

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

  @app Mix.Project.config()[:app]
  def start(:normal, []) do
    # configフォルダを掘る
    File.mkdir_p!(config_dir())

    # session用のETSを起動
    :session = :ets.new(:session, [:named_table, :public, read_concurrency: true])

    children = [
      Spotter.Repo,
      {Phoenix.PubSub, name: Spotter.PubSub},
      SpotterWeb.Endpoint
    ]

    opts = [strategy: :one_for_one, name: Spotter.Supervisor]
    # メインのsuperviser起動
    {:ok, sup} = Supervisor.start_link(children, opts)

    # phoenixサーバーが起動中のポート番号を取得
    {:ok, {_ip, port}} = Bandit.PhoenixAdapter.server_info(SpotterWeb.Endpoint, :http)

    # メインのsuperviserの配下にElixirDesktopのsuperviserを追加
    {:ok, _} =
      Supervisor.start_child(sup, {
        Desktop.Window,
        [
          app: @app,
          id: SpotterWindow,
          title: "Spotter",
          size: {400, 800},
          url: "http://localhost:#{port}"
        ]
      })
  end

  def config_change(changed, _new, removed) do
    SpotterWeb.Endpoint.config_change(changed, removed)
    :ok
  end
end

```
アプリケーション設定ファイルを差し替えます
```diff_elixir:mix.exs
  def application do
    [
-      mod: {Spotter.Application, []},
+      mod: {Spotter, []},
      extra_applications: [:logger, :runtime_tools]
    ]
  end
```

# iOS, Android開発でのDB設定
iOSとAndroidで開発するときはprod環境でビルドされるので
Dev環境の設定は無効化されており、DBとコネクションを貼るときにエラーになってアプリが落ちてしまいます
なのでProd環境の設定ファイルに以下を追加します

```config/prod.exs
config :spotter, Spotter.Repo,
  username: "postgres",
  password: "postgres",
  database: "spotter_dev",
  stacktrace: true,
  show_sensitive_data_on_connection_error: true,
  pool_size: 10

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

if Mix.target() == :android do
  config :spotter, Spotter.Repo, hostname: "10.0.2.2"
end
```

問題なく起動できました

![スクリーンショット 2023-12-18 23.40.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/d938960f-b891-0bdf-1c5b-bb5049a6a44f.png)


# 最後に
今回は DBをPostgresql、WebサーバーをBanditにしてアプリを作成してみました
次は認証機能の実装とセッション保持について解説します

本記事は以上になりますありがとうごうざいました