ナカザンドットネット

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

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を使わない開発方式をこう呼ぶと聞いたことがあるので、この名称を使わせてください

React Native for Windowsを斜め読みした感想

jp.techcrunch.com

はい、なんか出てきました。「react-native-windowsなら前からあったじゃん」と思ったのですが、どうやら大幅にリライトしたみたいなので、本家とどのくらい違うのか、簡単に流し読みしてみました。雑に読んだだけなので、たぶん勘違いを多分に含んでます。眉に唾をつけて読んでください。ちゃんと知りたい人はコード読んでください。

三行で

  • VC++使った結果、言語をブリッジするレイヤーがひとつ減ったのは面白い
  • フォルダ構成は大きく変わったけど、たぶん妥当
  • 開発者が使うときのAPIだけ本家と共通なら、内部実装の方針は多少本家と違ってても大丈夫なはず(ほんとか?)

前提

React Native製のアプリは、ざっくり分けると、次の3つのレイヤーで構成されます。

  • 開発者が作成したJavaScript製Reactアプリケーション
  • JavaScriptライブラリとしてのReact Native('react-native'モジュール)
  • 「JavaScript実行環境+ネイティブブリッジ」によるミドルウェアとしてのReact Native

「ミドルウェアとしてのReact Native」がWebViewのような役割を果たしつつ、DOM APIの代わりとなるReact Native Rendererにアクセスできる「JavaScriptライブラリとしてのReact Native」を私たちが利用することで、アプリを組み上げていく形です。

「ミドルウェアとしてのReact Native」には、C++ライブラリであるJavaScriptCoreが含まれており、この中でJavaScriptが動いています。さらにC++とObjective-C/Javaをつなぐコードも用意されているので、全体としては次のような構成になっています。

JavaScript
↕️ (JavaScriptCore)
C++
↕️ (Objective-C++ / Android NDK)
Objective-C / Java

このへんを軸に話をしていきます。

今までのreact-native-windows

まずは従来のreact-native-windows*1がどうなっていたのかを確認しておきましょう。現在のmasterである 4798c610 では、currentフォルダに旧実装が格納されています。

github.com

f:id:Nkzn:20190507111541p:plain
currentフォルダ

割とReact Native本家に近い構成になっています。ミドルウェアのiOS実装である React フォルダや、Android実装である ReactAndroid フォルダの代わりに、 ReactWindows フォルダが用意されている形です。中身は次のような感じ。

f:id:Nkzn:20190507111803p:plain
ReactWindowsフォルダ

ChakraBridge フォルダがあるので、JavaScriptCoreの代わりにChakraを使ってるんですかね(未確認)。ChakraBridgeの中はC++ですが、他のフォルダはだいたいC#で書いてあるようです。

f:id:Nkzn:20190507112142p:plain
ReactNative.Sharedフォルダ

ここまで来ると、だいぶC#プロジェクトっぽい見た目になってますね。

ざっくりとは次のような構成になっていると思われます。

JavaScript
↕️ (Chakra?)
C++
↕️ (Visual C++?)
Visual C#

ぶっちゃけWindows文化圏の開発環境に詳しくないので間違っていそうな気もする・・・

新しいreact-native-windows

それでは今回発表された、新しいReact Native for Windowsのほうを見てみましょう。 4798c610 のvnextフォルダに新しい実装が格納されています。

github.com

f:id:Nkzn:20190507112837p:plain:h640
vnextフォルダ

ごそっと増えました。ざっくり見た感じでは、次の点が変わっています。

  • ReactWindowsフォルダの中身をトップレベルに移した
  • ReactWindowsフォルダにまとめられていたネイティブ実装をUWP実装とWindows Desktop実装に分けた
  • C#製のネイティブ実装を(おそらくすべて)C++で再実装した

たぶん他にも色々あると思うのですが、私のWindows力が足りない……

C++版をmasterにマージした際のコメントに細かいコミットコメントがまとめられてるっぽいので、興味がある方はどうぞ。

この変更で、「ミドルウェアとしてのReact Native」は次のような構成になったのかなと想像しています。

JavaScript
↕️ (Chakra?)
Visual C++

これもWindows素人の想像なので、違うっぽかったらご指摘ください!

「ミドルウェアとしてのReact Native」の大変革については、後述の「感想 > 全面C++リライト」で感想を述べます。

