### Setup Metal-Backed Live View Controller Source: https://context7.com/cascable/cascablecore-swift/llms.txt Initializes and configures a `CameraLiveViewController` for Metal rendering. It handles adding the view as a child, setting up constraints, and adding an overlay using the provided layout guide. ```swift import CascableCoreSwift import CascableCore import UIKit import Combine class LiveViewViewController: UIViewController { private var liveViewController: CameraLiveViewController! private var liveViewSubscription: AnyCancellable? private var camera: Camera? override func viewDidLoad() { super.viewDidLoad() setupLiveViewRenderer() } private func setupLiveViewRenderer() { // Create Metal-backed live view controller liveViewController = CameraLiveViewController() // Add as child view controller addChild(liveViewController) view.addSubview(liveViewController.view) liveViewController.view.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ liveViewController.view.topAnchor.constraint(equalTo: view.topAnchor), liveViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor), liveViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), liveViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor) ]) liveViewController.didMove(toParent: self) // Add overlay using the image layout guide let overlayView = UIView() overlayView.backgroundColor = UIColor.red.withAlphaComponent(0.3) overlayView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(overlayView) let guide = liveViewController.liveViewImageLayoutGuide NSLayoutConstraint.activate([ overlayView.topAnchor.constraint(equalTo: guide.topAnchor), overlayView.bottomAnchor.constraint(equalTo: guide.bottomAnchor), overlayView.leadingAnchor.constraint(equalTo: guide.leadingAnchor), overlayView.trailingAnchor.constraint(equalTo: guide.trailingAnchor) ]) } func startLiveView(camera: Camera) { self.camera = camera // Configure orientation behavior liveViewController.orientation = .syncWithCamera // Or lock to specific orientation: // liveViewController.orientation = .overridden(.landscape) // Start streaming with Metal optimization liveViewSubscription = camera.liveViewPublisher(options: [.skipImageDecoding: true]) .receive(on: DispatchQueue.main) .sinkWithReadyHandler { print("Live view completed: \(completion)") self?.liveViewController.clearDisplay() } receiveValue: { self?.liveViewController.render(frame: frame) { // Frame rendered, UI can sync with orientation/size print("Rendered frame: \(size) at \(orientation)") readyForNextFrame() } } } func pauseForAnimation() { // Pause rendering during UI animations liveViewController.setPauseRendering(true) UIView.animate(withDuration: 0.3, animations: { // Perform UI animation }) { self.liveViewController.setPauseRendering(false) } } func stopLiveView() { liveViewSubscription?.cancel() liveViewSubscription = nil liveViewController.clearDisplay() } } ``` -------------------------------- ### Start Live View with Demand Management Source: https://context7.com/cascable/cascablecore-swift/llms.txt Initiates a live camera view stream using Combine, managing frame delivery rate with `sinkWithReadyHandler`. Includes essential options like skipping image decoding for performance. ```swift import CascableCoreSwift import CascableCore import Combine class LiveViewStreamManager { private var liveViewSubscription: AnyCancellable? func startLiveView(camera: Camera) { // Configure live view options let options: [LiveViewOption: Any] = [ .skipImageDecoding: true, // Essential for performance .favorHighFrameRate: false, .maximumFramesPerSecond: FrameRate(fps: 30.0) ] // Use sinkWithReadyHandler for proper demand management liveViewSubscription = camera.liveViewPublisher(options: options) .receive(on: DispatchQueue.main) .sinkWithReadyHandler { switch completion { case .finished: print("Live view ended normally") case .failure(let reason): switch reason { case .endedNormally: print("Live view ended") case .alreadyStreaming: print("Live view already streaming") case .failed(let error): print("Live view failed: \(error.localizedDescription)") } } } receiveValue: { // Process the frame print("Frame size: \(frame.naturalImageSize)") print("Orientation: \(frame.orientation)") // Signal ready for next frame after processing readyForNextFrame() } } // Asynchronous frame processing example func startLiveViewAsync(camera: Camera) { liveViewSubscription = camera.liveViewPublisher(options: [.skipImageDecoding: true]) .receive(on: DispatchQueue.global(qos: .default)) .sinkWithReadyHandler { print("Live view ended: \(completion)") } receiveValue: { // Process frame on background queue self?.processFrame(frame) // Return to main queue for UI updates, then request next frame DispatchQueue.main.async { self?.updateUI(with: frame) readyForNextFrame() } } } private func processFrame(_ frame: LiveViewFrame) { // Access raw image data for custom processing if let rawData = frame.rawImageData { // Process JPEG data } } private func updateUI(with frame: LiveViewFrame) { // Update UI elements } func stopLiveView() { liveViewSubscription?.cancel() liveViewSubscription = nil } } ``` -------------------------------- ### Discover Cameras via Shared Instance and Delegate Source: https://context7.com/cascable/cascablecore-swift/llms.txt Accesses the shared CameraDiscovery instance to start discovering cameras over the network and USB. It also sets up notifications for WiFi connectivity changes and implements a delegate to handle discovered cameras and initiate connections. ```swift import CascableCoreSwift import CascableCore // Access shared discovery instance let discovery = CameraDiscovery.shared // Start camera discovery discovery.startDiscovery(in: .networkAndUSBCameras) // Listen for WiFi connectivity changes NotificationCenter.default.addObserver( forName: CameraDiscovery.wifiConnectivityDidChangeNotification, object: nil, queue: .main ) { notification in print("WiFi connectivity changed - check for new cameras") } // Handle discovered cameras via delegate class CameraHandler: NSObject, CameraDiscoveryDelegate { func discovery(_ discovery: CameraDiscovery, didDiscover camera: Camera) { print("Discovered camera: \(camera.friendlyDisplayName ?? \"Unknown\")") // Connect to the camera camera.connect { if let error = error { print("Connection failed: \(error.localizedDescription)") } else { print("Connected successfully!") } } } } ``` -------------------------------- ### Configure Live View Options Source: https://context7.com/cascable/cascablecore-swift/llms.txt Demonstrates setting various `LiveViewOption` parameters to control live streaming behavior, including skipping image decoding, favoring frame rate, and limiting maximum frames per second. ```swift import CascableCoreSwift import CascableCore func configureLiveView(camera: Camera) { // Skip CPU-intensive image decoding when using Metal rendering let performanceOptions: [LiveViewOption: Any] = [ .skipImageDecoding: true ] // Favor higher frame rates over image quality let highFPSOptions: [LiveViewOption: Any] = [ .skipImageDecoding: true, .favorHighFrameRate: true ] // Limit maximum frame rate let limitedFPSOptions: [LiveViewOption: Any] = [ .skipImageDecoding: true, .maximumFramesPerSecond: 24.0 // Numeric value ] // Use FrameRate struct for frame rate control let frameRateOptions: [LiveViewOption: Any] = [ .skipImageDecoding: true, .maximumFramesPerSecond: FrameRate(fps: 15.0) ] // Unlimited frame rate let unlimitedOptions: [LiveViewOption: Any] = [ .skipImageDecoding: true, .maximumFramesPerSecond: FrameRate.unlimited ] // Apply options to existing publisher camera.applyLiveViewOptions(highFPSOptions) // Convert to CascableCore-compatible dictionary let cascableCoreOptions = performanceOptions.asCascableCoreLiveViewOptions } ``` -------------------------------- ### Activate License Key Asynchronously Source: https://github.com/cascable/cascablecore-swift/blob/main/README.md Use the async API to verify license keys and handle offline activation tokens. Store updated tokens to ensure future offline access. ```swift // Make sure we use a previously-granted offline activation token. let existingToken: Data? = UserDefaults.standard.data(forKey: "CascableCoreOfflineToken") do { let validatedToken = try await CascableCoreLicenseVerification.apply("1234", offlineToken: existingToken) if validatedToken.wasRefreshed { // Make sure you store updated offline activation tokens! UserDefaults.standard.set(validatedToken.tokenData, forKey: "CascableCoreOfflineToken") } // The SDK is now activated and can be used. searchForCameras() } catch { print("WARNING: License key activation failed: \(error.localizedDescription)") } ``` -------------------------------- ### Accessing and Observing Camera Properties in Swift Source: https://github.com/cascable/cascablecore-swift/blob/main/README.md Demonstrates how to access strongly-typed camera properties and set up observers for value and valid settable value changes. Ensure the observer token is stored to prevent deallocation. ```swift let isoProperty = camera.property(for: .iso) // Gives you a `TypedCameraProperty` print("ISO is: \(isoProperty.currentValue.localizedDisplayValue)") // Because properties are strongly-typed, we can do nice logic. if isoProperty?.commonValue == .iso100 { print("ISO 100!") } // Important: The observation will invalidate when this token is // deallocated — it should be stored somewhere! let observerToken = property.addObserver { property, changeType in if changeType.contains(.value) { print("ISO changed to: \(property.currentValue.localizedDisplayValue)!") } if changeType.contains(.validSettableValues) { print("Valid ISOs changed to: \(property.validSettableValues.compactMap({ $0.localizedDisplayValue }))!") } } ``` -------------------------------- ### Creating Combine Publishers for Camera Properties Source: https://github.com/cascable/cascablecore-swift/blob/main/README.md Shows how to create Combine publishers for camera property values, settable values, and general camera state. These publishers can be used to react to changes in camera settings and status. ```swift camera.publisher(for: .shutterSpeed).sink { shutterSpeedProperty in // Fires whenever the current value or valid settable values change. } camera.valuePublisher(for: .shutterSpeed).sink { shutterSpeed in // Fires whenever the current value changes. print("The current shutter speed is: \(shutterSpeed?.localizedDisplayValue ?? "nil")") } camera.settableValuesPublisher(for: .shutterSpeed).sink { shutterSpeeds in // Fires whenever the valid settable values change. print("Valid shutter speeds are: \(shutterSpeeds.compactMap({ $0.localizedDisplayValue }))") } ``` ```swift camera.videoRecordingStatePublisher().sink { recordingState in switch recordingState { case .notRecording: label.text = "Not Recording" case .recording(let timer?): label.text = "Recording: \(timer.asMinutesAndSeconds)" case .recording(_): label.text = "Recording" } } ``` ```swift camera.valuePublisher(for: .shutterSpeed) .combineLatest(camera.valuePublisher(for: .aperture)) .combineLatest(camera.valuePublisher(for: .iso)) .flatten() .sink { shutter, aperture, iso in print("Exposure triangle values: \(shutter), \(aperture), \(iso)") } ``` -------------------------------- ### Activate CascableCore License with Async/Await Source: https://context7.com/cascable/cascablecore-swift/llms.txt Applies a CascableCore license key using async/await, supporting offline token retrieval and storage for subsequent use without internet connectivity. Handles specific license verification errors. ```swift import CascableCoreSwift import CascableCore // Apply license key with offline token support func activateLicense() async { // Retrieve any previously-stored offline token let existingToken: Data? = UserDefaults.standard.data(forKey: "CascableCoreOfflineToken") do { let validatedToken = try await CascableCoreLicenseVerification.apply( "YOUR-LICENSE-KEY-HERE", offlineToken: existingToken, refreshMode: .automatic ) // Store refreshed tokens for offline use if validatedToken.wasRefreshed { UserDefaults.standard.set(validatedToken.tokenData, forKey: "CascableCoreOfflineToken") } // SDK is now activated - proceed with camera discovery print("License activated successfully") searchForCameras() } catch let error as LicenseKeyVerificationError { switch error { case .failedWithoutValidOfflineToken: print("License verification failed and no valid offline token available") case .notAllowedInCurrentEnvironment: print("License not valid in current environment (e.g., trial in App Store)") case .invalidLicense: print("License key is invalid for this bundle/platform") } } catch { print("Unexpected error: \(error.localizedDescription)") } } ``` -------------------------------- ### Accessing Camera Properties in Swift Source: https://context7.com/cascable/cascablecore-swift/llms.txt Demonstrates how to retrieve various camera properties using the property(for:) method and how to access category-specific identifiers. ```swift import CascableCoreSwift import CascableCore func demonstratePropertyIdentifiers(camera: Camera) { let props = camera.properties // Exposure Settings let aperture = props.property(for: .aperture) // ApertureValue let shutter = props.property(for: .shutterSpeed) // ShutterSpeedValue let iso = props.property(for: .iso) // ISOValue let ev = props.property(for: .exposureCompensation) // ExposureCompensationValue let lightMeter = props.property(for: .lightMeterReading) // ExposureCompensationValue let flashEV = props.property(for: .flashExposureCompensation) // ExposureCompensationValue // Video Exposure Settings let videoAperture = props.property(for: .videoAperture) let videoShutter = props.property(for: .videoShutterSpeed) let videoISO = props.property(for: .videoISO) let videoEV = props.property(for: .videoExposureCompensation) // Capture Settings let focusMode = props.property(for: .focusMode) // PropertyCommonValueFocusMode let driveMode = props.property(for: .driveMode) // PropertyCommonValueDriveMode let afSystem = props.property(for: .afSystem) // PropertyCommonValueAFSystem let mirrorLockup = props.property(for: .mirrorLockupEnabled) // Bool let mirrorStage = props.property(for: .mirrorLockupStage) // PropertyCommonValueMirrorLockupStage let digitalZoom = props.property(for: .digitalZoomEnabled) // Bool // Imaging Settings let whiteBalance = props.property(for: .whiteBalance) // PropertyCommonValueWhiteBalance let customWB = props.property(for: .customWhiteBalanceValue) // Int let aeMode = props.property(for: .autoExposureMode) // PropertyCommonValueAutoExposureMode let flashMode = props.property(for: .flashMode) // PropertyCommonValueFlashMode let colorTone = props.property(for: .colorTone) // NoCommonValues let artFilter = props.property(for: .artFilter) // NoCommonValues let meteringMode = props.property(for: .exposureMeteringMode) // NoCommonValues // Configuration Settings let bracketing = props.property(for: .inCameraBracketingEnabled) // Bool let noiseReduction = props.property(for: .noiseReduction) // NoCommonValues let imageQuality = props.property(for: .imageQuality) // NoCommonValues let destination = props.property(for: .imageDestination) // PropertyCommonValueImageDestination // Information (Read-only) let battery = props.property(for: .batteryPowerLevel) // PropertyCommonValueBatteryLevel let power = props.property(for: .powerSource) // PropertyCommonValuePowerSource let shots = props.property(for: .shotsAvailable) // Int let lensAttached = props.property(for: .lensStatus) // Bool let meterStatus = props.property(for: .lightMeterStatus) // PropertyCommonValueLightMeterStatus let dofPreview = props.property(for: .dofPreviewEnabled) // Bool let ready = props.property(for: .readyForCapture) // Bool let flashAvail = props.property(for: .flashAvailable) // Bool // Video Format let videoFormat = props.property(for: .videoRecordingFormat) // VideoFormatValue if let format = videoFormat.currentValue?.commonValue { print("Frame rate: \(format.frameRate ?? 0) fps") print("Frame size: \(format.frameSize ?? .zero)") print("Compression: \(format.compressionLevel?.rawValue ?? 0)") } // Live View Zoom let lvZoom = props.property(for: .liveViewZoomLevel) // LiveViewZoomLevelValue if let zoom = lvZoom.currentValue?.commonValue { print("Zoomed in: \(zoom.zoomedIn)") print("Zoom factor: \(zoom.zoomFactor ?? 1.0)x") } // Property categories let exposureProps = PropertyCategory.exposureSetting.propertyIdentifiers let captureProps = PropertyCategory.captureSetting.propertyIdentifiers let imagingProps = PropertyCategory.imagingSetting.propertyIdentifiers let configProps = PropertyCategory.configurationSetting.propertyIdentifiers let infoProps = PropertyCategory.information.propertyIdentifiers } ``` -------------------------------- ### Manage Live View Frames with sinkWithReadyHandler Source: https://github.com/cascable/cascablecore-swift/blob/main/README.md Demonstrates how to use sinkWithReadyHandler to control frame processing flow, either synchronously or asynchronously, to ensure efficient resource usage. ```swift // In this example, we're processing the frame synchronously in the subscription closure. camera.liveViewPublisher(options: [.skipImageDecoding: true]) .receive(on: DispatchQueue.global(qos: .default)) .sinkWithReadyHandler { completion in print("Live view ended with completion reason: \(completion)" ) } receiveValue: { frame, readyForNextFrame in let result = processFrameSynchronously(frame) readyForNextFrame() } // In this example, we're rendering the frame asynchronously and informing the subscription // when we're done and ready for another frame. camera.liveViewPublisher(options: [.skipImageDecoding: true]) .receive(on: DispatchQueue.global(qos: .default)) .sinkWithReadyHandler { completion in print("Live view ended with completion reason: \(completion)" ) } receiveValue: { frame, readyForNextFrame in let result = processFrameSynchronously(frame) DispatchQueue.main.async { // Rendering an image to screen still takes some time. self.renderProcessedFrameOnScreen(result) readyForNextFrame() } } ``` -------------------------------- ### Manually Discover Cameras Source: https://context7.com/cascable/cascablecore-swift/llms.txt Use manual discovery to connect to cameras via suggested gateways, specific network interfaces, or direct IP addresses when automatic discovery is insufficient. ```swift import CascableCoreSwift import CascableCore let manualDiscovery = CameraDiscovery.shared.manualDiscovery // Discover Canon camera at suggested gateway (camera's own WiFi network) manualDiscovery.discover(.cameraAtSuggestedGateway(.canon)) { result in switch result { case .success(let camera): print("Found Canon camera: \(camera.friendlyDisplayName ?? "Unknown")") // Keep a strong reference to the camera! case .failure(let error): print("Discovery failed: \(error.localizedDescription)") } } // Discover Nikon camera at specific network interface manualDiscovery.discover(.cameraType(.nikon, atGatewayOfInterface: "en0")) { result in switch result { case .success(let camera): print("Found Nikon camera on en0") case .failure(let error): print("Discovery failed: \(error)") } } // Discover Sony camera at specific IP address let ipAddress: IPv4Address = "192.168.1.100" manualDiscovery.discover(.cameraType(.sony, at: ipAddress)) { result in switch result { case .success(let camera): print("Found Sony camera at \(ipAddress.stringValue)") case .failure(let error): print("Discovery failed: \(error)") } } // Available camera types let cameraTypes: [CameraType] = [ .canon, // Canon EOS, EOS M, EOS R, PowerShot .nikon, // Nikon D series, Z series .sony, // Sony Alpha, RX .fuji, // Fujifilm X-series .olympus, // Olympus OM-D, PEN, TG .panasonic, // Panasonic LUMIX .generic // USB-connected cameras ] ``` -------------------------------- ### Monitor Camera State Publishers with Combine Source: https://context7.com/cascable/cascablecore-swift/llms.txt Subscribe to various camera state changes including focus, autoexposure, supported functionality, and command categories using Combine publishers. ```swift import CascableCoreSwift import CascableCore import Combine class CameraStateMonitor { private var cancellables = Set() func monitorCameraState(camera: Camera) { // Monitor focus information camera.focusInfoPublisher() .receive(on: DispatchQueue.main) .sink { focusInfo in if let info = focusInfo { print("Focus points: \(info.activeFocusPoints?.count ?? 0)") } } .store(in: &cancellables) // Monitor autoexposure results camera.autoexposureResultPublisher() .receive(on: DispatchQueue.main) .sink { aeResult in if let result = aeResult { print("AE Result available") } } .store(in: &cancellables) // Monitor supported functionality changes camera.supportedFunctionalityPublisher() .receive(on: DispatchQueue.main) .sink { functionality in if functionality.contains(.remoteControlWithoutLiveView) { print("Remote control supported") } if functionality.contains(.liveView) { print("Live view supported") } } .store(in: &cancellables) // Monitor available command categories camera.currentCommandCategoriesPublisher() .receive(on: DispatchQueue.main) .sink { categories in if categories.contains(.stillsShooting) { print("Still shooting commands available") } if categories.contains(.videoRecording) { print("Video recording commands available") } } .store(in: &cancellables) } } ``` -------------------------------- ### Perform Manual Camera Discovery Source: https://github.com/cascable/cascablecore-swift/blob/main/README.md Discover cameras manually using specific gateway or interface configurations. Ensure a strong reference to the resulting camera is maintained. ```swift let manualDiscovery = CameraDiscovery.shared.manualDiscovery manualDiscovery.discover(.cameraAtSuggestedGateway(.canon)) { result in switch result { case .success(let camera): // Use the camera. Make sure you keep a strong reference to it! case .failure(let error): print("Couldn't resolve camera with error: \(error)") } } manualDiscovery.discover(.cameraType(.canon, atGatewayOfInterface: "en0")) { result in switch result { case .success(let camera): // Use the camera. Make sure you keep a strong reference to it! case .failure(let error): print("Couldn't resolve camera with error: \(error)") } } ``` -------------------------------- ### Render Live View with Metal Source: https://github.com/cascable/cascablecore-swift/blob/main/README.md Connect a camera's live view feed to a Metal-backed renderer. Use .skipImageDecoding to avoid CPU-intensive decoding. ```swift // Make sure you pass .skipImageDecoding since CascableCore's internal // decoding is CPU-based and resource-intensive. camera.liveViewPublisher(options: [.skipImageDecoding: true]) .receive(on: DispatchQueue.main) .sinkWithReadyHandler { completion in print("Live view ended with completion reason: \(completion)" ) } receiveValue: { frame, readyForNextFrame in liveViewRendererView.render(frame: frame, completionHandler: { orientation, size in // Sync the rest of your UI with the orientation etc. readyForNextFrame() }) } ``` -------------------------------- ### Monitor Video Recording State with Combine Source: https://context7.com/cascable/cascablecore-swift/llms.txt Use videoRecordingStatePublisher to receive updates on the camera's video recording status. Handles both counting up and counting down timers. ```swift import CascableCoreSwift import CascableCore import Combine class VideoRecordingMonitor { private var cancellables = Set() func monitorVideoRecording(camera: Camera) { camera.videoRecordingStatePublisher() .receive(on: DispatchQueue.main) .sink { recordingState in switch recordingState { case .notRecording: print("Not recording") case .recording(let timer): if let timer = timer { switch timer { case .countingUp(let value): print("Recording: \(timer.asMinutesAndSeconds) elapsed") case .countingDown(let value): print("Recording: \(timer.asMinutesAndSeconds) remaining") } } else { print("Recording (no timer)") } } } .store(in: &cancellables) } // Direct property access (non-Combine) func checkRecordingState(camera: Camera) { let state = camera.videoRecordingState switch state { case .notRecording: print("Camera is not recording video") case .recording(let timer): if let timer = timer { print("Recording time: \(timer.asMinutesAndSeconds)") } } } } ``` -------------------------------- ### Observe Camera Property Changes with Callbacks Source: https://context7.com/cascable/cascablecore-swift/llms.txt Use this to observe changes to camera properties like ISO, shutter speed, and aperture. Ensure the observation token is retained to keep the observation active. The observer closure receives the property and a change type indicating what has changed. ```swift import CascableCoreSwift import CascableCore class CameraPropertyObserver { private var observerTokens: [CameraPropertyObservation] = [] func observeProperties(camera: Camera) { let isoProperty = camera.properties.property(for: .iso) // Add observer - store the token to keep observation active let token = isoProperty.addObserver { property, changeType in if changeType.contains(.value) { print("ISO changed to: \(property.currentValue?.localizedDisplayValue ?? \"nil\")") } if changeType.contains(.validSettableValues) { let values = property.validSettableValues.compactMap { $0.localizedDisplayValue } print("Valid ISO values changed: \(values)") } if changeType.contains(.pendingValue) { if let pending = property.pendingValue { print("Pending ISO value: \(pending.localizedDisplayValue ?? \"Unknown\")") } } } // Store token to maintain observation observerTokens.append(token) // Observe multiple properties let shutterToken = camera.properties.property(for: .shutterSpeed) .addObserver { property, changeType in guard changeType.contains(.value) else { return } print("Shutter speed: \(property.currentLocalizedDisplayValue ?? \"Unknown\")") } observerTokens.append(shutterToken) } func stopObserving() { // Invalidate all observers observerTokens.forEach { $0.invalidate() } observerTokens.removeAll() } } ``` -------------------------------- ### Subscribe to Camera Property Changes with Combine Source: https://context7.com/cascable/cascablecore-swift/llms.txt Subscribe to various camera property updates using Combine publishers. This includes full property updates, value changes, common values, settable values, and effective values. Ensure necessary imports are included. ```swift import CascableCoreSwift import CascableCore import Combine class CameraViewModel: ObservableObject { @Published var currentISO: String = "" @Published var currentShutter: String = "" @Published var currentAperture: String = "" private var cancellables = Set() func subscribeToProperties(camera: Camera) { // Subscribe to full property updates camera.properties.publisher(for: .iso) .sink { isoProperty in self.currentISO = isoProperty.currentLocalizedDisplayValue ?? "---" } .store(in: &cancellables) // Subscribe to value changes only camera.properties.valuePublisher(for: .shutterSpeed) .sink { shutterValue in self.currentShutter = shutterValue?.localizedDisplayValue ?? "---" } .store(in: &cancellables) // Subscribe to common values for logic camera.properties.commonValuePublisher(for: .aperture) .sink { apertureCommon in if let aperture = apertureCommon { print("Aperture common value: \(aperture)") } } .store(in: &cancellables) // Subscribe to valid settable values camera.properties.settableValuesPublisher(for: .iso) .sink { validISOs in let displayValues = validISOs.compactMap { $0.localizedDisplayValue } print("Available ISOs: \(displayValues)") } .store(in: &cancellables) // Use effective value (pending or current) camera.properties.effectiveValuePublisher(for: .exposureCompensation) .sink { evValue in print("Effective EV: \(evValue?.localizedDisplayValue ?? \"0\")") } .store(in: &cancellables) } } ``` -------------------------------- ### Set Camera Property Values Asynchronously with Async/Await Source: https://context7.com/cascable/cascablecore-swift/llms.txt Utilize modern Swift concurrency to set camera property values and wait for confirmation. This function includes built-in timeout handling for operations. It demonstrates setting specific values, incrementing/decrementing stepping properties, and waiting for value changes. ```swift import CascableCoreSwift import CascableCore func setPropertiesAsync(camera: Camera) async throws { let isoProperty = camera.properties.property(for: .iso) // Set value and wait for camera confirmation if let iso800 = isoProperty.validValue(matching: .iso800) { try await isoProperty.setValue(iso800, waitUntilReflectedByCamera: true, timeout: 1.0) print("ISO successfully set to 800") } // Increment/decrement stepping properties let apertureProperty = camera.properties.property(for: .aperture) if apertureProperty.valueSetType.contains(.stepping) { // Increment aperture (towards smaller f-numbers in UI terms) let newValue = try await apertureProperty.incrementValue(timeout: 1.0) print("Aperture incremented to: \(newValue?.localizedDisplayValue ?? \"Unknown\")") // Decrement aperture let decremented = try await apertureProperty.decrementValue(timeout: 1.0) print("Aperture decremented to: \(decremented?.localizedDisplayValue ?? \"Unknown\")") } // Wait for specific value let evProperty = camera.properties.property(for: .exposureCompensation) if let zeroEV = evProperty.validZeroValue { try await evProperty.waitForValueToChange(to: zeroEV, timeout: 10.0) print("EV is now at zero") } // Wait for any value change let newShutter = try await camera.properties.property(for: .shutterSpeed) .waitForValueToChangeFromCurrent(timeout: 10.0) print("Shutter speed changed to: \(newShutter?.localizedDisplayValue ?? \"Unknown\")") } ``` -------------------------------- ### Access Camera Properties with Type Safety Source: https://context7.com/cascable/cascablecore-swift/llms.txt Utilize the strongly-typed property API to read and modify camera settings with compile-time checks, preventing runtime type errors. ```swift import CascableCoreSwift import CascableCore func workWithCameraProperties(camera: Camera) { // Get strongly-typed ISO property let isoProperty = camera.properties.property(for: .iso) // Read current value with proper types if let currentISO = isoProperty.currentValue { print("Current ISO: \(currentISO.localizedDisplayValue ?? "Unknown")") print("String value: \(currentISO.stringValue)") } // Check common value with type safety if isoProperty.currentCommonValue == .iso100 { print("Camera is set to ISO 100") } // List all valid settable values let validValues = isoProperty.validSettableValues print("Available ISO values: \(validValues.compactMap { $0.localizedDisplayValue })") // Find and set a specific value if let iso400 = isoProperty.validValue(matching: .iso400) { isoProperty.setValue(iso400) { error in if let error = error { print("Failed to set ISO: \(error.localizedDescription)") } else { print("ISO set to 400") } } } // Get auto ISO value if available if let autoISO = isoProperty.validAutomaticValue { print("Auto ISO available: \(autoISO.localizedDisplayValue ?? "Auto")") } // Work with other property types let apertureProperty = camera.properties.property(for: .aperture) let shutterProperty = camera.properties.property(for: .shutterSpeed) let evProperty = camera.properties.property(for: .exposureCompensation) // Get zero EV value for exposure compensation if let zeroEV = evProperty.validZeroValue { print("Zero EV: \(zeroEV.localizedDisplayValue ?? "0")") } } ``` -------------------------------- ### Simplify combineLatest with .flatten() Source: https://context7.com/cascable/cascablecore-swift/llms.txt Use the .flatten() operator to simplify nested tuple output from combineLatest chains when monitoring multiple camera properties. This operator supports up to 8 combined publishers. ```swift import CascableCoreSwift import CascableCore import Combine class ExposureTriangleMonitor { private var cancellables = Set() func monitorExposureTriangle(camera: Camera) { // Combine multiple property publishers and flatten the nested tuples camera.properties.valuePublisher(for: .shutterSpeed) .combineLatest(camera.properties.valuePublisher(for: .aperture)) .combineLatest(camera.properties.valuePublisher(for: .iso)) .flatten() // Converts ((A, B), C) to (A, B, C) .sink { shutter, aperture, iso in let shutterStr = shutter?.localizedDisplayValue ?? "---" let apertureStr = aperture?.localizedDisplayValue ?? "---" let isoStr = iso?.localizedDisplayValue ?? "---" print("Exposure: \(shutterStr) | \(apertureStr) | ISO \(isoStr)") } .store(in: &cancellables) // Flatten supports up to 8 combined publishers camera.properties.valuePublisher(for: .shutterSpeed) .combineLatest(camera.properties.valuePublisher(for: .aperture)) .combineLatest(camera.properties.valuePublisher(for: .iso)) .combineLatest(camera.properties.valuePublisher(for: .exposureCompensation)) .combineLatest(camera.properties.valuePublisher(for: .whiteBalance)) .flatten() // Converts ((((A, B), C), D), E) to (A, B, C, D, E) .sink { shutter, aperture, iso, ev, wb in print("Full settings: \(shutter), \(aperture), \(iso), \(ev), \(wb)") } .store(in: &cancellables) } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.