Setup¶
Installation¶
Swift Package Manager is the only supported installation method.
In Xcode: File → Add Package Dependencies, enter the repository URL:
Select Up to Next Major Version from the latest release, then add the BackgroundGeolocation library to your target.
App initialisation¶
Call TSBackgroundFetch from your App initialiser:
import SwiftUI
import BackgroundGeolocation
@main
struct MyApp: App {
init() {
TSBackgroundFetch.sharedInstance()?.didFinishLaunching()
}
var body: some Scene {
WindowGroup { ContentView() }
}
}
License key¶
Purchase a license
The SDK requires a license for App Store builds. All other builds (debug, simulator, TestFlight, ad-hoc) are fully functional without one. Purchase at docs.transistorsoft.com.
Add a User-Defined build setting to your target:
Build Settings → + → User-Defined Setting
| Setting | Value |
|---|---|
TRANSISTOR_LICENSE_KEY |
your-license-key-jwt |
Then reference it from your supplementary Info.plist via $(TRANSISTOR_LICENSE_KEY).
Signing & Capabilities¶
Add the Background Modes capability to your target:
Signing & Capabilities → + Capability → Background Modes
Enable:
- ☑ Location updates
- ☑ Background fetch
- ☑ Background processing
- ☑ Audio, AirPlay, and Picture in Picture (required for debug sound effects only)
Info tab¶
Privacy usage strings¶
Target → Info → Custom iOS Target Properties
Add the following keys:
| Key (Xcode display name) | Raw key | Example value |
|---|---|---|
| Privacy - Location Always and When In Use Usage Description | NSLocationAlwaysAndWhenInUseUsageDescription |
App requires location access at all times for background tracking. |
| Privacy - Location When In Use Usage Description | NSLocationWhenInUseUsageDescription |
App requires location access while in use. |
| Privacy - Motion Usage Description | NSMotionUsageDescription |
Motion detection helps determine when the device is stationary. |
Background task identifiers¶
Still in Custom iOS Target Properties, add:
| Key (Xcode display name) | Raw key | Value |
|---|---|---|
| Permitted background task scheduler identifiers | BGTaskSchedulerPermittedIdentifiers |
Array → com.transistorsoft.fetch |
Supplementary Info.plist¶
TSLocationManagerLicense is a custom key that cannot be expressed as a build setting. Create a supplementary property list file:
- File → New → File from Template → Property List — name it
Info.plist - Remove it from all targets (leave every checkbox unchecked)
- Click Create — place the file next to your
.xcodeproj - Build Settings →
INFOPLIST_FILE→ set toInfo.plist
Add the license key, substituted from the TRANSISTOR_LICENSE_KEY build setting:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>TSLocationManagerLicense</key>
<string>$(TRANSISTOR_LICENSE_KEY)</string>
</dict>
</plist>
Tip
Because INFOPLIST_FILE is set alongside GENERATE_INFOPLIST_FILE = YES, Xcode merges the two sources. The file is never copied into your bundle directly.
Example¶
import SwiftUI
import BackgroundGeolocation
@main
struct MyApp: App {
private var subscriptions = Set<BGGeo.EventSubscription>()
init() {
let bgGeo = BGGeo.shared
// Register event listeners *before* calling ready()
bgGeo.onLocation { event in
print("[onLocation] \(event.coords)")
}.store(in: &subscriptions)
// ready() configures the SDK and restores persisted state.
// It does NOT start tracking — call start()/stop() separately.
bgGeo.ready { config in
config.geolocation.desiredAccuracy = kCLLocationAccuracyBest
config.geolocation.distanceFilter = 10
config.app.stopOnTerminate = false
config.app.startOnBoot = true
config.logger.debug = true
config.logger.logLevel = .verbose
}
}
var body: some Scene {
WindowGroup { ContentView() }
}
}
// Elsewhere — e.g. a toggle-button action in your UI
struct TrackingButton: View {
@State private var isTracking = BGGeo.shared.state.enabled
var body: some View {
Button(isTracking ? "Stop" : "Start") {
Task {
let bgGeo = BGGeo.shared
if isTracking {
bgGeo.stop()
} else {
try await bgGeo.start()
}
isTracking = bgGeo.state.enabled
}
}
}
}