LiveViewNative で簡単 iOS ネイティブアプリ -🐈チュートリアルを動かす🐈‍⬛- 

## はじめに

LiveViewNative は Elixir で iOS や Android のモバイルアプリが作れてしまう素晴らしいフレームワークです

https://github.com/liveview-native/live_view_native

@the_haigo さんが以前の記事で紹介してくれています

https://qiita.com/the_haigo/items/1936319bad327d14ad7b

同じようなものとして Elixir Desktop があります

https://github.com/elixir-desktop/desktop

LiveViewNative と Elixir Desktop の違いについては上記記事を参照してください

Elixir Desktop も @the_haigo さんがアプリの開発手順を紹介してくれています

https://qiita.com/the_haigo/items/17a89b5038a7f337b102

## LiveViewNative とは

LiveViewNative では Elixir だけでモバイルアプリのフロントエンドもバックエンドも実装します

複数の言語を習得する必要はなく、 API 連携を考える必要もありません

しかも、 LiveViewNative はネイティブコードで動くため、 Swift で実装したのと同じような外観、動作のアプリが作れます

ただし、モバイルアプリ単体では動かず、必ずサーバーがバックエンドに必要になること、オフラインでは動かないことに留意してください

## チュートリアルアプリ

GitHub 上に LiveViewNative の iOS チュートリアルがあります

https://github.com/liveview-native/ios-tutorial

今回はこのアプリを動かします

### 実行環境

- iOS Ventura 13.4.2
- XCode 14.3.1
- Elixir 1.15.2
- Erlang 25.3

:::note warn
Elixir 1.14 で mix setup すると以下のような警告が表示されます

```
...
warning: the dependency :live_view_native_platform requires Elixir "~> 1.15" but you are running on v1.14.4
...
warning: the dependency :live_view_native_swift_ui requires Elixir "~> 1.15" but you are running on v1.14.4
...
```
:::

### コードのクローン

任意のディレクトリーにチュートリアル用のコードをクローンします

```bash
git clone https://github.com/liveview-native/ios-tutorial.git \
  && cd ios-tutorial
```

ディレクトリー構造は以下のようになっています

```
├─ LVNTutorialApp
|  └─...
├─ lvn_tutorial_backend
│  └─ ...
├─ .DS_Store
└─ README。md
```

"LVNTutorialApp" 配下が iOS アプリ本体のコードです

"lvn_tutorial_backend" 配下がバックエンドで動かすサーバー側のコードです

### Elixir Erlang の準備

asdf を使っている場合、 "ios-tutorial" 配下で特定バージョンを使うように .tool-versions ファイルを作成します

```text
elixir 1.15.2-otp-25
erlang 25.3
```

そして、 `asdf install` を実行します

### バックエンド側

まず、バックエンド側を起動します

```bash
cd lvn_tutorial_backend
```

#### セットアップ

以下のコマンドを実行します

```elixir
mix setup
```

これにより、依存モジュールが全てインストールされます

#### コードの修正

一部、チュートリアルの内容が古いため、修正します

lvn_tutorial_backend/lib/lvn_tutorial_web/live/cat_live.ex
```diff_elixir
...
  def render(%{platform_id: :swiftui} = assigns) do
-   ~Z"""
+   ~LVN"""
    <VStack modifiers={@native |> navigation_title(title: @name) |> nav_favorite(is_favorite: @favorite)}>
...
```

lvn_tutorial_backend/lib/lvn_tutorial_web/live/cats_list_live.ex
```diff_elixir
...
  def render(%{platform_id: :swiftui} = assigns) do
-   ~Z"""
+   ~LVN"""
    <List modifiers={navigation_title(@native, title: "Cats!")}>
...
```

廃止された Z シギルの代わりに、 LVN というシギルを使います

#### 画像の準備

今回動かすチュートリアルアプリは、猫の一覧を表示し、ハートを送るサービスです

そのため、各猫毎に写真が必要になります

lvn_tutorial_backend/lib/lvn_tutorial_web/live/cats_list_live.ex
```elixir
...
  @cats [
    "Clenil",
    "Flippers",
    "Jorts",
    "Kipper",
    "Lemmy",
    "Lissy",
    "Mikkel",
    "Minka",
    "Misty",
    "Nelly",
    "Ninj",
    "Pollito",
    "Siegfried",
    "Truman",
    "Washy"
  ]
...
```

