Quickstart
This page walks through install, configure, start, and observing positions on every platform. Pick your platform once — the tabs stay synced as you scroll.
The platform-specific OS configuration (manifest, plist, pubspec) is in the per-platform setup deep-dives:
- Android setup — permissions, foreground service notification
- iOS setup — Info.plist keys, background mode
- Flutter setup — desugaring on API 24–25, both platforms’ native config
1. Install
Section titled “1. Install”dependencies { implementation("io.github.wayqteam:beekon:0.0.1")}Min SDK 24, Kotlin 2.x. Maven Central is included by default in AGP — no extra repo needed.
In Xcode: File → Add Package Dependencies… and paste:
https://github.com/wayqteam/beekon-ios-binary.gitOr in Package.swift:
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"), ]),]iOS 15 minimum. The package ships as a signed .xcframework binary target — set the framework to Embed & Sign in your app target.
dependencies: beekon_flutter: ^0.0.1flutter pub getFlutter 3.44+ recommended (default SwiftPM). On 3.32–3.43 enable it once: flutter config --enable-swift-package-manager. CocoaPods is not supported.
2. Initialize and configure
Section titled “2. Initialize and configure”initialize is one-time and idempotent — call it once at app startup. configure is where the preset and any overrides go.
class App : Application() { override fun onCreate() { super.onCreate() Beekon.initialize(this) Beekon.configure( BeekonConfig( preset = Preset.Balanced, notification = NotificationConfig( channelId = "beekon-tracking", channelName = "Tracking", notificationId = 7411, title = "Beekon", text = "Tracking your location", smallIcon = R.drawable.ic_stat_beekon, ), ), ) }}Android requires a foreground-service notification. The NotificationConfig is mandatory — see Background execution for what each field is used for.
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() } }}Beekon is an actor — every method is safe to call from any context. The host app drives the permission prompt before calling start(); see iOS setup.
import 'package:beekon_flutter/beekon_flutter.dart';
Future<void> bootstrap() async { await Beekon.instance.initialize(); await Beekon.instance.configure( const BeekonConfig( preset: BeekonPreset.balanced, androidNotification: AndroidNotification( channelId: 'beekon-tracking', channelName: 'Tracking', notificationId: 7411, title: 'Beekon', text: 'Tracking your location', smallIcon: 'ic_launcher', ), ), );}androidNotification is required when running on Android. iOS ignores it.
3. Start tracking
Section titled “3. Start tracking”start() requests location updates and brings up the platform’s background-execution machinery (Android foreground service, iOS background activity session). It throws if permission is missing or the platform refuses.
lifecycleScope.launch { try { Beekon.start() } catch (e: BeekonError.PermissionDenied) { // request ACCESS_FINE_LOCATION + ACCESS_BACKGROUND_LOCATION, then retry } catch (e: BeekonError.NoGmsAvailable) { // device has no Google Play Services — Beekon v1 doesn't support non-GMS }}do { try await Beekon.shared.start()} catch BeekonError.permissionDenied { // user denied or hasn't granted Always — see iOS setup for the prompt flow} catch BeekonError.locationServicesDisabled { // Settings → Privacy → Location Services is OFF system-wide}try { await Beekon.instance.start();} on PermissionDenied { // request runtime permissions on Android, drive Always prompt on iOS} on NoGmsAvailable { // Android device has no Google Play Services}4. Observe state and positions
Section titled “4. Observe state and positions”State and positions are streamed; consume with whatever’s idiomatic for your platform.
// Latest state, replay-1val state by Beekon.state.collectAsStateWithLifecycle()
// Position stream — replay-1, DROP_OLDEST, buffer 64LaunchedEffect(Unit) { Beekon.positions.collect { p -> Log.d("beekon", "${p.lat}, ${p.lng} acc=${p.accuracy}m") }}Task { for await state in await Beekon.shared.state { print("state: \(state)") }}
Task { for await p in await Beekon.shared.positions { print("\(p.lat), \(p.lng) acc=\(p.accuracy)m") }}Both streams are hot — every consumer sees the latest emission.
StreamBuilder<BeekonState>( stream: Beekon.instance.state, initialData: const Idle(), builder: (context, snap) => Text('${snap.data}'),);
Beekon.instance.positions.listen((Position p) { print('${p.lat}, ${p.lng} acc=${p.accuracy}m');});The position stream only emits while Flutter is alive — the persistent record is the native DB (read it via history).
5. Read history
Section titled “5. Read history”History is the native library’s own SQLite database. It survives process death; the live position stream does not.
val now = Instant.now()val hourAgo = now.minus(1, ChronoUnit.HOURS)val points: List<Position> = Beekon.history(from = hourAgo, to = now)let now = Date()let hourAgo = now.addingTimeInterval(-3600)let points = try await Beekon.shared.history(from: hourAgo, to: now)final now = DateTime.now();final hourAgo = now.subtract(const Duration(hours: 1));final points = await Beekon.instance.history(from: hourAgo, to: now);Retention is TTL 7 days + 100,000 row cap, auto-pruned on each write batch — the same on every platform. See Persistence & history for the schema.
- Wire OS-level config: Android, iOS, or Flutter.
- Read Lifecycle & states — the state machine that drives
state. - Read Background execution for what’s actually keeping your app alive.