「JavaScriptライブラリとしてのReact Native」はまだポーティングを進めている最中という感じでした。このへんはおいおい実装されていくと思います。ミドルウェアに合わせてJavaScript側の実装も最適化*2されていますが、コンポーネント名とPropsの型は本家に準拠してるっぽいので、普通に使っている分にはそんなに問題にはならないでしょう(たぶん)。JavaScriptCoreとChakraの差異とかのほうがよっぽどやばそう。

感想

というわけで本題です。疲れたので簡潔にいきます。

全面C++リライト

React Nativeは「ミドルウェアとしてのReact Native」が必ず持っている C++で書かれた処理そのプラットフォームで本来使われているGUI開発言語 に繋げないといけない性質上、「他言語とのブリッジ」という複雑な機構を持たざるをえませんでした。C++とJavaを繋ぐためにAndroid NDKのJNI機構が使われていますし、C++とObjecitve-Cを繋ぐためにObjective-C++が使われています。

さて、Java/KotlinやObjective-C/Swiftを使わずに画面を作ることはできるでしょうか。Android NDKでUIを書くことは可能ですが、あまり一般的ではありません。Objective-C++からUIKitを触るのは……できたっけ?(うろ覚え)もできます*3が、煩雑になりやすいため、やはり一般的ではないようです。

では、Windowsアプリケーションの開発ではどうかというと、Visual C++で画面を開発するのは、そこそこ一般的だったと記憶しています。POSレジのアプリがVC++ランタイムで動いているのを見たことがありますし。古い記憶なので今では廃れつつあるのかもしれませんが。そのへんよくしらない。

Windows向けの実装として考えた場合に、ブリッジを1段階廃して、最初からC++で書くというのは、パフォーマンスの面では良い選択であるように思えました。

C#でネイティブモジュールを作っていた人達からすると「何してくれてんのォ!?」という感じかもしれない……どうなんだろう……C++からC#を呼び出すブリッジコードみたいなのを足せば既存のモジュールも動くのかな……そこだけ心配……

フォルダ構成の変更

本家とこんなにフォルダ構成変えちゃって大丈夫なの?というのは思いますが、これはこれでありなのかなーと思えてきました。

本家は「AndroidとiOSの両方に対応する」という制約があるので、iOS向け実装とAndroid向け実装を別々に管理する必要がありましたし、各プラットフォーム向けのファイルがトップレベルに漏れ出すのは可能な限り避けられていました。しかし、react-native-windowsはWindowsのことだけ考えていればいいので、トップレベルにWindows向けの都合が漏れ出しても、そんなにまずくはありません。それどころか、UWP版とWinDesktop版の実装を分けるにあたっては、このくらいのフォルダ構成にしておいたほうが見通しが良くなるくらいかもしれないです。

本家との挙動の違いについて

本家側の修正に追従しようとしたときにしんどそうじゃない?という疑問は湧いてきますよね。

まあそもそも、本家側もiOSとAndroidで挙動が同じなのかという話がありまして、コントリビューターたちが頑張って似たような挙動になるようにしているだけで、別に挙動を同じにする仕組みがあるわけではないのです。

なので、Windows版の挙動も頑張って合わせていく感じになるんだろうなあと思います。

ひとまず「JavaScriptライブラリとしてのReact Native」を私たちが触るときのAPIさえ本家と同じ型なら、割り切って使うことはできそうです。本家のAPIについてはOpen Source Roadmapで語られたとおり、React Native 1.0を目指してStableなAPIにしていくそうなので、react-native-windowsもそこに追従してくれれば、多少内部実装が違っても使う側としては「これはこれで」になるのかなと。

まとめ

Windows素人の雑な感想でした。

たぶんこれからOfficeやSkypeなどで実用しながらブラッシュアップされていくと思うので、見守っていきたいと思います。

*1:たぶんWindows版のSkypeとかOfficeがこれを使ってる

*2:Android向けワークアラウンドを消したり

*3:https://twitter.com/omochimetaru/status/1125698058337964032

GUIアプリケーションのバージョニングどうする問題(あるいはメジャーバージョン上げられない問題)

皆さん、リリースしてますか! いいですよね、リリース。(雑な導入)

