ナカザンドットネット

それって私の感想ですよね

X-PlatかPWAっぽい気がする(ミルクボーイネタ)

思いついたまま勢いで書いたミルクボーイネタだけど、生煮えながらこれ以上面白くすることもできなさそうなので、供養のために置いておきます。


「うちのお客さんがね、Webサービス作りたいゆーんやけど」

「そうなんや」

「ユーザーへの届け方を忘れたらしいねん」

「ユーザーへの届け方を忘れるってどうなってんねん」

「いろいろ聞くんやけどな、全然わからへんねん」

「ほんだら俺がね、ユーザーへの届け方一緒に考えてあげるから、どんな要件言うてたとか教えてみてよ」

「AndroidとiOS両方にサービス提供したい言うてた」

「モバイルアプリやないかい? その特徴はもう完全にモバイルアプリやがな。すぐわかったよこんなもん」

「俺もモバイルアプリやとおもてんけどな、お客さんが言うには、UIを修正したらすぐにユーザーの手元に届いてほしいっていうんや」

「ほなモバイルアプリと違うか! モバイルアプリは審査とかあって、修正してもなかなかユーザーの手元に届かんからな。モバイルアプリってそういうもんやから。ほなモバイルアプリとちゃうがなそれ。もうちょっと詳しく教えてくれる?」

「プッシュ通知でエンゲージメントを高めたいらしい」

「モバイルアプリやないかい! ピロン♪って通知が鬱陶しいくらいに飛んでくるのはモバイルアプリやろ!」

「わからへんねん、でも」

「何が分かれへんねん」

「俺もモバイルアプリやと思てんけどな、お客さんがいうにはいっぺんソースコード書いたらAndroidでもiOSでも動くって言うてた」

「ほなモバイルアプリちゃうやないかい! AndroidアプリはAndroid SDKでKotlin、iOSアプリはiOS SDKでSwiftや! 共通化なんかそうそうできへん! モバイルアプリちゃうがな。もうちょっとなんか言ってなかった?」

「インターネットにつながってないときでも動くらしい」

「モバイルアプリやがな。ニュースアプリやSNSでもない限り、いっぺんインストールが終われば、圏外のときでもちゃんと動く。それがモバイルアプリってもんや。長旅のトンネルの中でも安心! モバイルアプリに決まり!」

「わからへん」

「わからへんことない! お客さんがユーザーにサービスを届ける方法はモバイルアプリ!」

「お客さんが言うにはモバイルアプリではないって言うてた」

「ほなモバイルアプリちゃうやないか! お客さんがモバイルアプリではないと言えばモバイルアプリちゃうがな!」

「そうやねん」

「ほんまにわかれへんがな、それどうなってんねん」

「うちの部長がいうには、Flashちゃうかって」

「いや絶対ちゃうやろ!」

「もうええわ。どうもありがとうございました。」

React Nativeの「(not) for you」を伝え続けた2019年を振り返る

2018年の夏に「React Nativeはメリデメ両方デカすぎて、気軽に採用すると事業や組織とのミスマッチを起こしやすいので、マッチしてるかどうか考えてから採用しましょうね」という話をしました。

blog.nkzn.info

このときは雑多に問題提起してしまったので、具体的なモデルケースを想像しづらいものになってしまっていました。

そこに課題意識を持った私は「2019年はRNにマッチしそうな事業(プロダクト)や組織(チーム)の姿を伝え続ける年にしよう」と位置付け、各所でその方針に基づいた情報発信を行いました。

人材調達の難しさに目をつけた第一弾

その第一弾として、ちょっとフライングして2018年末に公開されたのが次の記事です。

codezine.jp

  • 「どうせビジネスサイドは3プラットフォーム出したいっていうじゃん」
  • 「でも本当に3プラットフォームそれぞれの作法を理解してる人をそれぞれ集められるの?」
  • 「UI作法(ライフサイクルとか)だけでもWebに寄せたほうが、採用するにせよ育成するにせよ楽なんじゃない?」

という切り口で導入を書かせてもらいました。これを言語化できたおかげで、良いスタートダッシュが切れたんじゃないかと思っています。

さらにターゲットを絞った第二弾

