この記事の要点
- Swift 5.0 で ABI 安定性(ABI Stability) が達成され、Swift ランタイムと標準ライブラリは macOS / iOS / tvOS / watchOS の OS の一部 として同梱されるようになりました。これにより、アプリは Swift ランタイムを同梱する必要がなくなりダウンロードサイズが小さくなる、OS と深く統合され起動・実行が速くメモリ使用も減る、将来は OS のフレームワーク自体を Swift で提供できる、といった利点が得られます。
- 一方で、Swift ランタイムが「開発者のツールチェインの一部」から「ユーザーの OS の一部」に変わった結果、新しいランタイムや標準ライブラリの機能を使うには、それを含む新しい OS バージョンを要求する必要が生じうる ようになりました。これは Objective-C やシステムフレームワークで以前から存在したトレードオフが、Swift にも当てはまるようになったということです。
- 既存のアプリや言語互換モード(
-swift-version 4.2など)への影響はありません。Swift 4 のコードを Swift 5 モードへ移行しなくても安定 ABI は使えますし、ランタイム新機能を使わなければ新しい言語モードへの移行が OS 要件の引き上げを伴うこともありません。
背景: ランタイムが OS の一部になったことの意味
ABI 安定性は Swift 誕生以来の目標であり、Swift 5.0 で Apple プラットフォーム向けに達成されました。これにより Swift ランタイムと標準ライブラリは OS に同梱され、アプリ側でバンドルする必要がなくなります。ABI 安定性そのものの意味と、モジュール安定性・library evolution との関係は、同時期の「ABI 安定性とその先へ」で整理されています。
この記事が扱うのは、その 結果として生じる開発上のトレードオフ です。ランタイムが OS の一部になったことで、新しい Swift の機能を採用することと、古い OS バージョンとの互換性を保つことの間に、これまで Objective-C で存在したのと同じ緊張関係が生まれました。
どんな機能が新しい OS バージョンを要求しうるのか
新しい Swift ランタイムや標準ライブラリのサポートを必要とする機能は、OS の利用可能性(availability)による制限の対象になりえます。具体的には次のようなものです。
- 標準ライブラリへの追加。新しい型、プロトコル、プロトコル適合、関数、メソッド、プロパティなど。
- 型システムの変更。新種の型、既存の型への新しい修飾(関数型の属性など)、新しいブリッジング・サブタイピング・動的キャストの関係など。
Core Team は今後、レビューに入る各 Proposal について後方互換性への影響を検討していくとされています。
既存アプリと言語互換モードへの影響
記事は当時の典型的な疑問に Q&A 形式で答えています。要点は次のとおりです。
言語互換モードは ABI に影響しない
言語の互換性設定(-swift-version 4 / 4.2 など)は 純粋にコンパイル時の機能 で、ソース互換性を制御するものです。ABI には影響しません。安定 ABI を使うために Swift 4 のコードを Swift 5 モードへ移行する必要はありませんし、今後もランタイム新機能を使わない限り、新しい言語モードの採用が新しい OS 要件を課すことはありません。
既存の Swift アプリの再コンパイルは不要
ランタイムを同梱した既存の Swift バイナリは、macOS 10.14.4 / iOS 12.2 / tvOS 12.2 / watchOS 5.2 とそれ以降の OS でも、同梱したランタイムを使って動き続けます。これらの古いランタイムは安定 ABI とは互換性がないため、OS 側のランタイムとは互いに無関係に振る舞います(互いに相手の Swift クラスを素の Objective-C クラスとして見ます)。ただし、同梱ランタイムを使うアプリは App Store の app thinning の恩恵を受けられません。
古い OS へのデプロイ
Swift 5 でビルドしても、アプリの最小デプロイターゲットを引き上げる必要はありません。古い OS へデプロイするアプリには Swift ランタイムのコピーが埋め込まれますが、ランタイムを同梱する OS 上ではそのコピーは無視され(事実上不活性になり)ます。
新しいランタイムを自分で同梱できないのはなぜか
「OS を待たずに新しいランタイム機能を使うため、新しい Swift ランタイムを自分のアプリにバンドルする」ことは、いくつかの理由でできません。
- 共存の仕組みの制約。安定前のランタイムとの互換性を保つ仕組みは、1 プロセス内でアクティブな Swift ランタイムが 2 つまでで、かつ安定前ランタイムを使う Swift コードがアプリ内で完結していることを前提にしています。同じ仕組みで新しいランタイムを OS のランタイムと並べると、新しいランタイムは OS 内の Swift ライブラリや、OS ランタイムにリンクされた ABI 安定なサードパーティ Swift ライブラリにアクセスできません。
- セキュリティ。OS のランタイムをバンドル版で丸ごと置き換えると、OS バージョンのランタイムを使う前提でコード署名されているシステムライブラリのセキュリティを回避してしまいます。
- テストの複雑化。OS のランタイムを置き換え可能にすると、OS・ランタイム・サードパーティライブラリ・アプリのすべてが互いにテストすべき構成の組み合わせが増えます。いわゆる「DLL hell」のような状況は、テストや出荷を難しく高価にします。
- 統合と性能。OS 内にあることで、ランタイムは Objective-C ランタイムや Foundation など他の OS コンポーネントと密に統合され、dyld 共有キャッシュに組み込まれてメモリと読み込み時間のオーバーヘッドを最小化できます。OS の外でビルドしたランタイムでこの挙動を完全に再現するのは、いずれ不可能になるか、安定 API に制約される分だけ大きな性能コストを伴いかねません。
新機能を古い OS へバックデプロイできるか
一部のランタイム機能については、たとえばアプリに「shim」ランタイムライブラリを埋め込むといった手法でバックデプロイできる可能性があります。ただし常に可能とは限りません。バックデプロイの成否は、古い OS に出荷済みのバイナリ成果物が持つ制約や既存のバグによって根本的に制限されます。Core Team は今後、レビュー中の各 Proposal についてバックデプロイへの影響を個別に検討していくとされています。
まとめ
Swift 5 の ABI 安定性は、ダウンロードサイズの削減・OS との統合・将来の Swift 製フレームワークといった大きな利点をもたらしました。その代償として、Swift ランタイムが OS の一部になったため、ランタイムや標準ライブラリの新機能を使うには新しい OS を要求しうる、というトレードオフが生まれます。これは Objective-C で長く存在してきたものと同じ性質のもので、既存アプリや言語互換モードには影響しません。今後 Proposal をレビューする際には、後方互換性やバックデプロイへの影響が個別に考慮されていきます。
関連リンク
- ABI 安定性とその先へ — ABI 安定性・モジュール安定性・library evolution の関係を整理した同時期の解説