あらゆるネイティブアプリを作るライブラリ？！ LiveViewNativeの紹介

# はじめに
この記事は[「Elixir Advent Calendar 2022」](https://qiita.com/advent-calendar/2022/elixir)vol1 20日目の記事です．

ElixirConf 2022 USで発表されたLiveView Nativeの解説します

https://www.youtube.com/watch?v=dnDGh_Jmw-s

# LiveViewNativeとは

https://native.live

Phoenix LiveViewを共通プロトコルとしたマルチプラットフォーム

![スクリーンショット 2022-12-21 20.39.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/bc445ff3-ead6-f40a-4c4b-c3a3b3e5b766.png)

> LiveView Nativeは、Phoenixを使用してネイティブアプリケーションとWebアプリケーションをシームレスに構築できるようにするものです。
複数のチームを管理したり、複数のフレームワークを使いこなす必要はありません。
同じ開発者が両方を管理することで、納期を短縮し、イノベーションに集中するためのリソースを確保できます。

# LiveView Nativeの特徴
> LiveView Nativeを使用することで、単一のチームがWebおよびネイティブアプリケーションのフロントエンド開発を実行し、Phoenixのサーバーサイドレンダリングの利点を活用することが可能になりました。ElixirがWebにもたらす多くの利点を、今度はネイティブ環境で実感してください。

- 複雑なクライアントサイドのアプリケーションを、重いJavaScriptフレームワークで構築するよりも高速かつ軽量に構築できます。
- あらゆるプラットフォームでより良いユーザー体験を提供（「不気味の谷」現象はもう起きない）
- Webとネイティブにまたがる単一のコードベースを管理する単一チームによる生産性とリソースの最適化

# ElixirConf USざっくり

- Phoenix LiveViewをプロトコルとしたネイティブプラットフォーム実装
- バックエンドにPhoenixをそのまま使う
- フロント部分を各ネイティブ環境用のDSLで記述し、クライアント側で解析して画面を組み立てる
- ElixirConf USでLiveViewNativeで実装されたiOS製のチャットアプリをリリースして実際にイベントの交流ツールとして使用した

-> すでにプロダクションで使われている

# LiveViewNativeとElixirDesktopの違い

|LiveViewNative  |ElixirDesktop  |
|---|---|
|ネイティブUIで表示  |WebViewで表示  |
|外部Phoenixサーバー  |内部Phoenixサーバー  |
|ネイティブ機能と連携容易|WebViewを通してネイティブ機能を使用|
|個々のネイティブUIの知識が必要|Webアプリの知識で実装可能|

# LiveViewNative アーキテクチャざっくり
1. Swift側から LiveViewCoordinatorで接続先を指定
2. Phoenix側で index.ios.heexというSwiftUIのコンポーネントのDSLで書いたLiveViewページを表示
2. liveview-client-swifuiでそのデータを元に画面をビルドする


![スクリーンショット 2022-12-21 21.14.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/9e5be205-840e-ad87-d5cb-956430f4fd19.png)

# Swift側でやること

- PhoenixLiveViewNativeを読み込む
- LiveViewCoordinatorを作成
- ナビゲーション等の設定
- PhoenixサーバーのURLをセット
- ContentViewで LiveViewを表示

実際のコード

```swift
import SwiftUI
import PhoenixLiveViewNative

struct ContentView: View {
    @State var coordinator: LiveViewCoordinator<MyRegistry> = {
        var config = LiveViewConfiguration()
        config.navigationMode = .enabled
        return LiveViewCoordinator(
            URL(string: "http://localhost:4000/cats")!,
            config: config
        )
    }()
    var body: some View {
        LiveView(coordinator: coordinator)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
```

# Phoenix側でやること
- URLに対応した.ios.heexをつくる
- DSLでviewを記述
- ページ単位ではなくアプリ全体でstateを管理するGenServerの作成（なくても作れる）

```elixir:cat_live.ex
defmodule LvnTutorialWeb.CatLive do
  use LvnTutorialWeb, :live_view
  require EEx
  alias LvnTutorial.FavoritesStore

  EEx.function_from_file(
    :def,
    :render,
    "lib/lvn_tutorial_web/live/cat_live.ios.heex",
    [:assigns],
    engine: Phoenix.LiveView.HTMLEngine
  )

  def mount(%{"name" => name}, _sesion, socket) do
    {:ok,
     assign(socket,
       name: name,
       favorite: Enum.member?(FavoritesStore.get_favorites(), name),
       score: FavoritesStore.get_score(name)
     )}
  end
  ...
end
```

EEx.function_from_fileでの設定はライブラリを開発中なのでもう少し簡単になると思います

```elixir:cats_list_live.ios.heex
<list navigation-title="Cats!">
  <%= for {name, favorite} <- @cats_and_favorites do %>
    <navigationlink 
      id={name} 
      data-phx-link="redirect" 
      data-phx-link-state="push" 
      data-phx-href={Routes.live_path(@socket, LvnTutorialWeb.CatLive, name)}
    >      
        <hstack id={name}>
        <asyncimage src={"/images/phoenix.png"} frame='{"width": 100, "height": 100}' />
        <text><%= name %></text>
        <spacer />
        <button phx-click="toggle-favorite" phx-value-name={name}>
          <image system-name={if favorite, do: "star.fill", else: "star"} symbol-color={if favorite, do: "#f3c51a", else: "#000000"} />
        </button>
      </hstack>
    </navigationlink>
  <% end %>
</list>
```

# Swift側でできること
- coordinator.pushEventでPhoenix側のイベントを発火
-> センサデータや写真などをPhoenix側に送信するのが容易
- カスタムコンポーネントを作成してPhoenix側で使用
- iOSだけでなくmacOS, watchOS, tvOSのアプリも作れる

# チュートリアルサイト

実際に試してみたい方はこちら！

https://liveviewnative.github.io/liveview-client-swiftui/tutorials/yourfirstapp/

![スクリーンショット 2022-12-21 21.28.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/2cfedaea-6e5e-453c-36cb-4312a2ffb657.png)


# ロードマップ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/b1eefbcd-b45a-cf4a-c609-28132a0c61f8.png)

- SwiftUIの全コンポーネントをカバー
- Jetpack対応
- WinUI3対応

# 展望

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/737c8a48-b8dd-1361-9a24-2d9d739da381.png)

iOS,Android, Windowsだけでなく、C# Channels clientを通してUnityを操作できるようにするという恐ろしい計画があるそうです

# 最後に
いかがでしたでしょうか
すでにiOSアプリのフロントをLiveViewのDSLで記述し、バックエンド及びステート管理をLiveViewで実装できる激アツプロダクトだと思います

今年はまだα版な感じでまだこなれてないのと、あまり時間がないので触れてないですが
来年はこれを使ってアプリを実際に作りたいと思います

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

# 参考サイト
https://speakerdeck.com/thehaigo/elixirconfxi-garideliveviewnativeyatutemita
https://liveviewnative.github.io/liveview-client-swiftui/tutorials/yourfirstapp/
https://github.com/liveviewnative/liveview-client-swiftui
https://www.youtube.com/watch?v=dnDGh_Jmw-s