本文へ移動

ホーム / App / 第2部 プラットフォームの基礎

第2部 プラットフォームの基礎・第2章

iOS(Swift・SwiftUI)

★★★ 必須11トピック・面接 5問

iOS アプリの土台になる仕組みを押さえる。

ひとことで

起動・前面・背面・停止の状態遷移。背面に回ると処理が制限され、メモリ不足で終了させられることもある。

iOS のアプリは、画面に出ているか、裏に回ったかで OS から状態を切り替えられます。今のアプリはシーン(ウィンドウ1つ分。iPad では複数持てる)ごとに状態を持ちます。SwiftUI では @Environment(\.scenePhase) で .active・.inactive・.background を受け取れます。

前面・裏・停止の3つの段階を行き来する未起動非アクティブアクティブ操作できるバックグラウンドサスペンドコードは動かない終了起動前面へ着信・切り替えホームへ戻る数秒後メモリ不足バックグラウンドに入った時点で保存する。サスペンド後に終了されても、アプリには知らされない
iOS アプリの状態遷移(サスペンド中の終了は通知されない)
状態どんなときやること
アクティブ前面で操作できる通常の処理。画面の更新を再開
非アクティブ着信・通知センターを開いた・切り替え中動画やゲームを一時停止。重い処理は始めない
バックグラウンドホームに戻った直後編集中の内容を保存。タイマーや通信を止める。画面の機密情報を隠す
サスペンド裏で止まっているコードは動かない。ここから通知なしに終了されることがある

大事なのは、サスペンド中に終了されても通知は来ないことです。だから「終了するときに保存」ではなく、バックグラウンドに入った時点で保存しておきます。次に開いたときは最初からの起動(コールドスタート)になるので、どの画面だったかを復元できるようにします(SwiftUI なら @SceneStorage)。

面接ではこう答える

iOS のアプリは、未起動・非アクティブ・アクティブ・バックグラウンド・サスペンドの状態を移り、今はシーンごとに状態を持ちます。着信などでは一時的に非アクティブになり、ホームに戻るとバックグラウンドを経て数秒でサスペンドされます。サスペンド中はメモリが足りなくなると通知なしに終了されるので、終了時ではなくバックグラウンドに入った時点で保存し、次の起動で元の画面を復元できるようにします。

ひとことで

iOS の裏の処理は目的ごとの仕組み(短時間の延長・バックグラウンド転送・BGTaskScheduler・Background Modes)でだけ許され、実行の時刻は OS が決める。

iOS のアプリは、裏に回ると数秒でサスペンドされます。裏で処理を続けたいときは、目的ごとに用意された仕組みを使います。自由に動き続ける方法はないので、「何を・どれくらい確実に」したいかで仕組みを選びます。

裏でやりたいことと、使う仕組み保存・送信の仕上げ(数十秒まで)beginBackgroundTask大きなファイルの転送バックグラウンドの URLSession短い定期更新BGAppRefreshTask充電中などの長めの処理BGProcessingTask音楽・位置情報・通話Background Modes(用途を申告)色の付いたものは実行の時刻を OS が電池や使われ方を見て決める。決まった時刻に動く保証はない
やりたいことごとに仕組みが決まっていて、いつ動くかは多くの場合 OS が決める
  • できる:beginBackgroundTask で短い時間だけ処理を続ける(保存や送信の仕上げ)。URLSession のバックグラウンド設定でのダウンロード・アップロード(アプリが止まっても OS が続けてくれる)。BGTaskScheduler での定期的な更新(BGAppRefreshTask)や、充電中などに長めの処理(BGProcessingTask)。音楽の再生・位置情報・通話など、Background Modes で許可された用途。サイレントプッシュでの起動。
  • できない:好きなときに好きなだけ動き続けること。BGTaskScheduler の実行時刻は OS が電池や使われ方を見て決めるので、「毎時0分に必ず動く」は作れません。サイレントプッシュも回数を絞られ、届く保証はありません。利用者がアプリ切り替え画面から上にスワイプして終了させると、次に開くまで裏の処理はほぼ動きません。