第一弾の時点では「ふーん、そういう分野もあるかもね(私には関係ないけど)」くらいの認識になりそうだったので、もう少し明確にターゲットを絞って刺しに行ったのが、第二弾のMOBILE CREW NIIGATAでの発表です。

speakerdeck.com

  • 「B2B(2B)だと操作マニュアルとかユーザーサポートが必須だけど、もし楽にUIを揃えられるならサポート部門の負担がちょっと減るでしょ」
  • 「できる人を外から連れてくるような人材流動性はなくて、たまたまいた器用な人を育てたほうが現実的でしょ」
  • 「どうせなら潰しの効くスキルを覚えさせたいでしょ」

という切り口を追加して、「そう! お前ら! お前らに必要なソリューションなんだよ!」感を出してみました。

集大成の第三弾

第一弾で打ち立てた方向性を元に、第二弾で深く言語化することができましたが、技術的な詳細を見ても魅力的であることの紹介が疎かになっていました。

というわけで、技術的な特性の話題も加味した集大成として書いたのが、第三弾でエンジニアHubに寄稿した記事です。公開は2020年になりましたが、2019年の11月ごろに書いてました。

employment.en-japan.com

  • 技術的なイメージが付けやすいように具体的なコードや内部実装の話も重視した
  • 巨大なエコシステムの支援を得られることをイメージしやすくする
  • ビジネス要件としてクロスプラットフォームが求められている現場があることを改めて強調する

この記事で気をつけたのは上記のようなところでした。悲壮感の漂っていた第二弾を裏返して、ある程度ポジティブな味付けにしたつもりです。

三部作の後書き

やっている最中は「結局ずっと同じことの話をしているけれど、読者の方は飽きないだろうか」と思っていました。しかし、こうして振り返ってみると、話題の切り口が違うのでターゲット読者も違っているし、媒体が違うので届くチャンネルも違うし、ということで、かなり広く話題提供ができたんじゃないかと思います。

全部読んでくれている人はエンジニアHubのあたりで「またこの話か」と飽き気味になっていたかもしれません。これは素直にすいませんでした。

この1年で生み出した3部作は、もちろんReact Nativeの大成を願ってのものです。しかしながら、読んだ人にはわかるとおり、「多くの人にReact Nativeを使ってほしい」というものではありません。「こんな状況の事業・組織には、React Nativeは役に立つ可能性がある(for you)」をハッキリと伝えることで、「そういう状況にない事業・組織では役に立たないかもしれない(not for you)」がある程度浮き彫りになるような作りにしたつもりです。

Android SDK + KotlinやiOS SDK + Swiftでアプリをそれぞれ作ったほうが、顧客に対して出せるバリューが大きくなるプロダクトというのは当然ありますし、それを実現するためのチームが無理せず構築できるならば、React Nativeのようなクロスプラットフォーム技術はnot for youであるはずです。無理にクロスプラットフォーム技術などという巨大なフレームワークを採用して苦しみを抱える必要はないでしょう。

ただ、世の中でそういったチームを構築できる企業というのは、そんなに多いのだろうか、という疑問を、第一弾を書きながら思ってしまったのです。

「採用は人事や経営の仕事だからITエンジニアの自分が扱うべきスコープではない」と言ってしまうのは簡単ですが、採用が上手くいっているわけでもないのにユニコーン企業の技術選択やチームビルディングだけをカーゴカルト的に真似しようとしたところで、苦しむのは自分たちなのです。

様々な事情があるとは思いますが、「(まだ)お金が潤沢にない」「(まだ)十分な数のメンバーがいない」という状況において、React Nativeはfor youなツールになりうると私は思っています。また、そういった状況になくても、純粋にReact Nativeの技術特性が事業にとって好ましいと判断されれば、時雨堂さんDiscord iOSNature Remoのように、腕力のある器用な人々が扱う前提で採用されることもあります。

自分が関わっている事業と組織の課題と向き合ってみて、そのロードマップの途上にReact Nativeが役立ちそうな場所があれば、採用してみることを検討してみてください。そのときに私の書いた記事たちがお役に立てたなら、検討の結果がfor meでもnot for meでも、それはとても喜ばしいことなのです。

余談:他のX-Platを扱う方々へお願い