GUIアプリケーション(Webアプリやモバイルアプリ)のバージョン番号をどうやって決めるか(バージョニング)を迷ってしまったので、考えていることを一度吐き出してみることにしました。

結論

結論からいうと、GUIアプリケーションのバージョニングはSemVerにこだわる必要はないです。サービスを提供する主体として、開発するチームとして、そのバージョン表記によって誰に何を伝えたいのかがハッキリしていて、それが伝えたい人々に伝われば、何だっていいです。

SemVer

現代のWeb界隈でデファクトスタンダードなバージョン記法といえば、セマンティックバージョニング(通称SemVer)ですよね。

semver.org

バージョンを X.Y.Z (ex. 2.25.3)のような、ドットで区切られた3つの数値によって表現する方法です。それぞれの桁のバージョンアップは、次のような意味を持ちます。

  • Z (パッチバージョン)の数字を上げるのは、外部とのAPIを変えずに(後方互換を保ったままに)不具合を修正した場合です。旧バージョンの間違った振る舞いを正しくするために使います。
  • Y (マイナーバージョン)の数字を上げるのは、従来のAPIの後方互換を保ったままに外部とのAPIを追加した場合です。
  • X (メジャーバージョン)の数字を上げるのは、従来のAPIと後方互換がない(互換性が破壊された)変更がある場合です。

細かいところは前述の原典のサイトを見ていただいたほうがいいのですが、私はざっくりとこのように理解しています、ということで。

SemVerは、ライブラリの利用者がライブラリのバージョン表記を見て、自分のプロダクトに更新を取り込むべきかどうかを判断する際に、よい指針となります。ライブラリの作者と利用者にとって、素晴らしいコミュニケーションツールです。言い出しっぺのNPMでは遵守されていますし、他のパッケージ管理ツールも緩やかに似たようなルールを取り込んでいっている気がします*1

GUIアプリケーションとSemVer

さて、これをGUIアプリケーションのバージョンに適用するとどうなるでしょうか。実際にストアを見てみると、SemVer的なバージョニングを実施しているプロダクトは少なくないように思えます。私も会社で作っているAndroidアプリやiOSアプリやWebアプリに、SemVer準拠なバージョンを与えることが多いです。

SemVerなアプリが多い

SemVerはライブラリ/パッケージのために生まれました。では、GUIアプリケーションにとっても適したバージョニングなのでしょうか。前述した、それぞれの桁の意味について、GUIでの在り方を再考してみましょう。

  • Z の数字を上げるのは、ユーザーからの見た目や使いかたを変えずに、間違った挙動を正しくするためです。
  • Y の数字を上げるのは、従来の使い勝手を崩さずに、機能を追加した場合です。
  • X の数字を上げるのは、従来の使い方ができない部分が出てくるくらいにUIの更新があった場合です。

こんなところでどうでしょうか。SemVerでいうAPIを「ユーザーの使い勝手」という定性的なものに置き換えた形になります。

Z の数字を上げるのは、ユーザーからの見た目や使いかたを変えずに、間違った挙動を正しくするためです。

これは問題なさそうです。UIを変えずに内部処理の修正だけをしていた場合や、レイアウト崩れを修正した場合などに適用できそうです。

Y の数字を上げるのは、従来の使い勝手を崩さずに、機能を追加した場合です。

……ちょっと苦しくなってきました。どのくらいまでならUIを変えても「従来の使い勝手が崩れていない」と判断できるのでしょうか。機能要件は変わってないしコンポーネントの配置もほとんど変わっていないけれど、カラーリングやアイコンのリニューアルを行ったら、それは「従来の使い勝手が崩れていない」のでしょうか。

X の数字を上げるのは、従来の使い方ができない部分が出てくるくらいにUIの更新があった場合です。

これだと Yとの境目がわからない んですよね……従来のUIとの地続きと呼べる範囲ならYでよくて、そうでない場合がXなのかな……

しっかりYと差別化しようとすると、 驚き最小の原則に反するケースじゃないと上げられなさそう なんだけど、流石にそれはまずいんじゃないかな……

メジャーバージョン上げられない問題

SemVerを愚直にGUIアプリケーションに適用しようとすると、Xであるメジャーバージョンを上げるのは、めちゃくちゃ勇気のいる破壊的な変更であるような気がしてきました。

