WWDC Quick Look 💓 By SwiftGGTeam
Xcode essentials

Xcode essentials

元の動画を見る

ハイライト

Xcode チームは「編集・デバッグ・テスト・コミット」の日常開発サイクルから出発し、ナビゲーターフィルタ、Find Navigator の高度な検索、ブレークポイント条件と自動継続、Swift Testing タグフィルタ、Code Coverage 可視化、TestFlight 配布など、ワークフロー全体の効率向上テクニックを体系的に整理しました。


主要内容

プロジェクトが大きくなりチームが増えると、「変更するコードを見つける」こと自体が問題の半分になります。100〜200 ファイルの中からシンボルを検索したり、大量の検索結果を手動でスクロールしたり、クラッシュを 1 回再現するためにデバッグ中にアプリを何度も再起動したり — このセッションの目標は、こうしたステップにかかる時間を短縮することです。

前半はデザイナーの Cheech が編集とナビゲーションを担当。核心は、Xcode の各ナビゲーター下部にフィルタバーがあること、Find Navigator は検索範囲の保存とシンボルインデックスモードをサポートすること、Tab には永久型と暗黙型の 2 種類があること、Jump Bar と Open Quickly(Command-Shift-O)が最速のファイルジャンプ手段であること。これらの機能は UI 各所に散在していますが、組み合わせると「コードを探す」時間を大幅に削減できます。

後半は PM の Myke がデバッグ、テスト、配布を担当。デバッグではブレークポイントの高度な使い方が重点 — 条件付きブレークポイント、自動継続のログブレークポイント、Swift Error Breakpoint、緑のプログラムカウンタをドラッグして実行を巻き戻す操作。テストでは Swift Testing のタグフィルタ、scheme 間で再利用する Test Plans、行レベルの Code Coverage 可視化。配布では Archive フロー、TestFlight 内部/外部テスト、Xcode Organizer のフィードバックとクラッシュデータをカバーします。


詳細

ナビゲーションと検索

Project Navigator 下部のフィルタバーは、ファイル名検索、target 名フィルタ、git status フィルタ(変更済みファイルのみ表示)をサポート。