私はReact Nativeが手に馴染んだことによって見えた景色(観点)があったので、それを言語化する目的で記事を書いています。React Nativeが他のX-Platより優れていることを示すために書いた記事は(おそらく)ありません。(極めてfor meではあるものの、優れているとは思っていません)

私はFlutterやXamarinやIonic(Capacitor)をほとんど使ったことがありません。使ったことがないもののことを偉そうに解説することはできないので、私はこれらについて、今回の三部作のような語り口で解説することができません。

しかしながら、私の書いた三部作は、多くの部分でX-Platツールに共通の観点を含んでいると思っています。

私は技術選択が大好きなので、Flutterは、Xamarinは、Ionic(Capacitor)は、その特性によってどんな事業(プロダクト)と組織(チーム)にマッチするのかを誰かが記事にしてくれたら、喜んで読みます。結果的に僕の記事と似てしまっても全然構わない(むしろdiffが際立つ)ので、読みたいのです。

というわけで、誰か記事を書いてください。私からのお願いでした。

Uber Eatsの障害についての事実をReact Nativeの観点から確認する

本日、Uber Eatsで大規模障害がありました。React Native絡みのようなので、今わかっている範囲の事実だけメモしておこうと思います。

公式アナウンス

ユーザーの声

Twitterを眺めている限りでは、店舗側に配布されているアプリ(レストランアプリ)が利用できなくなり、受注の確認ができなくなっていたようです。

React Nativeっぽい

Uber EatsチームがReact Nativeを採用していることは、次の公式エンジニアブログでも言及されています。

eng.uber.com

本件で報告が上がっていた赤い画面は、React Nativeのデバッグモードで console.error() や最上位まで到達してしまった例外の内容を表示するための、RedBoxと呼ばれる開発補助のための機能です。レストランアプリもReact Native製と見て問題ないでしょう。

2つの事実を確認する

それでは、React Nativeの観点で、今回報告されている画面から読み取れる内容について解説します。主に次の2件について言及します。

  • RedBoxが表示されている
  • Textコンポーネントについてのエラーが出ている

RedBoxが表示されている

まずは、赤い画面(RedBox)が出ている件についてです。繰り返しになりますが、

本件で報告が上がっていた赤い画面は、React Nativeのデバッグモードで console.error() や最上位まで到達してしまった例外の内容を表示するための、RedBoxと呼ばれる開発補助のための機能

です。

React Nativeでアプリ開発をしたことがある方なら日常的に目にしているものですが、そうでない方はほぼ見たことないと思います。React Nativeは多くの有名アプリで使われているにもかかわらず、です。

というのも、RedBox(とconsole.warnを司るYellowBox)はリリース用にビルドした際に無効化され、ストアに公開された時点で表示されなくなっているために、ユーザーとしてアプリを触る場合には目にすることがないからです。

次の公式ドキュメントでも言及されています。

facebook.github.io

RedBoxes and YellowBoxes are automatically disabled in release (production) builds.

今回の障害では、Uber EatsのレストランアプリにRedBoxが表示されていました。

誤ってなのか、もともとそういう運用なのかはわかりませんが、今回障害のあったレストランアプリはデバッグビルドされたものだったことが見て取れます。

Textコンポーネントについてのエラーが出ている

RedBoxは console.error() なので、そのとき発生したエラーのメッセージとスタックトレースが表示されています。メッセージの内容を見てみましょう。

Invariant Violation: Text strings must be rendered within a <Text> component.

これはブラウザ開発に慣れ親しんだ方がReact Nativeを触り始めたときによくやるエラーで、簡単に再現することができます。↓の "Tap to play" を押すと、実行結果を見ることができます。

今回起きていたのは、JSX内で <Text> コンポーネント以外の場所に文字列を書くと発生するエラーです。ブラウザなら <div> の直下にテキストを書いても怒られませんが、React Nativeで <View> の直下にテキストを書くと怒られるのです。((最終的にAndroidのTextViewやiOSのUIViewに落とし込まないと表示できないと考えると、妥当な仕様です。))

これは静的解析では見つけづらいエラーなので、ちょっと同情はします。しかし、言ってしまえば凡ミスの類いなので、特に今回の障害でReact Nativeのヤバさが露見したとか、そういうことではないように思います。

追記

使ったことなかったけど、eslint-plugin-react-nativeの react-native/no-raw-text ルールで検出できそう。