落とし穴は、時間内に endBackgroundTask を呼ばないと OS にアプリを終了させられることと、目的外の Background Modes を付けると審査で落ちることです。定期的な同期は「開いたときに取りにいく」を基本にし、裏の更新はおまけと考えると安定します。

面接ではこう答える

iOS ではアプリが裏に回ると数秒でサスペンドされるので、裏の処理は目的ごとの仕組みを使います。保存の仕上げは beginBackgroundTask で短く延長し、大きな転送はバックグラウンドの URLSession に任せ、定期的な更新は BGTaskScheduler に登録します。ただし BGTaskScheduler の実行時刻は OS が電池や使われ方を見て決めるので、決まった時刻に動く保証はありません。なので同期は「開いたときに取りにいく」を基本にし、裏の更新は表示を速くするおまけと考えて設計します。

2.3SwiftUI と UIKit

用語★★★ 必須
ひとことで

SwiftUI は状態から画面を宣言的に作り、UIKit は命令的に画面を組み立てる。

SwiftUI は「状態がこうなら画面はこう」と宣言して書く UI の仕組みで、状態が変わると必要な部分だけが描き直されます。UIKit は昔からある仕組みで、画面の部品(View)を作って、状態が変わるたびに自分で書き換えます。新しい画面は SwiftUI で書くのが今の主流ですが、UIKit の資産や細かい制御が必要な場面はまだ残っています。

観点SwiftUIUIKit
書き方宣言的。状態から画面を作る命令的。部品を作って自分で更新する
コード量少ない。プレビューですぐ確認できる多い。Auto Layout の制約を書く
細かい制御苦手な場面がある(複雑なスクロールや文字入力など)ほぼ何でもできる。情報も多い
対応 OS新しい API ほど新しい iOS が必要古い iOS でも同じように動く
状態管理@State・@Binding・@Observable(iOS 17 以降)自分で通知・更新する(Combine・デリゲートなど)

判断の目安は、アプリが対応する最低の iOS の版です。SwiftUI の便利な機能(たとえば NavigationStack は iOS 16、@Observable は iOS 17 から)は新しい OS でしか使えないので、最低の版を上げられるかで書き方が変わります。

面接ではこう答える

SwiftUI は「状態がこうなら画面はこう」と宣言して書く仕組みで、状態が変わると必要な部分だけが描き直されます。UIKit は部品を作って状態が変わるたびに自分で書き換える命令的な仕組みで、細かい制御が得意で古い iOS でも同じように動きます。新しい画面は SwiftUI で書くのが今の主流ですが、使える機能はアプリが対応する最低の iOS の版で決まるので、最低の版と、細かい制御が必要かどうかで使い分けます。

ひとことで

@State は持ち主、@Binding は借りて書き換える、@Observable はクラスの変化を細かく追う仕組み。

SwiftUI では、画面は状態から作られるので、状態を誰が持つかをはっきりさせることが一番大事です。印(プロパティラッパー)は、持ち主か、借りているだけかを表します。

印意味使いどころ
@Stateその View が持ち主の状態。View が作り直されても値は残る開閉・入力中の文字など、その画面の中だけの状態。@Observable のオブジェクトを持つときにも使う
@Binding親の状態を借りて読み書きするトグルや入力欄など、親の値を書き換える部品
@Observable(iOS 17〜)クラスに付けると、読まれたプロパティの変化だけを追うViewModel。読んでいない値が変わっても描き直さない
@Bindable@Observable のプロパティから Binding を作るViewModel の値を入力欄につなぐ
@Environment上の View から下へまとめて渡す値ログイン中の利用者・共通の設定など
ObservableObject・@StateObject・@PublishediOS 16 以前からある方式。オブジェクトの変化を丸ごと通知する古い iOS に対応するアプリ
状態の持ち主は1つ。子には参照を渡す親の View@State var isOn(持ち主)子の View@Binding var isOn(借りる)値と書き換える権利ViewModel@Observable class画面 A@State で持つ画面 B引数・@Environment読んだプロパティが変わったView だけが描き直される
状態には必ず持ち主が1つある。子には @Binding か、同じオブジェクトを渡す

