NimbleをSwiftWasmに対応させてみた

SwiftWasmとは、SwiftをWebAssemblyにコンパイルしてブラウザやその他WebAssemblyランタイムで動作するようにしようという野心的なプロジェクトです。

ちなみにSwiftWasmには id:kateinoigaku さんがco-maintainerとして多大な貢献をされています。

fortee.jp kateinoigakukun.hatenablog.com


最近試しに、メンテナーをしているNimbleをSwiftWasmに対応させてみました。

github.com

以前にも一度トライして断念していたのですが、現在では標準ライブラリだけでなくFoundationもThread/GCDなど一部を除いて問題なく動作するようになっていたので、意外とすんなりと対応させることができました。Threadが使えないので toEventuallywaitUntil などのAsync expectationsの機能やNotificationのマッチャーは使えませんが、それ以外はテストスイートも全てパスしています。すごい!

喜んでもらえている様子:

GitHub ActionsでのCIについても https://github.com/swiftwasm/swiftwasm-action というアクションが用意されているので、手軽にCIでビルドやテストを実行させることができます。

ということで、Swift製のライブラリをお持ちの方は一度SwiftWasm対応を試してみてはいかがでしょうか。

現代ではSwiftの条件コンパイル用のフラグ定義にはOTHER_SWIFT_FLAGSではなくSWIFT_ACTIVE_COMPILATION_CONDITIONSを使う

タイトルがすべてです。意外と忘れがちなので。

developer.apple.com

Active Compilation Conditions is a new build setting for passing conditional compilation flags to the Swift compiler. Each element of the value of this setting passes to swiftc prefixed with -D, in the same way that elements of Preprocessor Macros pass to clang with the same prefix. (22457329)

uber/mockoloのclassモック生成のバグを修正した

uber/mockoloとは

というものです。

mockoloはv1.1.3からprotocolだけでなくclassのモック生成もできるようになったのだけど、これを実際に使おうとしてみると生成されたコードがコンパイルエラーになった。

具体的には、次のようなデフォルト値ありのpropertyを持つclassで問題が発生していた。

// Foo.swift

/// @mockable
class Foo {
    var foo: Int = 0
}


// Mocks.swift

class FooMock: Foo {

    private(set) var fooSetCallCount = 0
    override var foo: Int = 0 { didSet { fooSetCallCount += 1 } }
}
  • stored propertyをoverrideして新たなデフォルト値を代入することはできない
  • stored propertyを新たなデフォルト値でoverrideしようとして、そこにproperty observer (willSet/didSet)を付けようとすると Variable with getter/setter cannot have an initial value というおかしなコンパイルエラーになる
    • property observerはgetter/setterではない

propertyのモックにデフォルト値を代入するのは元々protocolモック用の実装だったので、classモックではデフォルト値を代入しないようにして解決した。

github.com

これによって生成コードはこうなる:

class FooMock: Foo {

    private(set) var fooSetCallCount = 0
    override var foo: Int { didSet { fooSetCallCount += 1 } }
}

ということで、こちらの修正が含まれたv1.3.2がリリースされていたので、どうぞお使いください。mockoloのclassモックはあまり枯れていないようなので枯らしていきましょう。

App Storeの定期購読・無料トライアルの基準タイムゾーンはUTC

App Store Connectのドキュメント(ヘルプページ)にはこうあります:

自動更新登録は、1 か月間の日数ごとではなく、最初の購入の暦日と同じ日に更新します。たとえば、カスタマーが 1 か月の無料トライアルを 1 月 7 日に開始する場合、トライアルが終了するのは 2 月 7 日です。翌月に同じ日が存在しない日に 1 か月のトライアルを開始した場合、トライアルが終了するのは翌月の末日ですが、利用可能になると元の日付に戻ります。たとえば、カスタマーが 1 月 30 日に登録すると、次の更新日は、2 月 28 日 (閏年の場合は 2 月 29 日) で、その次は 3 月 30 日になります。

これだけだと「そうですね」となるのですが、仕事で見かけた事例によると、どうも日時はUTCタイムゾーンを基準としているようです。日本時間などユーザーのローカルタイムゾーン基準でないことで、次のようなややこしいことが発生し得ます。

  1. 日本時間の3月1日 午前9時(UTC 3月1日 午前0時)に無料トライアルを開始
    • 無料トライアルの終了日(課金開始日)は4月1日
  2. 日本時間の3月1日 午前8時(UTC 2月28日 午後11時)に無料トライアルを開始
    • 無料トライアルの終了日(課金開始日)は3月28日で、4月1日ではない!

日本時間では同じ3月1日に無料トライアルを開始しても、たった1時間の違いで無料トライアルの期間が3日間も短くなってしまいます。

ということで、開発者としてユーザーからの問い合わせへの返答にも、ユーザーとして無料トライアルを最大限活用するのにも使える豆知識でした。

URLのパスの分割方法による結果の違い

let url = URL(string: "https://example.com/foo/bar")!
url.path // "/foo/bar"
url.path.split(separator: "/") // ["foo", "bar"]
url.path.split(separator: "/", omittingEmptySubsequences: false) // ["", "foo", "bar"]
url.pathComponents // ["/", "foo", "bar"]

ドキュメントはこちら:

2021年2月末でRxJava 2.xがEnd-of-Life (EoL)を迎えた

github.com

⚠️ This is the last planned update for the 2.x version line. After February 28, 2021, 2.x becomes End-of-Life (EoL); no further patches, bugfixes, enhancements, documentation or support will be provided by the project.

これまでAndroidアプリ開発での非同期処理の定番として使われていたRxJavaですが、その2系バージョンがEoLを迎えていました。Android JetpackでもKotlin Coroutinesを使ったAPIが増えてきているなど、最近はCoroutinesを使うことが増えてきているので、ひとまず最新の3.xに移行しつつ、Coroutinesに置き換えていきたいところですね。

Reactコンポーネントの定義にFCではなくVFCを使う

簡単に言うと

  • React.FCのpropsの型定義には暗黙的にchildrenが含まれてしまう
  • 今後@types/reactの18以降ではReact.FCにchildrenは含まれなくなる予定(破壊的変更)
    • これを先取りして@types/react16.9.48からReact.VFC/React.VoidFunctionComponentが追加されており、こちらではchildrenが含まれない

ということで

  • @types/react 18の時代を先取りし、基本的には全てをReact.VFCで定義して、childrenが必要なコンポーネントではpropsに明示的にchildrenを定義しましょう
  • React.FCからchildrenが消えた暁にはReact.VFCは非推奨になるはずで、その時はVFCFCに一括置換すれば問題ないはず

ということを会社のチーム内で話した。