github.com

感想と邪推

というわけで、Twitterで報告されていた内容から読み取れる範囲の事実について解説しました。ここからは私個人の意見や妄想の話で、事実に基づく話ではないものが混ざっています。眉に唾をつけて読んでください。

デバッグビルドの話はアプリの運用ルールの話なので、良いとも悪いとも断言できない部分もあるのですが、普通はやらないです。というのも、デバッグ中というのは、APKファイルの中にJSファイルを内包するのではなく、パソコン側で立ち上げた開発サーバーからJSファイルを逐次にダウンロードして画面に描画するため、配布に向いていないはずだからです。

……もしかして、開発サーバーを公開サーバーの中で起動しておいて、配布したデバッグビルドのアプリからアクセスさせている……? そうすれば、Hot Reloadの要領で、最新の実装がリアルタイムでエンドユーザーの手元に届くから……? そんなことある……? 簡易Code Pushとして技術的には実現可能かもしれないけど、本当にやるか……?

妄想はできるのですが、憶測を見てきたかのように語るのもよくないので、口を噤むことにしましょう。

追記:Textコンポーネントのエラーが起きるパターン

ちょっとだけ複雑なパターンもあるようです。

例えば、こんなコンポーネントがあったとします。

type Props = {
  message: string;
};
const Component: React.FC<Props> = ({ message }) => (
  <View>
    {message && <Text>message</Text>}
  </View>
);

これを <Component message=""> のように呼び出した場合、JSX部分の条件式が評価されて次のようになります。

<View>
  ""
</View>

こうなるとViewの直下にテキストがきてしまうので、アウトです。空文字はfalthyなので、短絡評価の結果として左オペランドだけが残った形ですね。Twitterを見ていると、このケースがあるある案件のようです。

とはいえ、想像の域を出ないので、実際にこういう実装をした結果として起きたエラーだというわけではありません。

おわりに

今回の件は、React Native関連の障害としては初めてと言っていいレベルの大規模なものになったように思います。しかしながら、React Nativeのせいというにはちょっと運用のほうが前衛的すぎるんじゃないかなという感想を持ちました。

こんな事例を挙げて、訳知り顔で「これだからReact Nativeみたいなツールは信用できない」みたいなことを言う人が出てくるのも業腹なので、そうではないよ、と言うために筆を取った次第です。

それにしても、本当にどういう運用をしているのか純粋に(割とポジティブな感情で)興味が出てきました。いつかReactConfとかApp.js Confあたりで、今回の障害についてUber Eatsのエンジニアから振り返りセッションがあったりすると面白いのになあ、と思っています。

Application Loader亡き後の私たちはどうやってipaをアップロードするのか

10/25追記: Application Loaderの後継となる公式アプリとして、TransporterがMac App Storeに公開されました。

Transporter

Transporter

  • Apple
  • 開発ツール
  • 無料
apps.apple.com

これで安泰ですね。


Xcode 11がMac App Storeからもダウンロードできるようになりました。既にアップデートした方もいるかと思います。

さて、世の中にはXcode本体を使わずにApp Store Connectにipaファイルをアップロードする方法を何とかして見つけないといけない、ちょっと辛い状況を抱えた人々が存在します。Xcodeでのアップロードが上手くいかなかったり、サードパーティの開発ツールからipaファイルを直接与えられてしまったりと、事情は様々ですが、そういった人たちはこれまで、Application Loaderを使うのが一般的でした。

qiita.com

しかし、残念なことに、Xcode 11にはApplication Loaderが同梱されないことが、Appleからアナウンスされました。

developer.apple.com

私たちはどうすればよいのでしょうか。

幸いにも、コマンドラインからデプロイする方法が従来から用意されています。

blog.kishikawakatsumi.com

altool というコマンドを使えばいいようです。直接叩いた場合には動作しませんでしたが、 xcrun altool という形であれば実行することができました。

$ xcrun altool
Copyright (c) 2009-2019, Apple Inc. Version 4.00.1181

