Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Rails: Hotwire Nativeで作るネイティブモバイルアプリ: iOS編(2)ネイティブ画面(翻訳)

概要

元サイトの許諾を得て翻訳・公開いたします。

日本語タイトルは内容に即したものにしました。

従来Turbo NativeとStradaと呼ばれていたものは、現在はHotwire Nativeに統合されました。

参考: Hotwire Native: Hotwire Native is a web-first framework for building native mobile apps.

hotwired/hotwire-native-ios - GitHub

Rails: Hotwire Nativeで作るネイティブモバイルアプリ: iOS編(2)ネイティブ画面(翻訳)

前回の記事では、/new/editで終わるルーティングにアクセスするたびにモーダルを表示するようにしました。

しかしモーダルだけではありません。さらに踏み込んで、iOSアプリのネイティブ画面に移動することも可能なのです。

🔗 ネイティブ画面を追加する

🔗 背景(読み飛ばしても構いません)

顧客とやりとりするときは、本当に必要でない限り、ネイティブ画面は構築しないようにすることをおすすめします。
Hotwire Nativeのメリットは、サーバーサイドでHTMLをレンダリングすることで、作成しなければならないネイティブ画面の数を減らせることです。

とはいうものの、ネイティブ画面には以下のようないくつかの強みがあることも確かです。

  • 複雑な入力でGPUアクセラレーションを利かせられる
  • アプリがオフラインのときにも表示できる
  • その後アプリがオンラインに復帰したら、ローカルDBに接続してすぐ同期できる

その分トレードオフもあります。ネイティブ画面を構築するにはiOSやさまざまなエコシステムに関する知識も必要です。ネイティブ画面を追加すると、結構な長旅になる可能性があることを知っておくことが大事です。

🔗 作成手順

ネイティブ画面の追加方法は、以下の3つのステップに分かれています。

  1. パス構成(Path Configuration)ファイルを調整する
  2. UIViewControllerをPathConfigurationIdentifiableeプロトコルで作成する
  3. handleメソッドをナビゲーターのデリゲートクラスで更新または追加する

Railsアプリ側で、todos/index.html.erbファイルに以下のリンクを追加します。

<%= link_to "Native", "/native" %>

Xcodeに戻り、パス構成で以下の新しいルール(...の下)を追加します。これによって、/nativeにアクセスしたときにネイティブ画面に移動します。

{

  "settings": {},

  "rules": [
  ...
    {
      "patterns": [
        "/native"
      ],
      "properties": {
        "view_controller": "hello"
      }
    }
  ]
}

補足: 上の設定にview_controllerというプロパティと"hello"というキーがあることにご注目ください。このおかげでどれほど便利になるかについては今後見ていきます。これによって、今後ビューコントローラをリファクタリングするときに複数のファイルを更新せずに済みますし、ナビゲーション方法がHotwire Native Android版に近くなります。

それではViewControllerで使うSwiftUIビューを作成しましょう。

Xcodeでコマンド+Nキーを押してダイアログでSwiftUIを選択します。HelloViewという名前を付けてCreateをクリックすると、以下の内容で作成されます。

import SwiftUI

struct HelloView: View {
    var body: some View {
        Text("Hello, World!")
    }
}

#Preview {
    HelloView()
}

続いて、SwiftUIビューをレンダリングするビューコントローラを作成しましょう。

Xcodeでコマンド+Nキーを押してダイアログでSwiftUIを選択します。HelloViewControllerという名前を付けてCreateをクリックし、以下の内容に置き換えます。

import UIKit
import HotwireNative
import SwiftUI

class HelloViewController: UIViewController, PathConfigurationIdentifiable {
    static var pathConfigurationIdentifier: String { "hello" }

    override func viewDidLoad() {
        super.viewDidLoad()

        let childView = UIHostingController(rootView: HelloView())

        addChild(childView)
        view.addSubview(childView.view)
        childView.view.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            childView.view.topAnchor.constraint(equalTo: view.topAnchor),
            childView.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            childView.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            childView.view.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])

        childView.didMove(toParent: self)

    }
}

NSLayoutConstraintは、オートレイアウト(Auto Layout)を管理するクラスです。子ビューが実質的に親ビューとなるには、このクラスを上のように扱う必要があります。

原注: 必ずしもSwiftUIを使う必要はありません。Storyboardとxibファイルを使っても、UIkitで直接ビルドしても構いません。私はたまたまSwiftUIが好きなので、SwiftUIにしたのはあくまで私個人の好みです。

最後のステップとして、SceneDelegateクラス内でナビゲーションを設定します。ここでは、handleと呼ばれる拡張をSceneDelegateクラスに追加します。

SceneDelegateファイルに以下を追加します。

extension SceneDelegate: NavigatorDelegate {
    func handle(proposal: VisitProposal) -> ProposalResult {
        switch proposal.viewController {
        case HelloViewController.pathConfigurationIdentifier:
            let helloViewController = HelloViewController()
            return .acceptCustom(helloViewController)
        default:
            return .accept
        }
    }
}

続いて、ナビゲータ用のデリゲートを設定します。

 class SceneDelegate: UIResponder, UIWindowSceneDelegate {
   var window: UIWindow?
..
-  private let navigator = Navigator()
+  private lazy var navigator = Navigator(delegate: self)
...

これで、コマンド+Rでアプリをリビルドしてから、エミュレータで/nativeリンクを開くと、以下のようにネイティブ画面に"Hello, World!"が表示されます。

ネイティブ画面はクールですが、それよりずっとクールなものといえば?

「氷!」

温度がもっと下がれば氷になるのはその通りですが、そっちではなく、Hotwire Nativeならではのクールな機能の話です。それがブリッジコンポーネント(Bridge Components)です。

次回の記事では、ネイティブ機能をいくつか追加して、サーバーのJavaScriptによってトリガーされるようにします。これこそが、優秀なハイブリッドアプリを構築するための秘訣です。

関連記事

Rails: Hotwire Nativeでネイティブモバイルアプリを作ろう: iOS編パート1(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。