実際、「市場検証の意味合いも強かったv1」や「v1をもっとちゃんとしたやつ版のv2」を作った後、v3にするタイミングを逸しているところとか、あるんじゃないでしょうか*2

やってるところは「これはマイナーバージョンアップ(Y)でもいいと思うんだけど、大きな変更だから、記念も込めてメジャーバージョン(X)あげちゃえ! えいや!」と上げてるのでしょうか。うちはこうしてるぜ!的なのがあれば、Qiitaとかブログに思想をつらつらと書いて教えていただけるとめちゃくちゃ助かります。

SemVerではない運用

SemVerではないバージョン記法を採用しているプロダクトもあります。

TwitterはX.Y

Twitterは X.Y のバージョン記法を採用しています。Xが上がったときは実際にそこそこびっくりする程度にUIが変わった気がするので、おそらくこれはメジャーバージョンとして運用されているのでしょう。一方、Yは機能追加や不具合修正の両方でカジュアルに上げているのだと思われます。

f:id:Nkzn:20190422172140p:plain:w320
ChromeはMAJOR.MINOR.BUILD.PATCH

Chromeは数値が4つ並んでいます。Chromeとしてのバージョニングのルールは見つかりませんでしたが、Chromiumプロジェクトにそれっぽい記載がありました。

www.chromium.org

BUILDやPATCHはRC版のリリースを重ねるために用意されているようです。そして、MAJORとMINORはChromeとしてリリースされるようなちゃんとしたバージョン付けに利用されるとのこと。MAJORはやはり、後方非互換の変更を表すために利用されるようです。 ドキュメント上は。

WikipediaにあるChromeのリリース履歴の読み方が間違っていなければ、 1.0以降から今日の73.0に至るまで、マイナーバージョンが運用された実績がv4.1.249の1回しかない ように見えます。機能追加しかないように見えるアップデートでもメジャーバージョンを上げ続けてここまで来ています。まあ、ChromeはGUIアプリケーションというよりは仮想マシンに近いですし、JavaScript処理系の細かいところまで見ていけば全てのメジャーバージョンアップがちゃんと後方互換のない変更なのかもしれませんが……

そんなわけで、Chromeは事実上、メジャーバージョンとマイナーバージョンを統合している(ように見える)運用をしています。

バージョニングは誰のために

そもそもバージョニングは誰のために行うのでしょうか。私は バージョン番号から何かを読み取りたい人 のためにバージョンを運用するべきだと思っています。ライブラリやパッケージの場合は、後方互換性の程度を利用者に読み取ってほしくて、X.Y.Zという3つの数字を運用する、SemVerが重宝されることになりました。

では、GUIアプリケーションのバージョンは誰に何を伝えたいのでしょうか。

Webアプリやストアアプリに関しては、エンドユーザーには基本的に最新版を使ってもらうことを想定していて、ユーザー側で昔のバージョンを細かく使い分けることもないので、サポートの面から考えても「最新なのか、そうでなければどのくらい古いか」だけがわかればよさそうです。数値がひとつだけあれば、バージョニングは事足りてしまうかもしれません。 Chrome 73 といった表記も、それに近い部分がありますね。

一方で、それを提供する会社の社内ではどうでしょうか。広報担当と一緒にリリース内容をどう宣伝するか考えるときに「新機能や機能改善を含むリリース」なのか「不具合修正のみのリリース」なのかがバージョンを見ただけでひと目でわかるのは、とても便利そうです。 FEATURE.PATCH のようなふたつの数値で表すことにして、前者と後者のどちらが上がったのかを見ることで、社内のコミュニケーションを円滑にするのは、良い方法かも知れません。

もし、ユーザーに一定以上の負担を強いるタイプの大幅なUIリニューアルを、それなりの頻度で行う社風があるのであれば、後方非互換性を表すメジャーバージョンの運用も、視野に入れてもいいのかもしれません。メジャーバージョンを上げるようなリニューアルをするたびにユーザーに怒られているTwitterを見ていると、それがいいことなのか、少し疑問ではありますが。

まとめ

バージョニングは、プロダクトの作り手と、プロダクトを利用したり運用したりする人々との、コミュニケーションツールです。伝えたいことが必要十分に伝わるならば、必ずしもSemVerにこだわる必要はないと、筆者は考えています*3

