LoginSignup
10
0

ElixirDesktop ネイティブ機能を使用してブラウザを開く(iOS)

Last updated at Posted at 2023-12-24

はじめに

この記事は Elixirアドベントカレンダーのシリーズ4の23日目の記事です

今回はWebViewからネイティブ側の関数を実行する方法を解説します
例としてiOSのブラウザを起動して指定したURLを開きます

処理の流れ

  1. LiveView: phx-hook をセット
  2. LiveView: push_eventで hookのthis.handleEventの関数を実行
  3. JS Hook: window.webkit.messageHandlers.xx.postMessage(data)を実行
    • xxはiOS側で設定したhandler名
  4. iOS: userContentControllerでhandler名で処理を分ける
  5. iOS: ネイティブ機能を実行(URLをSafariで開く)

iOS側の設定

init() の super.initのあとに
configuration.userContentController.add(self, name: イベント名)
でハンドラーを登録します

今回は openSafari としています

native/ios/todoapp/WebView.swift:L40
final class WebView: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
    var webview: WKWebView
    var finish: (() -> ())?
    
    override init() {
        let preferences = WKPreferences()        
        let page = WKWebpagePreferences()
        page.allowsContentJavaScript = true        
        
        let configuration = WKWebViewConfiguration()
        configuration.limitsNavigationsToAppBoundDomains = true
        configuration.preferences = preferences
        configuration.defaultWebpagePreferences = page
        
        webview = WKWebView(frame: CGRect.zero, configuration: configuration)
        webview.allowsBackForwardNavigationGestures = true
        webview.scrollView.isScrollEnabled = true
        
        super.init()

+       configuration.userContentController.add(self, name: "openSafari")

        ...
    }    
}

handlerをセットしたら、LiveViewから実行されたときのハンドリングを
func userContentControllerに追加します。
すでにswitch文にerrorがあるので、先程追加した openSafariのケースを追加します

なかの処理としては渡されたURLをブラウザで開くという内容です

native/ios/todoapp/WebView.swift:L89
final class WebView: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
    var webview: WKWebView
    var finish: (() -> ())?

    ...
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        switch message.name {
+       case "openSafari":
+           print(message.body)
+           let url = URL(string:message.body as! String)
+           if( UIApplication.shared.canOpenURL(url!) ) {
+             UIApplication.shared.open(url!)
+           }
        case "error":
            // You should actually handle the error :)
            let error = (message.body as? [String: Any])?["message"] as? String ?? "unknown"
            assertionFailure("JavaScript error: \(error)")
        default:
            assertionFailure("Received invalid message: \(message.name)")
        }
    }
}

LiveView側の設定

Hooksファイルを作成します
mount時に handleEvent open_safariで待ち受けます
ネイティブ側を呼ぶときは先程設定したhandlerNameopenSafariに対してpostMessageを実行します

assets/js/hooks.js
Hooks.NativeBridge = {
  mounted() {
    this.handleEvent("open_safari", ({ url }) => {
      window.webkit.messageHandlers.openSafari.postMessage(url);
    });
  },
};

export default Hooks;

Hooksの設定が終わったらLiveView側で呼ぶように実装します

idを付けたタグに phx-hookでHooksのモジュールを読み込みます
phx-clickでLiveViewのイベントを発火させます

lib/spotter_web/live/route_live/index.html.heex
<.gheader title="Routes">
  <:actions>
    <.link patch={~p"/routes/new"}>
        <.icon name="hero-plus-solid" class="h-8 w-8 mr-4" />
    </.link>
  </:actions>
</.gheader>

+ <.button id="open_safari" phx-hook="OpenSafari" phx-click="open">
+  open MapLibre
+ </.button>

handle_eventで待ち受けし、発火したら push_eventでHooksの関数を実行します

lib/spotter_web/live/route_live/index.ex
  def handle_event("open", _params, socket) do
    {:noreply, push_event(socket, "open_safari", %{url: "https://maplibre.org/"})}
  end

動作確認

ブラウザが起動して指定したURLが開かれるのを確認できました

cbe335300f58e9f9ab0186bd49d7c1f6.gif

最後に

本記事ではiOSのネイティブ機能をLiveView側から実行する方法を解説しました
実装の解説は本記事で終了になります

最後はElixirDesktopのアドカレまとめになります

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

10
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
0