CactusBrain Vision for iOS
Add the package, configure a project key, prepare a model, and run detection or OCR locally. Version 0.1.0.
Requirements
- iOS 17 or later
- Xcode 26 or later
- An Apple-silicon Mac for development. The engine ships arm64 device and arm64 simulator slices; there is no x86_64 simulator slice.
1. Install
In Xcode, choose File → Add Package Dependencies and enter the package URL, or add it to Package.swift:
// Package.swift
dependencies: [
.package(url: "https://github.com/cactusbrain/cactusbrain-swift", from: "0.1.0")
]
// Then add the product to your target:
.product(name: "CactusBrainVisionSDK", package: "cactusbrain-swift")The inference engine is a dependency of the package. You do not download, link or version it separately.
2. Configure
import CactusBrainVisionSDK
let session = try await CactusBrainVisionSDK.configure(
projectKey: projectKey
)Keep the key out of source control. An .xcconfig that is gitignored, with a committed example file, works well and lets you rotate the key without touching code.
Dashboard → your project → Quickstart. A live key begins cb_live_. It is displayed once.
3. Prepare a model
prepare resolves, downloads, verifies and installs the model your project is entitled to. It reports progress as it goes, and is a no-op once the model is installed.
let preparation = await session.prepare(.detect)
for try await event in preparation.events {
// .checking, .downloading(received:total:), .installing
update(event)
}
try await preparation.value // throws if preparation failedCall it before your first inference — typically at launch, or when the user first opens the feature that needs it, so a large download is tied to an action the user took.
4. Detect objects
let image = VisionImage(data: jpegData)
let output = try await session.detect(image)
for object in output.objects {
print(object.label, object.confidence, object.boundingBox)
}VisionImage takes encoded image bytes, so your app controls the conversion. Bake in EXIF orientation before encoding: if you do not, returned coordinates will be correct for the stored buffer but will not line up with the image as the user sees it.
5. Read text
let result = try await session.ocr(image)
print(result.text)
// Or read with a specific model:
let premium = try await session.ocr(image, using: .premium)Two OCR models are available: a small fast English model, and a larger multilingual one. Selecting a model does not download it — it arrives when you prepare or run it, so a picker cannot start a large transfer nobody asked for.
Handling errors
do {
try await preparation.value
} catch let error as ModelDeliveryError {
switch error {
case .notEntitled:
// Retrying will not help. Check entitlements in the dashboard.
showUpgradePath()
case .network:
showRetry()
case .integrityCheckFailed:
// The artifact was discarded rather than installed.
showIntegrityFailure()
case .missingCapability, .runtimeTooOld, .installedModelIncompatible:
showUnsupportedDevice()
default:
showGenericFailure()
}
} catch is CancellationError {
// Your own cancellation, not a delivery failure.
}Distinguish "not entitled" from "network failed". The first is a permanent answer and retrying is pointless; the second is worth a retry. Showing one message for both is the most common integration mistake.
Offline
// Models already on the device work with no network.
let installed = await session.installedModels()
let hasDetector = installed.contains { $0.family == .detect }Inference needs no network. Check what is installed if you want to show an offline-ready state before any preparation runs.