Usage: altool --validate-app -f <file> -t <platform> -u <username> {[-p <password>] | --apiKey <api_key> --apiIssuer <issuer_id>}
       altool --upload-app -f <file> -t <platform> -u <username> {[-p <password>] | --apiKey <api_key> --apiIssuer <issuer_id>}
       altool --notarize-app -f <file> --primary-bundle-id <bundle_id> -u <username> {[-p <password>] | --apiKey <api_key> --apiIssuer <issuer_id>} [--asc-provider <provider_shortname>]
       altool --notarization-info <uuid> -u <username> {[-p <password>] | --apiKey <api_key> --apiIssuer <issuer_id>}
       altool --notarization-history <page> -u <username> {[-p <password>] | --apiKey <api_key> --apiIssuer <issuer_id>} [--asc-provider <provider_shortname>]
       altool --list-apps -u <username> {[-p <password>] | --apiKey <api_key> --apiIssuer <issuer_id>}
       altool --store-password-in-keychain-item <name_for_keychain_item> -u <username> -p <password>

従来、Application Loaderで実施していたアップロード作業に相当する機能を持っているのは、次のコマンドです。

$ xcrun altool --upload-app -f <ファイル名> -t ios -u <username> -p <password>
$ xcrun altool --upload-app -f <ファイル名> -t ios -u <username> --apiKey <api_key> --apiIssuer <issuer_id>

試しに、パスワードを使うほうを動かしてみましたが、問題なくApp Store Connectにipaファイルをアップロードすることができました。

ひとまず、これで生きていくことができそうです。 fastlane deliver を使えばいいような気がするのですが、まあ公式ツールを直接使うに越したことはないでしょう。

iOS 13 プッシュ通知ヘッダー必須問題(解決)とNSDataのdescriptionの話

Amazon SNSがapns-push-typeヘッダーに対応する旨がアナウンスされました。ひとまず解決です。

aws.amazon.com


いろいろ触ってみたんですが、なんだか状況がよく見えないので会社ブログやQiitaに書くわけにもいかず、ひとまず個人ブログで供養しますみたいなやつです。

Motivation

iOS 13(とwatchOS 6)以降のデバイスに対してプッシュ通知を送る場合に、追加のヘッダーが必須になるという情報が、ドキュメントに追記されていました(2019年8月20日現在)。

Header field Description
apns-push-type (Required when delivering notifications to devices running iOS 13 and later, or watchOS 6 and later. Ignored on earlier system versions.) The type of the notification. The value of this header is alert or background. Specify alert when the delivery of your notification displays an alert, plays a sound, or badges your app's icon. Specify background for silent notifications that do not interact with the user.

The value of this header must accurately reflect the contents of your notification's payload. If there is a mismatch, or if the header is missing on required systems, APNs may delay the delivery of the notification or drop it altogether.

For information about configuring notification alerts, see Generating a Remote Notification. For more information about sending background notifications, see Pushing Background Updates to Your App.

Sending Notification Requests to APNs | Apple Developer Documentation

要約すると

  • apns-push-type ヘッダーに alertbackground を指定してください
    • alert を指定するのは、ユーザーに通知の存在を見せたい場合です
    • background を指定するのは、ユーザーに何も気づかせない(バックグラウンドプロセスを起動するのみに留める)場合です

これまで受け取り側(iOSデバイス側)で振り分けていたものを、サーバー側から制御する意図があるようです。

apns-push-typeを付与しないとどうなるのか

APNsに対して行ったプッシュ通知依頼のPOSTリクエストに、 400 InvalidPushType というエラーレスポンスが返るそうです(Table 5 Response error stringsより)。

Amazon SNSとか大丈夫?

同一ユーザーの複数デバイス宛や、あるセグメントのユーザーなど、ある程度のまとまったプッシュ通知を、Android/iOSの両方に送りたい場合などに、Amazon SNSのMobile Pushといった、プッシュ通知のラッパー的なサービスを使うことがあると思います。

これらのサービスは、 apns-push-type ヘッダーを付与してくれるのでしょうか。また、付与しなかった場合のエラーハンドリングはどうすればよいでしょうか。

最悪のケースとしてiOS 13が正式リリースされた直後から、ユーザーにプッシュ通知が届かなくなる恐れもあると思われたため、調べてみることにしました。

apns-push-type問題の結論

2019年8月現在のSANDBOX環境に限っていえば、Amazon SNS由来のプッシュ通知はiOS 13デバイスに届きました。

