Circadify

Configuration

Configure the Circadify iOS SDK — initialization, callbacks, and measurement options.

Initialization

The simplest way to initialize the SDK is with just your API key:

import CircadifySDK
 
let sdk = try CircadifySDK(apiKey: "ck_live_your_key_here")
swift

For full control, pass a CircadifyConfig:

let config = CircadifyConfig(
    apiKey: "ck_live_your_key_here",
    baseUrl: "https://api.circadify.com",
    measurementDuration: 30,
    debug: false
)
 
let sdk = try CircadifySDK(config: config)
swift

The Camera

CircadifyCamera owns the single AVCaptureSession that is shared by both the live preview and the measurement. You create one camera, render it with CircadifyCameraPreview(camera:), and hand the same instance to measureVitals(camera:) so the scan records the exact session the user is already looking at.

@State private var camera = CircadifyCamera()
swift

Because the camera and the SDK are decoupled, the preview can be live (with face tracking showing whether the user is framed, via camera.scanFace) before any measurement starts.

CircadifyCameraPreview(camera: camera)
    .ignoresSafeArea()
swift

Front or back camera

By default CircadifyCamera() uses the front (selfie) camera — the right choice for self-scans, where the user watches the live preview to frame their own face. Pass position: .back for the rear camera instead:

let camera = CircadifyCamera(position: .back)
swift

Reach for the rear camera when an operator measures another person (or in a kiosk / tripod setup). Its preview is not mirrored, and because the phone's screen no longer acts as a fill light for the subject (it faces away from them), ensure good ambient lighting. Both positions feed the identical on-device pipeline and produce the same measurements — see init(position:) in the method reference.

Note

CircadifyCamera is @MainActor and @Observable. Call try await camera.start() to prompt for permission and bring the session live, and camera.stop() when the view disappears. See Camera & Permissions for the full lifecycle.

Configuration Reference

public struct CircadifyConfig: Sendable {
    public let apiKey: String
    public var baseUrl: String
    public var measurementDuration: TimeInterval
    public var targetCaptureFps: Int
    public var minimumAcceptedFrames: Int
    public var maximumAcceptedFrames: Int
    public var debug: Bool
}
swift
PropertyTypeDefaultDescription
apiKeyStringRequired. Your API key (ck_test_* or ck_live_*).
baseUrlString"https://api.circadify.com"API base URL.
measurementDurationTimeInterval30Target capture duration in seconds.
targetCaptureFpsInt10Target capture rate; clamped to 5...30. Frames are accumulated at roughly this spacing.
minimumAcceptedFramesInt150Quality frames required before capture can finish.
maximumAcceptedFramesInt720Hard cap on accepted frames.
debugBoolfalsePrint debug logs to the console.

Callbacks

Progress updates are reported by the SDK, while quality warnings are reported by the camera. Both callbacks are delivered on the main thread.

Progress Updates

Set onProgress on the SDK instance to track a measurement as it runs:

sdk.onProgress = { update in
    print("Phase: \(update.phase)\(Int(update.percent))%")
}
swift
public struct ProgressUpdate {
    public let percent: Double       // 0-100
    public let phase: Phase          // .initializing, .capturing, .uploading, .processing
    public let elapsed: TimeInterval
    public let remaining: TimeInterval?
}
swift
PhaseDescription
.initializingSetting up camera capture
.capturingCapturing the measurement
.uploadingUploading the measurement payload
.processingWaiting for results

Quality Warnings

Quality warnings come from the camera, not the SDK. Set onQualityWarning on your CircadifyCamera. Warnings are emitted only while a measurement is running — the framing phase before measureVitals(camera:) produces face geometry, not warnings. For pre-scan guidance, read the camera's observable scanFace (present or absent, plus its geometry) to tell whether the user is framed; once the measurement starts, quality warnings take over.

camera.onQualityWarning = { warning in
    print("[\(warning.severity)] \(warning.type): \(warning.message)")
}
swift
public struct QualityWarning {
    public let type: WarningType     // .lighting, .motion, .facePosition, .occlusion
    public let message: String
    public let severity: Severity    // .low, .medium, .high
}
swift
Warning TypeTrigger
.lightingToo dark, too bright, or flickering
.motionExcessive movement
.facePositionHead turned or tilted too far
.occlusionFace partially blocked

Use these to show guidance in your UI — "Hold still", "Move to better lighting", etc.

Measurement Options

Pass MeasurementOptions to measureVitals(camera:) to provide demographics for improved accuracy:

let options = MeasurementOptions(
    demographics: Demographics(age: 35, sex: .male, fitzpatrick: 3)
)
 
let result = try await sdk.measureVitals(camera: camera, options: options)
swift
FieldTypeNotes
ageInt?User age in years.
sexSex?.male or .female. Encoded on the wire as "M" / "F".
fitzpatrickInt?Fitzpatrick skin type. Valid values are 16.

Demographics are optional but can improve measurement accuracy when provided.

Next Steps