Find Navigator(Command-Shift-F)の高度な使い方:

  • 下部フィルタバーで検索結果を二次フィルタ — ファイル名やマッチ行の追加キーワードで絞り込み
  • 検索結果のファイル名横の展開矢印を Command-クリックで、同レベルのファイルをすべて折りたたみ(すべてのアウトラインビューで有効)
  • 不要な結果を選択して Delete キーで現在のクエリから非表示(コードは削除されない)
  • 検索範囲(Scope)を特定ディレクトリに限定可能、よく使う範囲は保存可能
  • 検索ボックス上の「Text」をクリックしてシンボルインデックスモードに切り替え、「Descendant Types」でクラス階層を表示(05:10

テキスト選択後に Command-E — テキストが検索ボックスに入り、クリップボードは上書きされません。macOS 全体で有効(05:27)。

ファイル間移動と編集

Xcode の Tab には 2 種類:永久 Tab(積極的に編集するファイル)と暗黙 Tab(閲覧のみ — 斜体タイトル、離れると消える)。暗黙 Tab をダブルクリックするか右クリックで「Keep Open」を選ぶと永久 Tab に変換。Option-クリックで閉じるボタンを押すと現在以外のすべての Tab を閉じる(06:32)。

Open Quickly(Command-Shift-O)は Xcode 最速のジャンプ:ファイル名やシンボルの部分キーワードを入力してジャンプ、クエリにスラッシュを加えるとパスマッチ、コロンと行番号で指定行へ。Option を押しながら Return で新しい分割に開く(11:22)。

Quick Actions(Command-Shift-A)は自然言語で Xcode コマンドを検索 — ショートカットを覚えていない場面に便利。

Xcode 16 の Predictive Code Completion はコンテキストに基づいて行全体を予測。Tab で表示部分を受け入れ、Option-Tab で完全な予測を受け入れ。コメントと変数名が具体的ほど予測精度が向上(14:24)。

ブレークポイントの高度な使い方

高頻度でヒットするコードには、2 つのブレークポイントを組み合わせ:ボタンハンドラに 1 つ設定、ヒット後にビジー関数内のブレークポイントを手動で有効化してから継続 — 特定シナリオでのみ停止(17:39)。

Breakpoint Navigator で追加する Swift Error Breakpoint は、catch だけでなく Swift エラーの throw 箇所でアプリを停止(18:16):

let url = try! getVideoResourceFilePath()

キーポイント:

  • try! は強制アンラップ、失敗時はランタイムエラー
  • Swift Error Breakpoint はクラッシュではなく throw 発生時に即座に一時停止

条件付きブレークポイント:ブレークポイントをダブルクリックして編集、条件式を追加 — 条件が真のときのみ一時停止。print 式を追加して「Automatically continue」を設定すれば、再コンパイルなしで一時ログを取得(18:59):

cloudURL.scheme == "https"

キーポイント:

  • これはブール式 — URL の scheme が “https” のときのみブレークポイントが一時停止
  • 高トラフィックコードで特定条件のみ関心がある場面に適している

ブレークポイントにログ式を追加して自動継続を設定:

p "Username is \(cloudURL.user())"

キーポイント:

  • p は LLDB の print コマンド、式の値をコンソールに出力
  • 「Automatically continue」を有効にすれば、プログラムを一時停止せずに情報を記録

デバッガーのバックトレースと巻き戻し

デバッガーで p コマンドを使い、guard 文が早期 return した理由を逆追跡(19:44):

guard cloudURLs.allSatisfy({ $0. scheme == "https" }),
    session.configuration.networkServiceType == .video else {
    return
}

p cloudURLs.allSatisfy({ $0.scheme == "https" })p session.configuration.networkServiceType == .video を個別に実行して各条件の値を確認し、どれが false か特定。

緑のプログラムカウンタを後方にドラッグして以前の式を再実行。副作用は巻き戻らないが、デバッグセッションを完全に再起動するより速い(20:11)。

Command-Control-R「Run Without Building」はコンパイルをスキップして前回のビルドを実行 — コード未変更で繰り返しデバッグする場面に最適(21:02)。

Xcode 16 はエディター内で完全なバックトレース(Full Backtraces)を表示 — Debug Bar から有効化し、異なるファイルからのコールスタックを 1 つのエディタービューに集約(21:20)。

セッションは print と os_log の 2 つのデバッグログ方式を比較。print 出力はフィルタや出所の特定が困難:

var releaseDate: Date {
    print("🎬 Entering func \(#function) in \(#fileID)...")
    let currentDate = Date()
    let gregorianCal = Calendar(identifier: .gregorian)
    var components = DateComponents()
    components.year = releaseYear
    print("\(#fileID)@\(#line) \(#function): 📅 releaseYear is \(releaseYear)")
    if releaseYear == gregorianCal.component(.year, from: currentDate) {
        components.month = Int(releaseMonth)
        isNewRelease = true
        print("\(#fileID)@\(#line) \(#function): 🆕 this is a new release!")
    }
    if releaseYear < 2000 {
        isClassicMovie = true
        print("\(#fileID)@\(#line) \(#function): 🎻 this one is a classic!")
    }
    let calendar = Calendar(identifier: .gregorian)
    return calendar.date(from: components)!
}

os_log に切り替えると、各ログにレベル(debug/info/error)が付き、テキスト、レベル、ソースライブラリでフィルタ可能、矢印クリックでソース行に直接ジャンプ(22:09):

var releaseDate: Date {
    os_log(.debug, "🎬 Entering func \(#function) in \(#file)...")
    let currentDate = Date()
    let gregorianCal = Calendar(identifier: .gregorian)
    var components = DateComponents()
    components.year = releaseYear
    os_log(.info, "📅 releaseYear is \(releaseYear)")
    if releaseYear == gregorianCal.component(.year, from: currentDate) {
        components.month = Int(releaseMonth)
        isNewRelease = true
        os_log(.info, "🆕 this is a new release!")
    }
    if releaseYear < 2000 {
        isClassicMovie = true
        os_log(.info, "🎻 this one is a classic!")
    }
    let calendar = Calendar(identifier: .gregorian)
    return calendar.date(from: components)!
}

キーポイント:

  • os_log(.debug, ...)os_log(.info, ...) で各ログエントリにレベルを設定
  • コンソールはレベル、テキスト、ソースライブラリでフィルタ可能 — #fileID マクロを手動追加不要
  • ログエントリにジャンプ矢印 — クリックでソース行に移動

Swift Testing とタグフィルタ

Swift Testing は @Test でテスト関数をマークし、XCTAssert を #expect で代替。Tag でテストをグループ化・フィルタ(24:19):

@Test(.tags(.stars)) func testStarRating() async throws {
    for video in library.videos {
        #expect(video.info.starRating > 0)
        #expect(video.info.starRating <= 5)
    }
}

@Test(.tags(.library)) func testLibraryLoaded() async throws {
  #expect(library.videos.count > 1)
}

extension Tag {
  @Tag static var stars: Tag
  @Tag static var library: Tag
}

キーポイント:

  • .tags(.stars) でテストにタグ付け、Test Navigator でタグ別にフィルタ
  • #expect は Swift Testing のアサーションマクロ — XCTAssert より簡潔な構文
  • Tag 定義は extension Tag 内の static プロパティとして配置

コマンドラインから scheme、test plan、単一テストを指定して実行(26:35):

xcodebuild test -scheme DestinationVideo
xcodebuild test -scheme DestinationVideo -testPlan TestAllTheThings
xcodebuild test -scheme DestinationVideo -testPlan TestAllTheThings -only-testing "Destination VideoUITests/testABeach"

キーポイント:

  • 最初のコマンドは scheme 下のすべてのテストを実行
  • -testPlan でテストプランを指定
  • -only-testing で特定テストケースのみ実行 — git bisect でリグレッション特定に便利

Code Coverage

Editor メニューから Code Coverage を有効化してテスト実行後、エディター右側に各行の実行回数を表示。0 は未カバー — テストの抜けまたはデッドコードの可能性(29:03)。

Report Navigator(Command-9)はファイルレベルと関数レベルのカバレッジ概要を提供し、改善目標の特定に役立つ。

Test Report で失敗したテストをクリックすると、実行イベントと画面録画が並列表示され、タイムライン下部に失敗発生時点がマーク(29:57)。

配布と Organizer

Archive はコンパイル済み app のスナップショットで、release ビルドと debug symbols を含む。配布オプション:App Store Connect(TestFlight または App Store Connect へアップロード)、TestFlight Internal Only(App Review をスキップ、組織内部テスターのみ)、Release Testing / Enterprise / Debugging(登録デバイス向けビルド生成)(31:54)。

Xcode Organizer(Command-Option-Shift-O)は TestFlight フィードバック、起動時間、終了率などを集約 — 共有に同意したユーザーのデータ、プライバシー安全。Feedback タブはテスターの文字フィードバック、Launch と Termination タブはパフォーマンス問題とクラッシュの特定に役立つ(33:36)。


重要ポイント

  • やること:Find Navigator でよく使う検索範囲を保存。価値:大規模プロジェクトでは同じディレクトリを繰り返し検索する操作が一般的 — 保存した scope はワンクリックで切り替え。始め方:検索時に検索ボックス下の範囲メニューをクリック、「Custom Scopes…」を選び、ディレクトリを指定して保存。

  • やること:条件付きブレークポイント + 自動継続で一時 print を代替。価値:コード変更不要、再コンパイル不要 — ブレークポイントログが直接コンソールに出力、完了後はブレークポイントを削除。始め方:ブレークポイントをダブルクリック、p 式を追加、「Automatically continue after evaluating actions」にチェック。

  • やること:print デバッグログを os_log に移行。価値:os_log はレベルとソースライブラリでフィルタ、クリックでソース行にジャンプ — チーム協業時のコンソール出力が制御可能。始め方print("...")os_log(.debug, "...") に置き換え、手動の #fileID#function マクロを削除。

  • やること:Swift Testing テストに Tag ラベルを追加。価値:テスト数が増えると名前検索よりタグフィルタが効率的 — Test Navigator でモジュールのテストをワンクリックフィルタ。始め方@Test 属性に .tags(.yourTag) を追加、extension Tag で static タグを定義。

  • やること:Command-Control-R(Run Without Building)で繰り返しデバッグを高速化。価値:クラッシュデバッグではアプリ再起動が頻繁 — コード未変更なら再コンパイルをスキップしてビルド待ち時間を節約。始め方:次のデバッグサイクルで Command-R の代わりに Command-Control-R。


関連セッション

コメント

GitHub Issues · utterances