面接ではこう答える

SwiftUI では状態の持ち主を1つに決め、その印で役割を表します。@State は View が持ち主の値で、子に書き換えを任せるときは @Binding で渡します。ViewModel のような共有の状態は、iOS 17 以降なら @Observable を付けたクラスにし、読まれたプロパティが変わったときだけ描き直されるので効率がよくなります。古い iOS に対応するなら ObservableObject と @StateObject を使い、どちらの場合も同じ値を2か所に持たないようにします。

SwiftUI の画面遷移は、iOS 16 からの NavigationStack が基本です。積み重なった画面を path(配列)として状態で持てるので、コードから好きな画面へ移ったり、リンクから途中の画面を再現したりしやすくなりました。

path(配列)= 積み重なった画面path = [商品(12), レビュー(3)]append → 次の画面へremoveLast → 1つ戻るremoveAll → 最初の画面へ一覧(ルート)商品 12レビュー 3リンクや通知から開くときも、path を組み立てて入れるだけで途中の画面まで再現できる
path の配列がそのまま画面の積み重ね。配列を書き換えれば画面が移る
@State private var path: [Route] = []
NavigationStack(path: $path) {
  ListView()
    .navigationDestination(for: Route.self) { route in
      switch route { case .item(let id): ItemView(id: id) }
    }
}
遷移の種類使うもの向いている場面
積み重ねる(戻るで戻る)NavigationStack・NavigationLink一覧 → 詳細
下から出す.sheet・.fullScreenCover作成・編集のように、終わったら閉じる画面
左右に並べるNavigationSplitViewiPad・Mac の2〜3列の画面
タブTabViewアプリの大きな区分

path には画面そのものではなく、どの画面かを表す小さな値(ID など)を入れます。こうすると状態の保存や復元、テストがしやすくなります。古い NavigationView は非推奨なので、新しい画面では使いません。

2.6UIKit と SwiftUI を混ぜる

用語★★☆ 重要
ひとことで

UIHostingController で SwiftUI を UIKit に載せ、UIViewRepresentable で UIKit の部品を SwiftUI に包む。

既存のアプリを一度に SwiftUI へ書き直すことはまずありません。新しい画面から SwiftUI にし、地図やカメラなど UIKit にしかない部品は包んで使う、という混ぜ方が普通です。

お互いに包んで載せられるUIKit の画面既存の画面UIHostingController で載せるSwiftUI の画面新しい画面SwiftUI の画面UIViewRepresentable で包むUIKit の部品地図・カメラ・独自の部品どちらも同じ ViewModel を表示するだけにする。状態を2か所に持つと、どちらが正しいか分からなくなる
UIKit と SwiftUI はお互いに包んで混ぜられる。状態は1か所に置く

混ぜて使うとき

  • UIKit の中に SwiftUI:UIHostingController(rootView:) で SwiftUI の画面を UIKit の画面として載せます。既存アプリで新しい画面から SwiftUI に移すときの定番です。
  • SwiftUI の中に UIKit:UIViewRepresentable・UIViewControllerRepresentable で包みます。地図・カメラ・既存の独自部品などで使います。UIKit 側のデリゲートは Coordinator で受けます。

落とし穴は、両方で同じ状態を持ってしまい、どちらが正しいか分からなくなることです。状態は ViewModel などの1か所に置き、UIKit 側・SwiftUI 側はそれを表示するだけにします。また SwiftUI では、body の中で重い計算や通信を始めないこと(何度も呼ばれる)、一覧では安定した id を付けることが性能の基本です。