エラーが出ることを確認してAmazonに直談判するつもりだったので、ちょっと拍子抜けです。

Amazonがこっそり apns-push-type を付与する改修を済ませていたのか、AppleがまだAPNsサーバーにドキュメントの内容を適用していないのか……後者だったら怖すぎる……

deviceTokenが正しく取れない罠を踏んだ

ここからは余談です。

Cordova iOS製のアプリをiOS 13デバイスに入れて検証していたのですが、序盤にAPNsから 400 BadDeviceToken のエラーをもらってしまいました。

Cordovaでプッシュ通知を扱う場合はphonegap-plugin-pushを使います。デバイス登録を行った際の deviceToken を、16進数文字列に変換してから渡してくれるので、ちょっと便利です。

さて、 BadDeviceToken というからには deviceToken にミスがあったのだろうとログを仕込んでみると、次の文字列が出てきました。

"{length=32,bytes=0x3dd89c70dabd0be290cc2f8e215132e0...75a03b3cbf9bbe0a}"

これをBase64エンコードしたものを使ってAPNsにリクエストした結果として、 BadDeviceToken が出ていたようです。さもありなん。

さて、 bytes の中身は16進数っぽいですが、謎の省略がされています。正規表現で抜き出すということもできなさそうです。Objective-C側の挙動が変わったようで、JavaScript側からはどうにもできなさそうです。

実装上の意図されていた挙動

phonegap-plugin-pushのコードを読んでみると、NSData型の deviceToken から description で文字列を取り出した後、加工することで16進数文字列を作っているようでした。

NSString *token = [[[[deviceToken description] stringByReplacingOccurrencesOfString:@"<"withString:@""]
                   stringByReplacingOccurrencesOfString:@">" withString:@""]
                   stringByReplacingOccurrencesOfString: @" " withString: @""];

https://github.com/phonegap/phonegap-plugin-push/blob/110e3c2/src/ios/PushPlugin.m#L365

上記の処理では、[deviceToken description] の結果が次のような文字列になることを期待しています。

<124686a5 556a72ca d808f572 00c323b9 3eff9285 92445590 3225757d b83997ba>

ほぼバイナリの16進数表記ですね。これを加工することで、NSString *token には 124686a5556a72cad808f57200c323b93eff9285924455903225757db83997ba という文字列が格納されていたわけです。

これがなぜ {length=32,bytes=...} になってしまったのでしょうか。

NSData#descriptionの挙動が変わった?

Appleのフォーラムを調べてみると、似たような話題が見つかりました。

プッシュ通知や deviceToken がどうのこうのというよりは、 description の挙動そのものが変わってしまったような印象も受けますが、どうなんでしょう………

phonegap-plugin-pushにPR投げました

ひとまず目の前の問題として、phonegap-plugin-pushを何とかしないといけないので、iOS 13以降では description を使わずに deviceToken の16進数文字列を生成できるようにしたPRを作成しました。

github.com

フォーラムでFBの実装がいいよ!という書き込みがあったので、ほぼコピペです。

Objective-CでOSSにPRを投げる実績を解除したぞ!!!🎉 (まだマージされたわけではない)

マージされました

イエーイ✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️

Ionicでもプッシュ通知するならphonegap-plugin-push使えと言われてるので、かなり広い範囲の人々を救済できたのではないだろうか……

まとめ

iOS 13でプッシュ通知が動かなくなりそうな罠を複数見つけてしまって、気が気でないですね……

どうか穏便に正式リリースの日を迎えたい……

Expo for Webを支えるモジュールたち

React Nativeの抽象化フレームワーク、Expoのv33が先日リリースされました。

blog.expo.io

今回の目玉の一つは、何と言っても Expo for Web です。React Native for Webを取り込んだことで、Expoで作ったアプリがよほどネイティブな機能を使ってない限り、ブラウザでも動かせるかもしれない感じになってきました。

どうして歯切れが悪いのかというと、当然Expoにはブラウザでの再現が困難な機能も多く含まれているからです。React Native for Webのサンプル集にあるような機能であれば高確率で動くので、安全な範囲で導入したい方は expo パッケージの機能に手を出さないようにしておけば、かなりの部分は動くはずです。