プロダクトの特性や、開発組織の特性によって、どのようなバージョニングを採用するかは工夫してもよさそうです。皆さんの組織では、どんなバージョニングをしてみたいですか?

*1:まだmaven central repositoryなんかはやんちゃなバージョニングをしているなあと思いますが

*2:弊社です

*3:NPMにリリースするようなライブラリは、^とか~が動かなくなるかもしれないのでちゃんとSemVerにしましょうね

増刷が決まりました!(たった1日で基本が身に付く!Androidアプリ開発超入門)

昨年9月に、Androidアプリ開発の入門書を発売しました。

blog.nkzn.info

たった1日で基本が身に付く!  Androidアプリ開発超入門

たった1日で基本が身に付く! Androidアプリ開発超入門

 

発売から約7ヶ月が経ち、amazonでレビューをいただいたり、Twitterで抜け漏れを指摘していただくようになり、多くの方に読んでいただいていることを実感しています。

大学生向けのセミナーで、テキストとして導入いただいている事例もありました。

さて、そんな拙著ですが、この度、

増刷されることが決まりました!

🎉🎉🎉🎉やったぜ🎉🎉🎉🎉

技評さんとしても予想外の売れ行きだったらしく、嬉しい限りです。

 

第2刷での変更点

改版ではないので大きい変更はありませんが、主に次の点を修正しています。

  • 図の指示の誤りを修正
  • プロジェクトセットアップ手順のAS3.3対応
  • 用意したのに何故か使われなかったアイコンについて言及
  • 日本語化zipファイルのダウンロード方法について追記

特に最初の図の誤りはサポートページにも載っているやばいやつなので、最優先で修正になりました。

また、Androidプロジェクトのセットアップウィザードが、Android Studio 3.2のときに大幅に変更されまして、初心者殺しになっていたので、こちらも修正しました。手順が減ったので、ページ数が減りかねない事態だったのですが、編集さんが腕力でなんとかしてくれました。ページ数に変更はありません。

他にも、読者の方からTwitterでご質問いただいた内容を元に、修正することができました。ありがたい限りです。

今後ともよろしくお願いします

(序盤だけですが)AS3.3対応で、また最新の現場でも使いやすい入門書になったと思います。

未経験から最初の一歩を踏み出すのには、とても適した本らしいことが、amazonのレビューで分かってきました。皆さんの身近にも、Androidアプリ開発に興味のある方がいれば、オススメしていただけると幸いです。

前書きにも書きましたが、これは僕が10年前にAndroidをやり始めた頃に読みたかった本です。この本でAndroidアプリ開発への第一歩を踏み出した方と、何年後かにお会いできることを、楽しみにしています。

 

余談

読者の方から相談を受けるトップの話題が、「この本の次に何を読んだらいいですか?」です。

この本でどのレベルに到達できたかもよるのですが、ある程度しっくり来た方には、重村さんのポケットリファレンスをオススメしてもいいかなと思っています。

[改訂新版]Android SDKポケットリファレンス (POCKET REFERENCE)

[改訂新版]Android SDKポケットリファレンス (POCKET REFERENCE)

 

「こういうことをしたい場合はこうするといい」がたくさん詰まっており、目次を読むだけでも楽しいです。サンプルも豊富なので、写経してみるだけでもいいかもしれません。

私も新人の頃には初版にお世話になったので、効果は折り紙つきです。よろしければいかがでしょうか。

経験者の方、他にも「この本がいいんじゃないか」というのがあれば、教えてください。

「ネイティブアプリ開発者は絶滅危惧種なのか?」への感想文

jp.techcrunch.com

こちらの記事への雑な感想です。感想は私の主観であり、ポジショントークであり、所属する組織の意見とは無関係であることを先に述べておきます。

