iOS setup
BeekonKit is a Swift package distributed as a signed binary .xcframework. iOS 15 is the minimum; iOS 17+ uses the modern CLLocationUpdate.liveUpdates API and iOS 15–16 falls back to the CLLocationManager delegate. The fallback is internal — your app sees the same Beekon.shared actor on every OS version.
Add the package
Section titled “Add the package”In Xcode: File → Add Package Dependencies… then paste:
https://github.com/wayqteam/beekon-ios-binary.gitSelect the version (or branch main for latest) and add BeekonKit to your app target.
For SwiftPM-driven projects (Package.swift):
let package = Package( // … platforms: [.iOS(.v15)], dependencies: [ .package(url: "https://github.com/wayqteam/beekon-ios-binary.git", from: "0.0.1"), ], targets: [ .target(name: "App", dependencies: [ .product(name: "BeekonKit", package: "beekon-ios-binary"), ]), ])Info.plist
Section titled “Info.plist”Add usage descriptions and the location background mode. The strings appear in the system permission dialog — write them in your app’s voice.
<key>NSLocationWhenInUseUsageDescription</key><string>Used to show your live position in the app.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key><string>Used to keep tracking your trips when the app is in the background.</string>
<key>UIBackgroundModes</key><array> <string>location</string></array>fetch and processing are not required for v1 — only location.
Authorization flow
Section titled “Authorization flow”Beekon does not request location permission for you. The host app drives the prompt because the right time and copy depend on your UX. You must reach Always authorization before background tracking will work.
- Request
whenInUsefirst — required by iOS before you can ever ask foralways. - After the user has granted
whenInUseand used the relevant feature once, requestalways. iOS will deny silently if you ask too aggressively. - Call
Beekon.shared.start()once authorization isalways.
The sample app at beekon-ios/Sample/LocationPermissionManager.swift shows the canonical two-step prompt — the relevant lines are:
private let manager = CLLocationManager()
func requestWhenInUse() { manager.requestWhenInUseAuthorization() }func requestAlways() { manager.requestAlwaysAuthorization() }Wire these to two buttons in your onboarding rather than firing both at once.
Initialise once
Section titled “Initialise once”import BeekonKit
@mainstruct MyApp: App { init() { Task { try await Beekon.shared.initialize() await Beekon.shared.configure(BeekonConfig(preset: .balanced)) } } var body: some Scene { WindowGroup { ContentView() } }}initialize is async throws and idempotent. configure is async and non-throwing — it’s a setter.
Beekon is an actor — every method is safe to call from any context, and the streams are hot (every consumer sees the latest state and positions).
When start() will fail
Section titled “When start() will fail”do { try await Beekon.shared.start()} catch BeekonError.permissionDenied { // user denied or hasn't granted Always — drive the prompt} catch BeekonError.locationServicesDisabled { // Settings → Privacy → Location Services is OFF system-wide} catch BeekonError.notConfigured { // configure(…) was never called} catch BeekonError.notInitialised { // initialize() was never called} catch BeekonError.internalError(let underlying) { // log and surface — should be rare}See Errors for the full taxonomy and what to do about each.
What happens behind the scenes
Section titled “What happens behind the scenes”| iOS version | Mechanism |
|---|---|
| iOS 17+ | CLLocationUpdate.liveUpdates async API. CLBackgroundActivitySession is held to keep background delivery alive. iOS 18+ additionally takes a CLServiceSession(authorization: .always). |
| iOS 15–16 | Legacy CLLocationManager delegate with allowsBackgroundLocationUpdates = true and showsBackgroundLocationIndicator = true. Wrapped as an AsyncStream so callers see one shape. |
A Significant Location Change monitor runs alongside on every version — it’s the only mechanism that can wake your app from terminated state. Beekon persists tracking intent in a UserDefaults suite (com.wayq.beekon) so an SLC-driven relaunch into a fresh process auto-resumes tracking.
- Android setup — if you also ship for Android.
- Background execution — terminated-state wake, foreground-vs-background rules.
- Lifecycle & states — what
Beekon.shared.statewill emit.