面接ではこう答える

UIKit のアプリに SwiftUI の画面を載せるときは UIHostingController を使い、逆に SwiftUI の中で地図や既存の部品など UIKit のものを使うときは UIViewRepresentable や UIViewControllerRepresentable で包みます。UIKit 側のデリゲートは Coordinator で受けます。混ぜるときに一番気をつけるのは状態を2か所に持たないことで、ViewModel に1つだけ置いて、両方の側はそれを表示するだけにします。

2.7Swift の並行処理

用語★★☆ 重要
ひとことで

async/await と actor で、データ競合を型の仕組みで防ぐ。UI の更新はメインスレッドで。

Swift の並行処理は、async・await で「待つ処理」を素直に書ける仕組みです。通信の結果を待つ間もアプリは固まらず、終わったら続きが実行されます。Task { } で処理を始め、actor で「同時に1つしか触れないデータ」を作れます。

言葉意味
async/await待つ処理を書く印。await のところで一度手放し、終わったら続きから
Task { }並行処理を始める。画面が消えたら .task 修飾子の Task は自動でキャンセルされる
actor中のデータに同時に1つの処理しか触れない型
@MainActorメインスレッドで動くことを保証する印
Sendableスレッドをまたいで安全に渡せる型の印
await の間はメインスレッドを手放すメインスレッド(MainActor)別のスレッドload() 開始画面の操作に応える(固まらない)user を更新通信・JSON の解析await再開画面に触る処理は MainActor、重い処理は外へ。Swift 6 ではこの区別をコンパイラーが確かめる
await で待つ間、メインスレッドは手放され、画面は操作に応え続ける

MainActor

画面の更新は必ずメインスレッドで行う決まりがあります。@MainActor を付けたクラスや関数は、メインスレッドで動くことをコンパイラーが保証します。ViewModel には @MainActor を付け、重い処理(画像の加工・大きな JSON の解析)だけを別の場所で動かすのが基本の形です。

@MainActor
final class ProfileViewModel: ObservableObject {
  @Published var user: User?
  func load() async {
    user = try? await api.fetchUser()  // 待つ間も画面は固まらない
  }
}

面接ではこう答える

Swift の並行処理は async と await で待つ処理を順番どおりに書ける仕組みで、await のところで一度スレッドを手放すので、待っている間も画面は固まりません。画面の更新はメインスレッドで行う決まりがあり、@MainActor を付けるとコンパイラーがそれを保証します。ViewModel は MainActor にして、画像の加工や大きな JSON の解析のような重い処理だけを別の場所で動かすのが基本の形です。

ひとことで

Swift 6 の言語モードは、データ競合の可能性をコンパイル時のエラーにし、Sendable と actor で安全を保証させる。

データ競合は、複数のスレッドが同じデータを同時に書き換えて値が壊れる不具合です。たまにしか起きず再現も難しいので、Swift 6 では型の仕組みでコンパイル時に見つけるようにしました。

データ競合と、actor での防ぎ方ふつうのクラス処理 A:count += 1処理 B:count += 1count同時に書くと 1 増えるだけactor処理 A処理 B(待つ)count1つずつ順番に触るSwift 6 の言語モードでは、競合しうる書き方をコンパイル時のエラーとして教えてくれる
同時に書き換えると値が壊れる。actor は触る順番を1つずつに並べる

Swift 6 の言語モードでは、データ競合(複数のスレッドが同じデータを同時に書き換える不具合)の可能性をコンパイル時のエラーとして出します。スレッドをまたいで渡す値は Sendable(安全に渡せる型)である必要があり、メインスレッド専用のものを別のスレッドから触るとエラーになります。最初は大量のエラーが出るので、Swift 5 モードのまま警告として有効にし、モジュールごとに少しずつ直すのが現実的です。Swift 6.2 からは、モジュール全体を既定で MainActor にする設定(Default Actor Isolation)も選べ、Xcode 26 で新しく作ったアプリのプロジェクトでは最初からこの設定になっています。アプリのコードでは注釈の手間がかなり減ります。

