WWDC Quick Look 💓 By SwiftGGTeam
Build a great Lock Screen camera capture experience

Build a great Lock Screen camera capture experience

元の動画を見る

ハイライト

サードパーティ撮影 App がロック画面から直接起動可能に。ユーザーはコントロールセンター、Action Button、ロック画面コントロールからビューファインダーに入り、デバイスをアンロックせずに撮影できます。

主要内容

ユーザーが写真を撮るためにスマートフォンを取り出すとき、期待するのは「押したら撮れる」こと。「アンロック → App を探す → ビューファインダーの読み込み待ち」ではありません。システムカメラ App は常にロック画面から直接使用できましたが、サードパーティ撮影 App にはこの能力がありませんでした——iOS 18 まで。

iOS 18 の LockedCameraCapture フレームワークがこのギャップを埋めます。新しい App Extension タイプ——Locked Camera Capture Extension——を提供し、撮影 UI をコントロールセンター、Action Button、ロック画面コントロールからアンロック不要で起動できます。操作パスは「アンロック → App を探す → 撮影」から「コントロールを押す → 直接撮影」に短縮。

ライフサイクル全体が「ロック画面セキュリティ」を中心に設計:拡張起動後は直ちにビューファインダーを表示しなければならず、さもなければシステムがプロセスを終了。撮影完了後、コンテンツは sessionContentURL ディレクトリまたは PhotoKit 経由で保存。ユーザーが深い操作(SNS への共有など)を必要とする場合、拡張は openApplication を呼びアンロックを要求し、メイン App へシームレスに遷移。拡張が dismissed されると session コンテンツはメイン App のコンテナへ自動移行し、拡張コンテナは消去。各レイヤーがロック画面状態でのデータ分離を保証。

詳細

Locked Camera Capture Extension のエントリ

拡張のエントリは LockedCameraCaptureExtension プロトコルに準拠する SwiftUI 構造体。body は LockedCameraCaptureUIScene を返す(11:21):

@main
struct ClownTownCaptureExtension: LockedCameraCaptureExtension {
    var body: some LockedCameraCaptureUIScene {
        LockedCameraCaptureUIScene { session in
            CaptureView(session: session)
        }
    }
}

キーポイント:

  • @main は拡張のエントリポイント
  • LockedCameraCaptureExtension は iOS 18 の新プロトコル。Xcode はビューファインダーとシャッターボタンを含むテンプレートを提供
  • LockedCameraCaptureUIScene の初期化クロージャは LockedCameraCaptureSession オブジェクトを受け取る。拡張とシステムの相互作用の核心

LockedCameraCaptureSession が提供する能力

Session オブジェクトは拡張実行中の中心ハブ。3 つの重要な能力を提供(12:41):

// 1. 永続化ディレクトリ:撮影データを保存する唯一の推奨場所
let contentURL = session.sessionContentURL

// 2. コンテンツ処理完了をマーク。システムがディレクトリを削除可能に
session.invalidateSessionContent()

// 3. メイン App の起動を要求(デバイスアンロックをトリガー)
session.openApplication(
    NSUserActivity(activityType: NSUserActivityTypeLockedCameraCapture)
)

