@andynursa/checkpoint-capacitor
v0.1.0
Published
Checkpoint location / geofence / ingest SDK for Capacitor (iOS + Android). The SDK is a sensor; all detection logic runs server-side.
Maintainers
Readme
@checkpoint/capacitor
The Checkpoint location / geofence / ingest SDK for Capacitor (iOS + Android).
The SDK is deliberately thin: it bootstraps with your publishable key, identifies a subject, pulls the armed fence set, registers native region monitors, and posts location fixes — including from a force-quit app woken by the OS. All detection logic (M-of-N confirmation, dwell, exit hysteresis) runs server-side; the SDK is a sensor, not a decision-maker.
Install
npm install @checkpoint/capacitor && npx cap syncThis repo's app currently consumes the package from source via a path alias (
@checkpoint/capacitor→packages/checkpoint-capacitor/src) and afile:dependency — see the rootvite.config.ts,tsconfig.app.json, andpackage.json. No npm publish is required for the in-repo app.
Host app requirements
iOS Info.plist
Background location and force-quit wake-on-geofence are the whole point, so all
three are mandatory in the host app's Info.plist:
NSLocationWhenInUseUsageDescriptionNSLocationAlwaysAndWhenInUseUsageDescriptionUIBackgroundModes: location
iOS cold-relaunch revive (host AppDelegate)
The package's GeofenceManager engine owns the cold-relaunch revive
(reviveForBackgroundLaunch()), but it must be called from the host app's
AppDelegate.didFinishLaunchingWithOptions when launchOptions[.location] is
set, so an always + streamNow device woken from a killed state resumes
streaming. This is an app-delegate concern and stays in the app:
import CheckpointCapacitor // the pod's module
// ...
if launchOptions?[.location] != nil {
GeofenceManager.shared.reviveForBackgroundLaunch()
}Plugin registration
Once installed as an npm package, the native plugin auto-registers via
Capacitor's cap sync-generated packageClassList — do not also register it
manually (registerPluginInstance / registerPlugin(...)) or you double-register
and two instances race on onRegionEvent. See the repo's
docs/sdk-extraction-plan.md §7 (risk R1) for the device-verification checklist
around this flip.
Usage
import { Checkpoint } from "@checkpoint/capacitor";
// 1. Configure transport once. baseUrl, anonKey, and publishableKey are all
// required — point them at YOUR platform project (anon key is public-safe).
Checkpoint.init({
publishableKey: "pk_live_…",
baseUrl: "https://<your-project-ref>.supabase.co",
anonKey: "<your-platform-anon-key>",
});
// 2. Read the server-resolved directive / apply a mode.
const directive = await Checkpoint.getTrackingDirective("sub_…", { appId });
await Checkpoint.setTrackingMode("always"); // applies to the native layerPublic API surface
- Layer 1 (raw native plugin):
NativeGeofence+ the frozenNativeGeofencePlugincontract types (TrackingMode,RegionEvent,NativeDiagnostics,NativeFenceDiag). Wrapper SDKs (RN / Flutter) bind to this same native ABI. - Layer 2 (facade):
Checkpoint.{init, setTrackingMode, getTrackingMode, getTrackingDirective, setDeviceTrackingMode}+CheckpointConfig,CheckpointTransport,CHECKPOINT_API_VERSION. - Device REST hot-path:
mintDeviceToken,getTrackingDirective,setDeviceTrackingMode(all transport-injected; never the app's Supabase session client). - Presence primitives (framework-agnostic):
pullArmedFences(with the build-7 transient-empty-pull guard),ingestFix,postSelfFence,postJoinPlace,fetchNearbyPlaces,metersBetween,fenceSignature,zoneFor,DEFAULT_DIRECTIVE. - Subject id codec:
encodeSubjectPublicId,decodeSubjectPublicId. - Tracking-mode local persistence:
readStoredMode,writeStoredMode,readPendingMode,writePendingMode,clearPendingMode,modeKey.
Tests
npm run build && node --test test/pure.test.mjs test/fences.test.mjsCovers the pure functions (metersBetween, fenceSignature, zoneFor, the
subject-id codec) and the build-7 transient-empty-pull guard in pullArmedFences.