よくある失敗は、エラーを消すために @unchecked Sendable や nonisolated(unsafe) を安易に付けることです。これはチェックを止めるだけで、競合はなくなりません。

面接ではこう答える

Swift 6 の言語モードでは、データ競合の可能性がコンパイル時のエラーになります。スレッドをまたいで渡す値は Sendable である必要があり、メインスレッド専用のものを別の場所から触るとエラーになります。既存のアプリでは、まず Swift 5 モードのまま厳しいチェックを警告として有効にし、モジュールごとに直してから言語モードを上げます。@unchecked Sendable などでエラーを消すのはチェックを止めるだけなので、理由を書いて最小限にとどめます。

Swift の並行処理は構造化されていて、Task の中で始めた子の処理は親に結びつきます。親が終わるかキャンセルされると、子にもキャンセルが伝わるので、画面を閉じた後に通信が残り続けることを防げます。

書き方使いどころ
.task { }(SwiftUI)画面が出たときに始め、消えたら自動でキャンセル。画面の読み込みは基本これ
async let決まった数の処理を同時に始めて、あとでまとめて待つ
withTaskGroup数が決まっていない処理(画像を10枚など)を同時に行う
Task { }ボタンを押したときなど、同期のコードから始める。自分で変数に持ってキャンセルする
Task.detached親と切り離す。キャンセルも優先度も引き継がないので、ほぼ使わない

キャンセルは止めてほしいという合図で、処理が勝手に止まるわけではありません。長いループでは try Task.checkCancellation() や Task.isCancelled で確かめます。URLSession の async の関数はキャンセルされると CancellationError などで終わるので、それをエラーとして画面に出さないようにします。検索欄の入力ごとに前の検索を取り消す、といった場面でよく使います。

親が取り消されると、子にも伝わる.task { } の Task画面が消えると取り消しasync let 利用者async let おすすめTaskGroup 画像×10続きは動かないキャンセルは「お願い」。止まるのは await のところと、自分で確かめたところだけ
親の Task がキャンセルされると、子の処理にもキャンセルが伝わる
ひとことで

証明書・App ID・端末をまとめたプロビジョニングプロファイルで署名して、初めて iOS のアプリは端末で動く。

iOS のアプリは、Apple が発行した証明書で署名しないと実機でも動きません。「ビルドはできるのに実機に入らない」「CI でだけ失敗する」の多くは署名の問題です。

署名に必要なものは、プロファイルにまとまっている証明書誰が作ったか(秘密鍵とセット)App IDバンドル ID と使う機能端末の一覧開発・Ad Hoc のときだけプロビジョニングプロファイル署名済みのアプリ
証明書・App ID・端末をまとめたのがプロファイル。どれか1つでも合わないと署名できない
もの役割よくあるつまずき
証明書(Development・Distribution)誰が作ったアプリかを示す。秘密鍵とセットで使う秘密鍵が別の Mac にしかない。期限切れ
App ID・バンドル IDアプリを見分ける ID と、使う機能(プッシュ通知・Associated Domains など)機能を Xcode で足したのにプロファイルが古い
プロビジョニングプロファイル証明書・App ID・(開発用は)端末をまとめた許可証端末を登録していない。配信用と開発用の取り違え
Entitlementsアプリが使う機能の申告App ID の設定と合っていない

1人の開発では Xcode の自動署名で十分ですが、チームや CI では、証明書を1か所で管理して配る仕組み(fastlane の match など)や、App Store Connect API のキーでの自動化を使います。証明書や鍵は CI のシークレットに置き、リポジトリには入れません。

面接ではこう答える

iOS のアプリは、Apple が発行した証明書で署名しないと端末で動きません。プロビジョニングプロファイルは、証明書、バンドル ID と使う機能を表す App ID、開発用なら端末の一覧をまとめた許可証で、どれか1つでも合わないと署名に失敗します。チームでは証明書を1か所で管理して配り、CI では App Store Connect API のキーなどで自動化して、人の Mac に頼らないようにします。