また「ネイティブ」という言葉に「C/C++などから作られた機械語」という本来の意味に加えて、「プラットフォームの標準言語(WindowsのC#, AndroidのJava, iOSのObj-C)や標準開発ツールである」というニュアンスを含めることをご容赦ください。

ポジション

こんな感じのポジションの人です。

  • 中小企業向けにBtoBでアプリを作ることが多い中小企業の人です
  • エンジニアとしてのキャリアの大半がAndroidアプリ
  • 最近はReact Nativeやってる
  • もっと最近はPWAやってる
  • どんなテクノロジがイケてるかよりも、目の前の事業を前に進めるにはどんなテクノロジが適切かを考えるのが好きです

MSがRNめっちゃ使ってるという話について

まずはこちら。

「マイクロソフトのiOSおよびAndroidアプリの中身をスキャンしてみた。その中で、Word、Excel、Xbox、その他もろもろ38本ものアプリが、最近のアップデートでReact Nativeを利用するようになったことを発見した」

昨年の6月に中の人がツイートした、開発中の話題が、実際にリリースされるようになったことを受けての話題ですね。

ツイートはこちら。

Twitterだと話題の発散がしんどいということでRedditに移動したのがこちら。

www.reddit.com

ここで注目しておきたいのは、

Office 365's UI, a lot of it, but definitely not all of it, are pieces that are built using React Native (Windows). API's and Services are still going to be powered by C++, C#, or whatever is the most appropriate for that team. Nothing is converting to "all/completely" JavaScript/TypeScript.

という説明です。「Office 365のUIの多くがReact Nativeやreact-native-windowsで組まれている」のは確かです。しかし、「APIやサービスは各チームにとって最適な言語であるC++やC#や他の言語で書かれており、アプリケーションのすべてをJavaScriptやTypeScriptで記述したものはない」とも言っています。

あえてPDSの用語で分けるなら、「UI」というのはいわゆるプレゼンテーション層でしょうし、「APIやサービス」というのはドメイン層*1にあたるでしょうか。ざっくり言いかえると「画面はReact NativeとJS/TSで書いてるところが多いけど、ビジネスロジックは各チームにとって適切な言語で書いてるよ」ということになりそうです。

Brownfield事例は実質的にネイティブの事例

React Nativeの運用としてはBrownfieldとGreenfieldという言葉があり、「大部分をネイティブアプリとして運用しつつUI付近でReact Nativeを活用する」ことをBrownfield、「ネイティブ側はできるだけinit時のままにしつつ、アプリケーションとしてのすべてのロジックをJS側に書く」ことをGreenfieldと呼びます。Greenfieldの場合は必要なAndroid/iOS開発の知識が少なめになるので、JavaScript出身者にはオススメです。

さて、Office 365の事例は典型的なBrownfieldです。本来ならネイティブUIで開発を行ってもやれるくらいの玄人集団が、ReactというUI状態管理パラダイムによる開発の効率化や、UI人材の効率的な運用のために実施した施策であると感じました。UIの共通化という目的も入っている可能性は否めませんが、「作り方がだいたい同じである」という時点で人材運用上の価値が高い(Learn Once, Write Anywhere)ので、プラットフォームごとにパーツを作り直していても別に不思議ではないかなと思っています。

これは「React NativeとかいうUI管理DSLライブラリを使って、ネイティブアプリのUI実装を効率化したよ」とか「UI実装をWebチームの人に手伝ってもらえるようになったよ」みたいな話であり、開発スタイルとしては依然としてネイティブアプリ開発者の力が多大に必要であるはずです。

XAMLやUIKitやStoryboardやレイアウトXMLやDataBindingといった、ネイティブUIのツールキットを使う機会が減っている可能性は高いですが、GUIアプリケーションはそれらだけでできているわけではありません。むしろ複雑なアプリケーションであれば、PDSのPよりDのほうが大きくなることもあるでしょう。Officeの開発では、ネイティブアプリ開発者が、めちゃくちゃ活躍しているはずなのです。たぶん。

そういった観点から、Officeの事例を理由にしてネイティブアプリ開発者の需要を語る切り口は、少し的を外しているように筆者は思いました。

Skypeの事例ならどうなのか

記事内では触れられていませんが、SkypeチームはReact XPという自作のReact Nativeラッパーを開発しており、おそらく今でもこれを運用しています。

microsoft.github.io

これは「Write Once, Run Anywhere」寄りの思想で作られているように筆者には見えており、比較的にGreenfield寄りの運用をしているのかなと想像しています(想像です)。Skype内での活用事例の記事を見ても、JavaScriptランタイムでの話ばかりが出てきます。

React XPはReact NativeをWebやWindowsにも拡張して運用するためのラッパーなので、特殊といえば特殊なのですが、こちらの運用ではネイティブアプリ開発者が登場する場面は少ないのかもしれません(想像です)。Android専用モジュール(hoge.android.tsx)や、Web専用モジュール(hoge.web.tsx)みたいなものを作るケースはありそうなので、ゼロ人ということはありえませんが、Officeチームよりはネイティブアプリ開発者の割合を減らせそうな気がします。(中の人!違ってたらごめん!違ってたらマサカリ投げてくれ!!すぐ直す!!!)

こっちを根拠にして記事を書いてくれてたら、ちょっとだけ納得感上がったのになあ、と思ってます。

ネイティブアプリ開発者の仕事は減るのか

ここからは筆者の極めて個人的な意見です。筆者は自分のキャリアを考えていく上で、現在の業界をこういう観点で見ているよ、という文章なので、違った考え方を持っている人はいっぱいいると思いますし、それらはすべて素晴らしい観点です。是非この記事のブコメに短く書いたりせずに、ご自身のブログ等で持論を展開してください。読みたいです。

ハードウェア利用とかバックグラウンド動作の要件が強いものはネイティブアプリとして作るしかないものもありますが、オフライン時の動作くらいであれば、Service Workerなどの進化(いわゆるPWA)によって解決されつつある分野も多くなってきました。サーバーからもらったデータを画面に表示して、ユーザーの入力をサーバーに投げるだけ*2の要件にネイティブアプリを作る必要があるのかというと、筆者の中では少しずつ疑問符が付くようになってきています。

BtoBでお客さんが「アプリが欲しい」と言った場合に、実はそれは素朴なWebアプリでいい要件だったり、少し込み入ってもPWAの範囲でなんとかなるものは、意外と多いのではないでしょうか(肌感覚です)。もちろん、ネイティブアプリで作るべき要件も世の中に多くありますが、それはすべてではありません。工数や時間やお金をかけて、AndroidとiOSのネイティブアプリをそれぞれ作るコストをかけたいほどの要件を持った顧客は、実はそこまで多くないのではないでしょうか(完全に肌感覚です)。

2010年ごろ、Ajaxの流行とともに、.NET製のデスクトップアプリケーションの一部を、Webアプリケーションに置き換える案件が多くあったように思います。それと同じように、モバイルアプリの世界もブラウザ技術に置き換わっていく部分が出てくると、筆者は考えています。当時とはブラウザ技術の雰囲気も随分と違うので、形は違うことになるかもしれませんが、プラットフォームネイティブな開発一辺倒だった業界が、現実的な別の選択肢を得て、多様化していく、という点では似たようなことになるのかなと。

そういう観点では、相対的に、モバイル向けのサービス提供という分野におけるネイティブアプリの比率は、下がっていくのかもしれません。そういう意味では、ネイティブアプリ開発者の仕事が減るという話も、わからなくはないです。

いまネイティブアプリ開発者である人は、これからシェアが広がっていく(かもしれない)ブラウザ向けの開発を覚えるのもいいのかもしれませんし、よりネイティブアプリ開発者としての力を付けて仕事を取っていくのもいいのかもしれません。「モバイルアプリ開発者」という肩書きで、AndroidネイティブもiOSネイティブもブラウザも全部いけるぜ!というポジションを取っていくのもいいかもしれません。

まとめ

ここから先は君の目で確かめてくれ!という無責任な締めになってしまうのですが、僕もまだAndroidアプリエンジニアを自称している立場であり、今後こういった時代の渦中に放り込まれる立場なので、ご勘弁ください。これからどうなるのかは僕だって知りたい。

冒頭の記事については、ちょっと飛ばし過ぎだけど示唆に富んでいるので、話半分で読んでおこうね、くらいの気持ちです。

ポジショントークは以上です。

みんなの反応

是非この記事のブコメに短く書いたりせずに、ご自身のブログ等で持論を展開してください。読みたいです。

ちゃんとブログを書いてくださった方々を見かけたので、この記事の次に読む記事としてオススメしておきます。

Xamarin勢の反応

「なんでそこでXamarinじゃねーんだよ!」に対するアンサーです。

qiita.com

Cordova勢の反応

昔のイメージでCordovaをdisんないでくれ頼む、というお話。

note.mu

iOSネイティブアプリ開発者の反応

ハイブリッドアプリ的な技術とどう向き合うか、どう見なすか、というお話。(すごくよかった)

otihateten.hatenablog.com

*1:DDDのやつじゃないです

*2:この「だけ」がでかいんですが