Skip to content

Setup

Installation

Swift Package Manager is the only supported installation method.

In Xcode: File → Add Package Dependencies, enter the repository URL:

https://github.com/transistorsoft/native-background-geolocation

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:

  1. File → New → File from Template → Property List — name it Info.plist
  2. Remove it from all targets (leave every checkbox unchecked)
  3. Click Create — place the file next to your .xcodeproj
  4. Build Settings → INFOPLIST_FILE → set to Info.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
            }
        }
    }
}