デフォルトでは画像ファイルは用意されていないため、全猫分、以下のパスに自分で用意します
（もしくは @cats の方を短くします）

- lvn_tutorial_backend/priv/static/images/cats/Clenil.jpg
- lvn_tutorial_backend/priv/static/images/cats/Flippers.jpg
...
- lvn_tutorial_backend/priv/static/images/cats/Truman.jpg
- lvn_tutorial_backend/priv/static/images/cats/Washy.jpg

#### Phoenix の起動

以下のコマンドを実行し、バックエンドのサーバーをローカルで起動します

```bash
mix phx.server
```

#### ngrok による Web サーバーの公開

ローカルで起動した場合、 http://localhost:4000 でアクセス可能ですが、モバイルアプリは別端末なので localhost ではアクセスできません

[ngrok](https://ngrok.com/) を使ってローカルに外部から HTTPS 通信でアクセスできるようにします

ngrok のインストール方法等についてはこちらを参照してください

https://www.mgo-tec.com/blog-entry-ngrok-install.html

:::note info
ngrok はあくまでも一時的な動作検証用の方法です

実際に運用する場合、 [Fly.io](https://fly.io/) などにバックエンドをデプロイしましょう
:::

ngrok の準備ができたら、以下のコマンドで 4000 番ポートを外部に公開します

```bash
ngrok http 4000
```

コマンドを実行すると、ターミナルに以下のように表示されます

```
ngrok by @inconshreveable                                                                             (Ctrl+C to quit)

Session Status                online
Account                       xxx
Update                        update available (version 2.3.41, Ctrl-U to update)
Version                       2.3.35
Region                        United States (us)
Web Interface                 http://127.0.0.1:4040
Forwarding                    http://xxx-111-111-111-111.ngrok-free.app -> http://localhost:4000
Forwarding                    https://xxx-111-111-111-111.ngrok-free.app -> http://localhost:4000

Connections                   ttl     opn     rt1     rt5     p50     p90
                              0       0       0.00    0.00    0.00    0.00
```

ランダムに生成される `https://xxx-111-111-111-111.ngrok-free.app` の部分（先頭 `https` の方）を後で使います

### フロントエンド側

#### 参照先 URL 変更

先程公開した URL を参照するため、フロントエンド側の以下のコードを編集します

LVNTutorialApp/LVNTutorialApp/ContentView.swift
```diff_swift
import SwiftUI
import LiveViewNative

@MainActor
struct ContentView: View {
    @StateObject private var session: LiveSessionCoordinator<MyRegistry> = {
        var config = LiveSessionConfiguration()
        config.navigationMode = .enabled
-       return LiveSessionCoordinator(URL(string: "http://localhost:4000/cats")!, config: config)
+       return LiveSessionCoordinator(URL(string: "https://xxx-111-111-111-111.ngrok-free.app/cats")!, config: config)
    }()

    var body: some View {
        LiveView(session: session)
    }
}
```

#### プロジェクトの準備

LVNTutorialApp/LVNTutorialApp.xcodeproj を XCode で開きます

左メニューディレクトリー構造の一番上 LVNTutorialApp をクリックし、アプリの設定を開きます

Signing & Capabilities タブを開き、 Team を自分のチームに指定します

![スクリーンショット 2023-07-20 0.10.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/7fa49d0f-d67a-6867-45c1-33fb62d0be70.png)

#### アプリのビルド

右赤枠で任意の実行環境を選択し、左赤枠の実行ボタンをクリックします

![スクリーンショット 2023-07-20 0.24.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/7e76cfd8-6c38-b265-5adc-6337b006b301.png)

:::note info
初回はパッケージを信頼するか、というような警告が出ますが、信頼して進めます
:::

しばらくビルドして、うまくいけばアプリが起動します

![lvn.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/18ad2d2f-bada-4b5b-5973-abb04d14df32.gif)

無事猫アプリが起動しました

すごくお茶目なチュートリアルですね

## まとめ

LiveViewNative はプレリリース状態なので、チュートリアルがまだ最新版に追いついていなかったりなど未完成な部分もありますが、 Elixir でネイティブアプリが作れるのは非常に魅力的ですね

とりあえず今回はチュートリアルを動かすまでです

画面の変更方法などはおいおいやっていきます