These are intentionally short, partial examples. They are meant to show one pattern at a time, not serve as complete drop-in apps.
Shared Setup
Most examples assume you already own an SDK instance:
import SwiftUI
import AVFoundation
import Charts
import SmartSpectra
struct ExampleHostView: View {
private let sdk = SmartSpectraSDK.shared
init() {
sdk.config.apiKey = "YOUR_API_KEY"
}
var body: some View {
Text("Ready")
}
}
Accessing Face Mesh
Read the latest face landmarks from sdk.metrics and render them into your own overlay.
Face landmarks are only populated when face metrics are requested.
Landmarks are pixel coordinates in the capture frame, so scale x by the frame
width and y by the frame height — they are different numbers. Capture defaults to
1280x720; if you change the capture resolution, change these constants to match.
let captureWidth: CGFloat = 1280
let captureHeight: CGFloat = 720
sdk.config.requestedMetrics = SmartSpectraConfig.breathingMetrics + SmartSpectraConfig.faceMetrics
if let latestLandmarks = sdk.metrics?.face.landmarks.last {
GeometryReader { geometry in
ZStack {
ForEach(Array(latestLandmarks.value.enumerated()), id: \.offset) { _, landmark in
Circle()
.fill(.blue)
.frame(width: 3, height: 3)
.position(
x: CGFloat(landmark.x) * geometry.size.width / captureWidth,
y: CGFloat(landmark.y) * geometry.size.height / captureHeight
)
}
}
}
}
Accessing Metrics
Read the latest metrics directly from the SDK object. Pulse and HRV values require cardio metrics to be requested.
sdk.config.requestedMetrics = SmartSpectraConfig.breathingMetrics + SmartSpectraConfig.cardioMetrics
if let metrics = sdk.metrics {
if let breathingRate = metrics.breathing.rate.last {
Text("Breathing: \(breathingRate.value.formatted()) RPM")
}
if let pulseRate = metrics.cardio.pulseRate.last {
Text("Pulse: \(pulseRate.value.formatted()) BPM")
}
}
LineChartView
Use a small reusable chart view for breathing, pulse, or confidence values.
struct LineChartView: View {
let orderedPairs: [(time: Date, value: Float)]
let title: String
var body: some View {
VStack(alignment: .leading) {
Text(title)
.font(.headline)
Chart {
ForEach(orderedPairs, id: \.time) { pair in
LineMark(
x: .value("Time", pair.time),
y: .value("Value", pair.value)
)
}
}
.frame(height: 180)
}
}
}
You can feed it from metrics like this:
if let breathingSeries = sdk.metrics?.breathing.rate {
LineChartView(
orderedPairs: breathingSeries.map {
(
time: Date(timeIntervalSince1970: Double($0.timestamp) / 1_000_000.0),
value: $0.value
)
},
title: "Breathing Rate"
)
}
Metrics Data Export
Convert the latest metrics into your own export format before writing to disk or uploading.
struct MetricsExportRow: Codable {
let timestampUs: Int64
let breathingRate: Float?
let pulseRate: Float?
}
if let metrics = sdk.metrics {
let row = MetricsExportRow(
timestampUs: metrics.breathing.rate.last?.timestamp
?? metrics.cardio.pulseRate.last?.timestamp
?? 0,
breathingRate: metrics.breathing.rate.last?.value,
pulseRate: metrics.cardio.pulseRate.last?.value
)
let jsonData = try JSONEncoder().encode(row)
let jsonString = String(decoding: jsonData, as: UTF8.self)
print(jsonString)
}
State Management
Own the SDK once and bind UI directly to its observable state.
struct MonitoringView: View {
private let sdk = SmartSpectraSDK.shared
init() {
sdk.config.apiKey = "YOUR_API_KEY"
}
var body: some View {
VStack {
Text("Status: \(String(describing: sdk.processingStatus))")
if let validationStatus = sdk.validationStatus {
Text(validationStatus.hint)
}
if let error = sdk.error {
Text(error.message)
.foregroundStyle(.red)
}
}
}
}
Camera Handling
For SDK-owned capture, select the camera while stopped before calling
try await sdk.start().
let sdk = SmartSpectraSDK.shared
sdk.config.apiKey = "YOUR_API_KEY"
try sdk.useCamera(.front)
To change cameras, stop, select the camera, and start again. Explicit selections never fall back to another camera.
@MainActor
func switchToBackCamera() async throws {
let sdk = SmartSpectraSDK.shared
try await sdk.stop()
try sdk.useCamera(.back)
try await sdk.start()
}
Your Own Camera or Video Decoder
If your app already captures video, select try sdk.useCustomInput() while
stopped, await sdk.start(), and pass each uncompressed sample to
input.sendFrame(sampleBuffer). Handle .rejected(let error) and observe
metrics as above. Your app keeps ownership of its camera and buffers.
The AVFoundation example
shows capture delegation and buffer requirements. The demo app’s
Video Testing sample
uses AVAssetReader to decode a clip and submit its frames through the public
API, including cancellation, frame rejection, and cleanup. Both paths require
normal SDK authentication.