ハイライト
Foundation は
Localeを参照型から値型へ変更し、#Predicateマクロによって述語クエリのプロパティ名と型チェックをコンパイル時に行えるようになりました。
主要内容
今回の Foundation アップデートのメインテーマは、Objective-C 時代から残っていた設計上の負債を一つひとつ返済していくことです。Locale を参照型から値型へ変更したというのは、一見すると内部実装の細部に過ぎないように思えますが、これによってマルチスレッド由来のバグを一群まるごと潰すことができます。たとえば、ユーザーがシステム言語を切り替えた瞬間に 2 つのスレッドが異なる locale を読み取り、フォーマット結果が一致しなくなる、といった類のバグです。この種の問題は再現が極めて難しく、これまではロックを掛けるか、あるいは祈るくらいしか回避策がありませんでした。今回の変更で Locale.current は呼び出し時点のスナップショットを返すようになり、一度手元に取得した値が後から書き換わる心配はありません。
JSONEncoder および JSONDecoder の内部実装も書き直され、パフォーマンスが向上しています。大量の API レスポンスを処理するアプリにとっては、何もしなくても得られる嬉しい改善でしょう。もう一つ実用的な変更として OutputFormatting.withoutEscapingSlashes が追加され、/ のエスケープ挙動を制御できるようになりました。バックエンドによっては \/ の扱いが長らく地雷になっていたため、これは地味ながら助かるアップデートです。
#Predicate マクロは今回の目玉です。NSPredicate では "age >= 18" のような文字列でクエリを書くため、タイポは実行時にしか判明しませんでした。一方 #Predicate を使えば、Swift コンパイラがビルド時にプロパティ名と型をチェックしてくれます。SortDescriptor のチェーン可能な API と組み合わせることで、Core Data クエリもようやくエンドツーエンドで型安全に書けるようになりました。
詳細
#Predicate は文字列ベースのクエリを Swift クロージャに変換し、型チェックをコンパイラに任せます。典型的な使い方は次のとおりです。
struct Person {
var name: String
var age: Int
}
let adults = #Predicate<Person> { person in
person.age >= 18
}
let sortByName = SortDescriptor(\Person.name)
ポイント:
#Predicate<Person>は述語をPerson型に紐付け、クロージャ引数personのフィールドアクセスがコンパイラによって検査されます。person.age >= 18はそのまま Swift の式なので、プロパティ名を間違えれば Xcode が即座にエラーを出します。実行時に文字列をパースする際の落とし穴も避けられます。SortDescriptor(\Person.name)は KeyPath でソートフィールドを指定するため、従来の文字列キーによるタイポを防げます。- この述語は SwiftData や Core Data の
FetchDescriptorに渡せるため、エンドツーエンドで型安全になります。
JSONEncoder の出力制御は outputFormatting オプションを組み合わせて指定できます。
let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted, .withoutEscapingSlashes]
ポイント:
.prettyPrintedを指定すると出力にインデントが付き、デバッグしやすくなります。.withoutEscapingSlashesを指定すると/のエスケープが無効化され、http:\/\/example.comではなくhttp://example.comがそのまま出力されます。- 両者は
OptionSet構文で組み合わせ可能で、互いに干渉しません。
重要ポイント
-
やること: プロジェクト内の
Locale.current呼び出しを一通り棚卸しする。- 価値: マルチスレッド環境下での locale ドリフトによるフォーマット崩れは再現が極めて困難で、本番障害の温床になりがちです。
- 始め方: Xcode の Find Navigator で
Locale.currentをプロジェクト全体検索し、特に GCD キューやasyncコンテキスト内での使用箇所を重点的にチェックしましょう。
-
やること: 新規コードの
NSPredicate文字列を#Predicateマクロに移行する。- 価値: コンパイル時チェックによってタイポや型不一致の大半を未然に防ぎ、本番クラッシュを減らせます。
- 始め方: まずは新しく追加するクエリロジックから着手し、関連モジュールに手を入れるタイミングで既存コードを少しずつ移行していくのがおすすめです。
-
やること: デプロイメントターゲットを上げた後、JSON パースのパフォーマンスベースラインを取り直す。
- 価値:
JSONEncoder/JSONDecoderの内部書き直しはコード変更なしで恩恵が得られる、いわばタダ乗りの最適化です。 - 始め方: XCTest の
measureブロックで大規模 JSON ペイロードのベンチマークを取り、アップデート前後の所要時間を比較しましょう。
- 価値:
関連セッション
- What’s new in Swift — Swift 言語側のアップデートまとめ。Foundation と合わせて読むと理解が深まります。
- Embracing Swift concurrency — 値型
Localeを並行処理シーンで活用する際のベストプラクティスの延長線。 - SwiftData: Dive into inheritance and schema migration —
#Predicateを SwiftData クエリで使う際の本格的なユースケース。 - Improve memory usage and performance with Swift — Swift のパフォーマンスチューニング視点から、Foundation の改善の背後にあるトレードオフを理解できます。
コメント
GitHub Issues · utterances