Error Handling
All errors from measureVitals() are thrown as CircadifyError values. Use Swift’s do/catch with pattern matching to handle specific cases.
Error Format
Section titled “Error Format”public enum CircadifyError: LocalizedError, Equatable { // Camera case cameraNotAvailable case cameraPermissionDenied case cameraInUse
// Configuration case invalidApiKey case missingApiKey
// Capture case captureFailed(String) case qualityTooLow case faceNotDetected
// Network case networkError(String) case uploadFailed(String) case apiError(String)
// Session case sessionExpired case sessionNotFound
// Processing case processingFailed(String) case timeout
// Usage case quotaExceeded case rateLimited(retryAfter: Int)
// General case cancelled case unknown(String)}Every case has a human-readable errorDescription and an isRetryable flag.
Handling Errors
Section titled “Handling Errors”do { let result = try await sdk.measureVitals() displayResults(result)} catch let error as CircadifyError { switch error { case .cameraPermissionDenied: showSettingsPrompt("Camera access is required. Enable it in Settings.")
case .cameraNotAvailable: showAlert("No camera found on this device.")
case .faceNotDetected: showAlert("No face detected. Make sure your face is visible and well-lit.")
case .qualityTooLow: showAlert("Scan quality was too low. Try again with better lighting.")
case .rateLimited(let retryAfter): showAlert("Too many requests. Try again in \(retryAfter) seconds.")
case .cancelled: break // User cancelled — no action needed
default: if error.isRetryable { showRetryPrompt(error.errorDescription ?? "Something went wrong.") } else { showAlert(error.errorDescription ?? "An error occurred.") } }} catch { showAlert("Unexpected error: \(error.localizedDescription)")}Error Reference
Section titled “Error Reference”Camera Errors
Section titled “Camera Errors”| Error | Description | Retryable |
|---|---|---|
cameraNotAvailable | No camera on this device | No |
cameraPermissionDenied | User denied camera access | No |
cameraInUse | Camera is in use by another app | No |
Configuration Errors
Section titled “Configuration Errors”| Error | Description | Retryable |
|---|---|---|
missingApiKey | No API key provided | No |
invalidApiKey | Key is malformed, revoked, or expired | No |
Capture Errors
Section titled “Capture Errors”| Error | Description | Retryable |
|---|---|---|
captureFailed(String) | Frame capture failed | No |
qualityTooLow | Capture quality too poor for analysis | No |
faceNotDetected | No face found in camera feed | No |
Network Errors
Section titled “Network Errors”| Error | Description | Retryable |
|---|---|---|
networkError(String) | Network request failed | Yes |
uploadFailed(String) | Upload to cloud storage failed | Yes |
apiError(String) | API returned an error | No |
Session Errors
Section titled “Session Errors”| Error | Description | Retryable |
|---|---|---|
sessionExpired | Session timed out | No |
sessionNotFound | Session ID doesn’t exist | No |
Processing Errors
Section titled “Processing Errors”| Error | Description | Retryable |
|---|---|---|
processingFailed(String) | Server-side inference failed | No |
timeout | Polling for results timed out | Yes |
Usage Errors
Section titled “Usage Errors”| Error | Description | Retryable |
|---|---|---|
quotaExceeded | Monthly scan limit reached | No |
rateLimited(retryAfter:) | Hourly rate limit exceeded. retryAfter is seconds to wait. | Yes |
General
Section titled “General”| Error | Description | Retryable |
|---|---|---|
cancelled | Measurement cancelled via cancel() | No |
unknown(String) | Unexpected error | No |
Retry with Backoff
Section titled “Retry with Backoff”For retryable errors, wait before retrying:
func measureWithRetry(sdk: CircadifySDK, maxRetries: Int = 3) async throws -> VitalSignsResult { for attempt in 0..<maxRetries { do { return try await sdk.measureVitals() } catch let error as CircadifyError where error.isRetryable { if attempt == maxRetries - 1 { throw error }
let delay: UInt64 if case .rateLimited(let retryAfter) = error { delay = UInt64(retryAfter) * 1_000_000_000 } else { delay = UInt64(pow(2.0, Double(attempt))) * 1_000_000_000 }
try await Task.sleep(nanoseconds: delay) } }
throw CircadifyError.unknown("Max retries exceeded")}Next Steps
Section titled “Next Steps”- Methods — Full method reference
- Camera & Permissions — Handle permission denial
- REST API Errors — Server-side error reference