the CameraCaptureIntent protocol allows you to define an associated AppContext type (up to 4KB) that gets passed to your LockedCameraCaptureExtension when the intent is triggered from the Lock Screen control.
Below is the steps to pass data with this AppContext
- Define Your AppContext
Create a Codable struct that conforms to the requirements for use as an AppContext. The key is making all methods nonisolated so the type is usable as Sendable:
import AppIntents
/// The appContext contains user preferences for the camera capture
struct MyAppContext: Codable {
/// Control which camera to use: true for front camera, false for rear camera
var useFrontCamera: Bool
enum CodingKeys: String, CodingKey {
case useFrontCamera
}
// Make initializers and Codable methods nonisolated so the type is usable as a Sendable AppContext.
nonisolated init(useFrontCamera: Bool) {
self.useFrontCamera = useFrontCamera
}
nonisolated init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.useFrontCamera = try container.decode(Bool.self, forKey: .useFrontCamera)
}
nonisolated func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(useFrontCamera, forKey: .useFrontCamera)
}
}- Create Your CameraCaptureIntent Define your intent and associate it with your AppContext:
struct MyCamCaptureIntent: CameraCaptureIntent {
typealias AppContext = MyAppContext
static let title: LocalizedStringResource = "Camera"
static let description = IntentDescription("Capture photos with your app.")
func perform() async throws -> some IntentResult {
// Return empty result - the context is automatically passed to the extension
return .result()
}
}Important!!!! Make sure this file is included in all three targets:
- Main app
- Widget extension
- Capture extension
- Update Context from Your Main App In your main app's view controller, update the context when user preferences change:
import AppIntents
class ViewController: UIViewController {
private let frontBackSwitch: UISwitch = UISwitch()
@objc private func frontBackPreferenceChanged() {
updateIntentContext()
}
private func setupUI() {
frontBackSwitch.addTarget(self, action: #selector(frontBackPreferenceChanged), for: .valueChanged)
}
private func updateIntentContext() {
Task {
let newContext = MyAppContext(useFrontCamera: cameraSwitch.isOn)
try? await MyCamCaptureIntent.updateAppContext(newContext)
}
}
private func loadSettings() {
Task { @MainActor in
let context = try? await MyCamCaptureIntent.appContext
frontBackSwitch.isOn = context?.useFrontCamera ?? false
}
}
}Step 4: Read Context in Your Capture Extension In your capture extension's UI, access the context to configure the camera:
import SwiftUI
import UIKit
import UniformTypeIdentifiers
import LockedCameraCapture
import AppIntents
struct Viewfinder: UIViewControllerRepresentable {
let session: LockedCameraCaptureSession
func makeUIViewController(context: Context) -> UIImagePickerController {
let imagePicker = UIImagePickerController()
imagePicker.sourceType = .camera
// Read the AppContext to configure camera
Task { @MainActor in
if let appContext = try? await MyCamCaptureIntent.appContext {
imagePicker.cameraDevice = appContext.useFrontCamera ? .front : .rear
print("Camera device set to: \(appContext.useFrontCamera ? "front" : "rear")")
} else {
imagePicker.cameraDevice = .rear
}
}
return imagePicker
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
}
This approach provides a clean, type-safe way to pass user preferences from your main app to your Lock Screen camera capture extension!