キーポイント:

  • sessionContentURL は拡張コンテナ内のディレクトリを指す。拡張 dismissed 後に唯一メイン App コンテナへ移行されるコンテンツ
  • 拡張コンテナ内の他のデータはすべて消去——永続化が必要なデータを別の場所に保存しない(08:46
  • invalidateSessionContent() 呼び出し後、以前書き込んだコンテンツは削除されるが、ディレクトリ自体は引き続き書き込み可能(13:38
  • openApplication はユーザー能動的操作(ボタンタップなど)時のみ呼び出し。自動トリガー不可(13:51

CameraCaptureIntent と Control の連携

ユーザーが拡張を起動するコントロールが必要。WidgetKit の Control と新しい CameraCaptureIntent で実装(14:54):

struct ClownTownCaptureIntent: CameraCaptureIntent {
    static var title: LocalizedStringResource = "ClownTown Capture"

    // appContext は App と拡張間で設定を共有
    var clownTownContext: ClownTownContext?

    func perform() async throws -> some IntentResult {
        // context を読み取り、拡張の初期状態を設定
        return .result()
    }
}

キーポイント:

  • CameraCaptureIntent は iOS 18 の新 Intent タイプ。撮影拡張起動専用
  • Intent は 3 つの target に同時含める必要がある:Widget Extension、Capture Extension、メイン App(15:17
  • appContext は App と拡張間の状態共有チャネルだがサイズ制限あり。大きすぎると永続化不可(16:08
  • updateAppContext() で共有状態を更新。両側が最新値を読める(16:56

メイン App 側での撮影コンテンツ受信

メイン App は LockedCameraCaptureManager で拡張の撮影コンテンツを取得(20:24):

let manager = LockedCameraCaptureManager.shared

// 保留中の全 session ディレクトリを取得
let urls = manager.sessionContentURLs

// 新しい session コンテンツを非同期監視
for await update in manager.sessionContentUpdates {
    switch update {
    case .added(let url):
        // 新着撮影コンテンツを処理
        processContent(at: url)
        manager.invalidateContent(at: url)
    case .removed(let url):
        break
    }
}

キーポイント:

  • sessionContentUpdates は AsyncSequence。初期値に既存の全 session URL を含み、その後追加・削除を継続監視(20:53
  • 拡張から App への遷移時、最新 session ディレクトリは App 起動後に準備完了する場合がある。一度きりの読み取りより非同期シーケンスを推奨(21:19
  • invalidateContent(at:) 呼び出し後、ディレクトリコンテンツが削除され .removed 更新イベントがトリガー(21:27

ロック画面状態でのセキュリティ制限

拡張はロック画面状態で厳格に制限(05:34):

  • 直ちにカメラビューファインダーを表示しなければならず、さもなければシステムが拡張を終了
  • ハードウェアボタンイベント(音量キーでの撮影/録画)には AVCaptureEventInteraction を使用必須
  • ネットワークアクセス不可
  • App の共有 Group Container への読み書き不可
  • App の SharedPreferences へのアクセス不可
  • ロック画面下の PhotoKit は現在 session に書き込んだ写真と動画の読み取りのみ許可

重要ポイント

  • 何をするか:既存撮影 App にロック画面ショートカットを追加。なぜ価値があるか:ユーザーは撮影時に速度を求める。ロック画面直アクセスは操作摩擦を大幅に下げ、サードパーティ App にシステムカメラと同等の起動優先度を与える。始め方:Xcode で Locked Camera Capture Extension target を新規作成。既存 SwiftUI ビューファインダービューを再利用。Control と Intent を追加。

  • 何をするか:sessionContentURL で撮影データのオフライン安全保存を実現。なぜ価値があるか:拡張のネットワークとコンテナアクセスは禁止されるが、session ディレクトリは拡張 dismissed 後にメイン App へ自動移行。「先に撮影、後で同期」アーキテクチャが自然に形成——撮影時は完全オフライン、App 実行時にアップロード。始め方:撮影データを session.sessionContentURL に書き込み。メイン App の LockedCameraCaptureManager.sessionContentUpdates で非同期取得・処理。

  • 何をするか:appContext で拡張と App 間でユーザー設定を同期。なぜ価値があるか:拡張は共有コンテナと設定にアクセス不可だが、App で選択したフィルターや撮影モードはロック画面撮影シーンでも継続すべき。始め方CameraCaptureIntentappContext に重要設定を保存。拡張起動時に context から読み取り。拡張内で設定変更時は updateAppContext() で書き戻し。

  • 何をするか:「拡張撮影 → アンロック遷移 → 深度編集」の一貫ワークフローを設計。なぜ価値があるか:多くの撮影シーンは後続操作(フィルター、共有、編集)が必要。拡張はネットワークと完全リソースにアクセス不可。openApplication がシームレス遷移を提供。始め方:深度操作が必要なボタンで session.openApplication() を呼び出し。NSUserActivity の userInfo でコンテキストを渡し。メイン App の handleActivity で対応 UI 状態に復帰。

関連セッション

コメント

GitHub Issues · utterances