iOS は毎年6月の WWDC で新しい版が発表され、秋に公開されます。利用者の更新が速いので、公開の日から多くの人が新しい OS でアプリを使います。

毎年くり返す OS の更新の流れ6月WWDC・ベータ公開夏ベータで動作確認9月ごろ新しい iOS の公開翌春ごろ新しい SDK が必須に公開の日に古いアプリが崩れないよう、夏のうちにベータで確かめておく
毎年6月に新しい OS が発表され、秋に公開される。それに合わせて準備する
時期やること
6月(発表・ベータ)新しい API と、挙動が変わる点(権限・裏の処理・見た目)を確かめる
夏(ベータの期間)ベータ版の OS で今のアプリを動かし、崩れ・クラッシュを直して先に出しておく
秋(公開)新機能(ウィジェット・新しい UI など)を出す。クラッシュの増加を見る
翌年の春ごろストアに出すアプリは新しい Xcode・SDK でのビルドが必須になるのが毎年の流れ。CI の Xcode を上げる

対応する最低の iOS の版は、利用者の分布を見て、毎年1つずつ上げるチームが多いです。最低の版を上げると、新しい SwiftUI の機能が使えて分岐のコードが減ります。目安は「最新とその1〜2つ前まで」ですが、自分のアプリの利用者の数字で決めます。

この章のまとめ

🖨 まとめシート(印刷・PDF)
  • アプリとビューのライフサイクル
  • SwiftUI と UIKit の違い、Swift の並行処理(async/await・actor)
  • アプリのライフサイクル:起動・前面・背面・停止の状態遷移。背面に回ると処理が制限され、メモリ不足で終了させられることもある。
  • バックグラウンドでできること:iOS の裏の処理は目的ごとの仕組み(短時間の延長・バックグラウンド転送・BGTaskScheduler・Background Modes)でだけ許され、実行の時刻は OS が決める。
  • SwiftUI と UIKit:SwiftUI は状態から画面を宣言的に作り、UIKit は命令的に画面を組み立てる。
  • SwiftUI の状態(@State・@Binding・@Observable):@State は持ち主、@Binding は借りて書き換える、@Observable はクラスの変化を細かく追う仕組み。
  • UIKit と SwiftUI を混ぜる:UIHostingController で SwiftUI を UIKit に載せ、UIViewRepresentable で UIKit の部品を SwiftUI に包む。
  • Swift の並行処理:async/await と actor で、データ競合を型の仕組みで防ぐ。UI の更新はメインスレッドで。
  • Swift 6 とデータ競合のチェック:Swift 6 の言語モードは、データ競合の可能性をコンパイル時のエラーにし、Sendable と actor で安全を保証させる。
  • 署名とプロビジョニング:証明書・App ID・端末をまとめたプロビジョニングプロファイルで署名して、初めて iOS のアプリは端末で動く。

この章のクイズ

面接ではこう聞かれる

まず答えを見ずに声に出して答え、そのあと開いて比べる。

Q1iOS アプリのライフサイクルを説明してください。

模範回答

iOS のアプリは、未起動、非アクティブ、アクティブ、バックグラウンド、サスペンドの状態を移ります。今はシーンごとに状態を持ち、SwiftUI なら scenePhase で受け取れます。アクティブは操作できる状態で、着信や通知センターを開くと一時的に非アクティブになるので、動画やゲームはここで止めます。

ホームに戻るとバックグラウンドになり、少し経つとサスペンドされてコードは動かなくなります。サスペンド中はメモリが足りないと通知なしに終了されるので、バックグラウンドに入った時点で編集中の内容を保存し、通信やタイマーを止め、必要なら画面の機密情報を隠します。続きの処理が必要なら beginBackgroundTask で短い時間だけ延長し、大きな転送はバックグラウンドの URLSession、定期的な更新は BGTaskScheduler に任せますが、実行のタイミングは OS が決めるので保証はありません。次に開いたときに元の画面を復元できるようにしておくのも大事です。