安全でない範囲で導入したい方への朗報としては、sdk-33ブランチ(執筆時点でf5f35c30)を見ていただけるとわかるのですが、次のようなモジュールのWeb版(.web.ts/tsx)が実装されています。

  • import { Video, Audio } from 'expo-av';
  • import { GLView } from 'expo-gl';
  • import { SQLite } from 'expo-sqlite';
  • import { Camera } from 'expo-camera';
  • import { Accelerometer, Gyroscope } from 'expo-sensors';

どれもインパクトがあるやつですね。だいたいEvan Baconの仕業です。何なんだあの情熱はどこから来るんだ。

ちゃんと実装を見てみると、そんなに魔法は使っていないパターンが多く、普通に 最近のWeb APIってすげーな 、という感じになります。

魔法枠としては、実はfeatureブランチですがブラウザ上でBarcodeScannerを動かすコードもEvan Baconの手によって実験されています*1。何なんだあの情熱はどこから来るんだ。

こんな感じで、単にReact Native for Webを取り込んだだけではなく、従来からあったモジュールに関しても(Evan Baconがほぼ一人で黙々と作業して)Web版がどんどん実装されています。正直、彼の情熱が尽きた時点でメンテナンスされなくなる恐怖があるので、あんまりまだ使おうと思えないのですが、我こそは人柱になろうという奇特な方がいらっしゃれば、ぜひお願いします。

本当はプロダクションビルドしたビルド済みフォルダの話がしたかったのですが、長くなってきたので次回にします。

*1:まあjsQR使ってるだけといえばだけですが

モバイルアプリ開発フレームワークのレイアウトの計算、描画方式の違い

レイアウト計算のエンジンの派閥みたいなのがたまに気になるので、いま認識している範囲で雑に書いておきます。

「俺の愛する○○が入ってないじゃないか」は、僕が知らないかたまたま思いつかなかっただけで、特に意図的に排除したものはありません。

公式のレイアウト計算+公式の描画

Androidでいえば、Androidを支える技術<I>で解説されているような、プラットフォームネイティブなレイアウト計算(measure)と描画(draw)が行われている方式です。

  • Android SDK
  • iOS SDK
  • Xamarin Native

公式のSDKがここに入っているのは当然のこととして、Xamarin Native*1も、普通にLayout XMLやStoryboardでUIを組んだりするので、こちらに入ります。

独自のレイアウト計算+公式の描画

レイアウト計算は独自の方式で実施しつつ、描画はネイティブ側におまかせする方式です。

  • Yoga (React Native / Litho / ComponentKit)
  • Xamarin.Forms

このジャンルだと、広く使われていそうなのはFacebookのYogaでしょうか。Flexboxを中心とした、Webの方法論でUIを構築するためのレイアウトエンジンです。

今回調べてみて初めて知ったのですが、Xamarin.Formsも独自のレイアウトエンジンで動いてるっぽいですね。↓の記事を流し読んだだけなので、もしかしたら違うのかもしれませんが。

xamarinhelp.com

独自のレイアウト計算+独自の描画

レイアウト計算も描画も自前で頑張ってる方式です。

  • Flutter Engine
  • Unity
  • Jetpack Compose?

公式と言えば公式のJetpack Composeを独自と呼ぶのもアレですが、Canvasにゴリゴリ書いてるみたいです。レイアウト計算について明言しているところは見つかりませんでしたが、ひとまずここに置いてみました。

FlutterやUnityは、独自方式をとっているおかげで、プラットフォーム間やバージョン間での表示差異が出づらいのがいいところです。

まとめ

クロスプラットフォーム方面は、言語やUIキットの違いが取り沙汰されることが多い感じですが、こうして中を見るとレイアウト計算や描画の方式にも違った特徴があります。

言語やパラダイムが似ているように見えても、プラットフォーム差異やバージョン差異の起こりやすさ、ネイティブUIの取り込みやすさなどで違いがあるので、意識しておくとよさそうです。

追記

エンジンがモバイル向けとして作られているわけではないのであえて書きませんでしたが、CordovaのレンダリングエンジンをWebViewであるとした場合は、「独自のレイアウト計算+独自の描画」枠です。

*1:Xamarin.Formsを使わない開発方式をこう呼ぶと聞いたことがあるので、この名称を使わせてください