深掘りされたら

  • サスペンド中に終了されたことをどう扱いますか?
  • BGTaskScheduler で定期的な同期をするときの注意点は?

見られているポイント状態と、それぞれでやるべきこと

Q2SwiftUI と UIKit をどう使い分けますか?

模範回答

新しい画面は基本的に SwiftUI で書き、UIKit は必要なところだけ使います。SwiftUI は状態から画面を宣言するのでコードが短く、プレビューで確かめやすいからです。一方で、複雑なスクロールや文字入力の細かい制御、既存の UIKit の部品の資産があるところは UIKit のほうが早いことがあります。

判断には、アプリが対応する最低の iOS の版も効きます。SwiftUI の便利な API は新しい OS でしか使えないものが多いからです。混ぜるときは UIHostingController や UIViewRepresentable でつなぎ、状態は ViewModel の1か所に置いて、両方が同じものを表示するようにします。

深掘りされたら

  • SwiftUI で困ったことと、どう回避しましたか?
  • UIKit の画面を SwiftUI に移すときの進め方は?

見られているポイント最低の対応 OS・状態の置き場所まで考えた判断

Q3@State・@Binding・@Observable の違いを説明してください。

模範回答

@State はその View が持つ小さな状態で、変わると View が描き直されます。@Binding は親の状態への参照で、子が値を読み書きしても実体は親にあります。@Observable は iOS 17 からの Observation の仕組みで、クラスに付けると、その View の body で実際に読んだプロパティが変わったときだけ描き直されます。

以前の ObservableObject と @Published は、オブジェクトのどれかが変わると、購読している View が描き直されやすいという違いがあります。@Observable のクラスを View が持つときは @State で持ち、子にバインディングを渡したいときは @Bindable を使います。

深掘りされたら

  • ObservableObject から @Observable に移すときの注意は?
  • @State に参照型を入れるとどうなりますか?

見られているポイント状態の持ち主と、描き直しの範囲の理解

Q4既存のアプリを Swift 6 の言語モードに移行するとき、どう進めますか?

模範回答

一度に切り替えず、警告から少しずつ直します。まず Swift 5 のモードのまま、並行性のチェックを完全(complete)にして警告を出し、依存の少ないモジュールから直していきます。画面まわりの型には @MainActor を付け、スレッドをまたいで渡す値は Sendable な値型にし、共有して書き換えるデータは actor に入れます。

外部のライブラリが対応していない場合は @preconcurrency で取り込みを和らげます。警告が消えたモジュールから言語モードを 6 に上げます。@unchecked Sendable で黙らせるのは、本当に安全だと説明できる場合だけにします。

深掘りされたら

  • MainActor を既定にする設定は使いますか?
  • 移行で見つかった本物のデータ競合の例は?

見られているポイント段階的に進める計画と、警告を黙らせない姿勢

Q5iOS の新しいメジャーバージョンに対応した経験を教えてください。

模範回答

例えば、毎年6月にベータ版が出たら、すぐに今のアプリをベータの OS で動かして、落ちる・見た目が崩れる箇所を洗い出します。次に新しい Xcode と SDK でビルドし、非推奨の API や、新しい SDK でビルドしたときだけ変わる挙動を確かめます。

直す必要があるものは秋の正式リリースの前に修正版を出しておき、新しい機能への対応は優先度を付けて後から入れます。正式リリースの直後は、新しい OS の利用者のクラッシュ率を版ごとに見て、ほかの OS より悪くなっていないかを確かめました。

深掘りされたら

  • 最低の対応 iOS をいつ上げますか?
  • ベータで見つけた不具合を Apple に報告したことはありますか?

見られているポイント前もって準備する進め方