Skip to content

Commit 24c1241

Browse files
committed
chore: TSLocationManager 4.0.31 (url + checksum)
1 parent 097a288 commit 24c1241

17 files changed

Lines changed: 309 additions & 78 deletions

CHANGELOG.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,40 @@
11
# CHANGELOG
22

3+
## 4.0.31 — 2026-04-02
4+
- Update panel images
5+
- docs: rewrite README with setup, build, test, publish sections
6+
- remove Pods from version control
7+
- Migrate isPowerSaveMode -> new DeviceSettings class
8+
- feat(State): add isFirstBoot property
9+
- chore: pod install (regenerate Pods from Podfile.lock)
10+
- feat(Geofence): add entryState, stateUpdatedAt, hits from TSGeofence
11+
- refactor(Geofence): remove unnecessary dict initializer
12+
- Revert "feat(Geofence): make dict initializer public"
13+
- feat(Geofence): make dict initializer public
14+
- feat(GeofenceEvent): add geofence: BGGeo.Geofence property; add dict init to Geofence
15+
- feat(LocationEvent): add GeofenceTrigger nested struct and mock property
16+
- Add typed State struct, remove getStationaryLocation()
17+
- Rename ConnectivityChangeEvent.hasConnection → isConnected
18+
- Align Swift API naming with Kotlin for cross-platform parity
19+
- Type GeofenceEvent.location as LocationEvent instead of raw dictionary
20+
- Add age and extras properties to LocationEvent
21+
- Add uuid property to LocationEvent
22+
- Add resetOdometer(), type authorization returns with CoreLocation enums
23+
- Move DocsExamplesCompileTest to DemoApp2Tests target
24+
- Add query params to Logger.getLog/emailLog, add uploadLog method
25+
- Update CHANGELOG for 4.0.30
26+
- Remove dead BackgroundGeolocationPlugin class name check
27+
- Add retry to asset checksum verification in publish-ios.sh
28+
- Update CHANGELOG for 4.0.29
29+
- Set framework IPHONEOS_DEPLOYMENT_TARGET to 13.0 to match podspec
30+
- Update CHANGELOG for 4.0.27
31+
- Remove CocoaPods Swift subspec -- SwiftInterface is SPM-only
32+
- Add --local flag to build-ios.sh for local SPM testing
33+
- Don't commit version wtih --bump and --dry-run
34+
- Update CHANGELOG for 4.0.24
35+
- Clean stale entries from Unreleased section
36+
- Add changelog generation to publish script
37+
338
## 4.0.30 — 2026-03-26
439
- Fix Capacitor validation
540
- Add retry to asset checksum verification in publish-ios.sh

Package.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ let package = Package(
2525
targets: [
2626
.binaryTarget(
2727
name: "TSLocationManager",
28-
url: "https://github.com/transistorsoft/native-background-geolocation/releases/download/4.0.30/TSLocationManager.xcframework.zip",
29-
checksum: "d1add09b4f2fa3c933b16cb2b0277a18bae2704210f15a2e7f5c18b2a7a9994a"
28+
url: "https://github.com/transistorsoft/native-background-geolocation/releases/download/4.0.31/TSLocationManager.xcframework.zip",
29+
checksum: "78c0401b40e2e7fa17b0df25a8fbfc4375f2e68f4d468e62f6a33959cd752d70"
3030
),
3131

3232
// Swift overlay that reexports the binary + TSBackgroundFetch

Sources/BackgroundGeolocation/SwiftInterface/App.swift

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,12 @@ extension BGGeo {
1212

1313
init() {}
1414

15-
public func createBackgroundTask() -> UIBackgroundTaskIdentifier {
15+
public func startBackgroundTask() -> UIBackgroundTaskIdentifier {
1616
manager.createBackgroundTask()
1717
}
1818

1919
public func stopBackgroundTask(_ taskId: UIBackgroundTaskIdentifier) {
2020
manager.stopBackgroundTask(taskId)
2121
}
22-
23-
public var isPowerSaveMode: Bool {
24-
manager.isPowerSaveMode()
25-
}
2622
}
2723
}

Sources/BackgroundGeolocation/SwiftInterface/Authorization.swift

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
//
55

66
import Foundation
7+
import CoreLocation
78
import TSLocationManager
89

910
extension BGGeo {
@@ -16,10 +17,13 @@ extension BGGeo {
1617
BGGeo.ProviderChangeEvent(manager.getProviderState())
1718
}
1819

19-
public func requestPermission() async throws -> Int {
20+
public func requestPermission() async throws -> CLAuthorizationStatus {
2021
try await withCheckedThrowingContinuation { continuation in
2122
manager.requestPermission(
22-
{ status in continuation.resume(returning: status.intValue) },
23+
{ status in
24+
let authStatus = CLAuthorizationStatus(rawValue: status.int32Value) ?? .notDetermined
25+
continuation.resume(returning: authStatus)
26+
},
2327
failure: { status in
2428
continuation.resume(throwing: NSError(
2529
domain: "BGGeo", code: status.intValue,
@@ -30,10 +34,13 @@ extension BGGeo {
3034
}
3135
}
3236

33-
public func requestTemporaryFullAccuracy(purpose: String) async throws -> Int {
37+
public func requestTemporaryFullAccuracy(purpose: String) async throws -> CLAccuracyAuthorization {
3438
try await withCheckedThrowingContinuation { continuation in
3539
manager.requestTemporaryFullAccuracy(purpose,
36-
success: { continuation.resume(returning: Int($0)) },
40+
success: {
41+
let accuracy = CLAccuracyAuthorization(rawValue: Int($0)) ?? .reducedAccuracy
42+
continuation.resume(returning: accuracy)
43+
},
3744
failure: { continuation.resume(throwing: $0) }
3845
)
3946
}

Sources/BackgroundGeolocation/SwiftInterface/BGGeo.swift

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public class BGGeo {
3535
/// - transistorAuthorizationToken: Optional token from ``TransistorAuthorizationService``.
3636
/// When provided, auto-configures `http.url` and `authorization` for the Transistor demo server.
3737
/// Always applied regardless of `reset`, since tokens may refresh between launches.
38-
/// - configure: Closure to set config properties. Executed inside a `batchUpdate`.
38+
/// - configure: Closure to set config properties. Executed inside a `config.edit`.
3939
///
4040
/// ```swift
4141
/// // Standard: apply config every launch
@@ -61,20 +61,20 @@ public class BGGeo {
6161

6262
if tsConfig.isFirstBoot() {
6363
// First install: always apply config regardless of reset flag
64-
config.batchUpdate(configure)
64+
config.edit(configure)
6565
} else if reset {
6666
// Subsequent launch + reset: wipe persisted config, re-apply from closure
6767
tsConfig.resetConfig(true)
68-
config.batchUpdate(configure)
68+
config.edit(configure)
6969
}
7070
// else: subsequent launch + reset:false → skip closure, use persisted config
7171

7272
// Token rewrite: always applies (even when reset:false skips the closure),
7373
// since tokens may refresh between launches.
7474
if let token {
75-
config.batchUpdate { config in
75+
config.edit { config in
7676
config.http.url = token.apiUrl
77-
config.authorization.strategy = "jwt"
77+
config.authorization.strategy = .jwt
7878
config.authorization.accessToken = token.accessToken
7979
config.authorization.refreshToken = token.refreshToken
8080
config.authorization.refreshUrl = token.refreshUrl
@@ -119,8 +119,23 @@ public class BGGeo {
119119
manager.changePace(isMoving)
120120
}
121121

122-
public var enabled: Bool {
123-
manager.enabled
122+
// MARK: - State
123+
124+
/// Read-only snapshot of the SDK's current runtime state.
125+
public var state: State {
126+
let tsConfig = TSConfig.sharedInstance()
127+
let odometer = TSOdometer.sharedInstance()
128+
return State(
129+
enabled: tsConfig.enabled,
130+
isMoving: tsConfig.isMoving,
131+
trackingMode: TrackingMode(tsConfig.trackingMode),
132+
odometer: odometer.odometer,
133+
odometerError: odometer.odometerError,
134+
schedulerEnabled: tsConfig.schedulerEnabled,
135+
didDeviceReboot: tsConfig.didDeviceReboot(),
136+
didLaunchInBackground: tsConfig.didLaunchInBackground,
137+
isFirstBoot: tsConfig.isFirstBoot()
138+
)
124139
}
125140

126141
// MARK: - Geolocation
@@ -178,8 +193,20 @@ public class BGGeo {
178193
}
179194
}
180195

181-
public var odometer: CLLocationDistance {
182-
manager.getOdometer()
196+
/// Reset the odometer to zero.
197+
///
198+
/// Convenience for `setOdometer(0)`.
199+
public func resetOdometer(
200+
timeout: TimeInterval = 10,
201+
desiredAccuracy: CLLocationAccuracy = kCLLocationAccuracyBest,
202+
maximumAge: Int = 5000,
203+
samples: Int = 3,
204+
persist: Bool = true,
205+
extras: [String: Any]? = nil
206+
) async throws -> LocationEvent {
207+
try await setOdometer(0, timeout: timeout, desiredAccuracy: desiredAccuracy,
208+
maximumAge: maximumAge, samples: samples,
209+
persist: persist, extras: extras)
183210
}
184211

185212
public func watchPosition(
@@ -205,10 +232,6 @@ public class BGGeo {
205232
manager.stopWatchPosition(watchId)
206233
}
207234

208-
public func getStationaryLocation() -> [String: Any]? {
209-
manager.getStationaryLocation() as? [String: Any]
210-
}
211-
212235
// MARK: - Event Listeners
213236

214237
@discardableResult

Sources/BackgroundGeolocation/SwiftInterface/Config/AuthorizationConfig.swift

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,23 @@ import Foundation
77
import TSLocationManager
88

99
extension BGGeo {
10+
public enum AuthorizationStrategy: String {
11+
case jwt = "JWT"
12+
case sas = "SAS"
13+
14+
init?(_ value: String?) {
15+
guard let value else { return nil }
16+
self.init(rawValue: value.uppercased())
17+
}
18+
}
19+
1020
public class AuthorizationConfig {
1121
private let module: TSAuthorizationConfig
1222
init(_ module: TSAuthorizationConfig) { self.module = module }
1323

14-
public var strategy: String? {
15-
get { module.strategy }
16-
set { module.strategy = newValue }
24+
public var strategy: AuthorizationStrategy? {
25+
get { AuthorizationStrategy(module.strategy) }
26+
set { module.strategy = newValue?.rawValue }
1727
}
1828
public var accessToken: String? {
1929
get { module.accessToken }

Sources/BackgroundGeolocation/SwiftInterface/Config/Config.swift

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,13 @@ extension BGGeo {
3838
logger = BGGeo.LoggerConfig(c.logger)
3939
}
4040

41-
// MARK: - State
42-
43-
public var enabled: Bool { tsConfig.enabled }
44-
public var isMoving: Bool { tsConfig.isMoving }
45-
public var trackingMode: TrackingMode { TrackingMode(tsConfig.trackingMode) }
46-
public var schedulerEnabled: Bool { tsConfig.schedulerEnabled }
47-
4841
// MARK: - Query
4942

50-
public var isLocationTrackingMode: Bool { tsConfig.isLocationTrackingMode() }
5143
public var isFirstBoot: Bool { tsConfig.isFirstBoot() }
5244

53-
// MARK: - Batch Update
45+
// MARK: - Edit
5446

55-
public func batchUpdate(_ block: @escaping (Config) -> Void) {
47+
public func edit(_ block: @escaping (Config) -> Void) {
5648
tsConfig.batchUpdate { _ in block(self) }
5749
}
5850

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//
2+
// DeviceSettings.swift
3+
// TSLocationManager
4+
//
5+
6+
import TSLocationManager
7+
8+
extension BGGeo {
9+
public class DeviceSettings {
10+
private let manager = BackgroundGeolocation.sharedInstance()
11+
12+
init() {}
13+
14+
/// Returns `true` if the device is currently in Low Power Mode.
15+
///
16+
/// Low Power Mode is a device-wide setting, independent of any individual app.
17+
public var isPowerSaveMode: Bool {
18+
manager.isPowerSaveMode()
19+
}
20+
}
21+
}

Sources/BackgroundGeolocation/SwiftInterface/Events/GeofenceEvent.swift

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,40 +15,35 @@ extension BGGeo {
1515
public let identifier: String
1616
public let action: String
1717
public let timestamp: Date
18-
public let location: [String: Any]
18+
public let location: BGGeo.LocationEvent
19+
public let geofence: BGGeo.Geofence
1920
public let extras: [String: Any]?
2021

21-
// Geofence center & radius (MEC for polygons)
22-
public let latitude: Double
23-
public let longitude: Double
24-
public let radius: Double
25-
public var center: CLLocationCoordinate2D {
26-
CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
27-
}
28-
29-
// Trigger coordinate extracted from location dict
22+
// Trigger coordinate extracted from location coords
3023
public let triggerCoordinate: CLLocationCoordinate2D?
3124
public let triggerHeading: Double?
3225

3326
public init(_ obj: TSGeofenceEvent) {
3427
self.identifier = obj.identifier
3528
self.action = obj.action
3629
self.timestamp = obj.timestamp
37-
self.location = obj.location as? [String: Any] ?? [:]
30+
let locationDict = obj.location as? [String: Any] ?? [:]
31+
let coordsDict = locationDict["coords"] as? [String: Any]
32+
let clLocation = CLLocation(
33+
latitude: (coordsDict?["latitude"] as? NSNumber)?.doubleValue ?? 0,
34+
longitude: (coordsDict?["longitude"] as? NSNumber)?.doubleValue ?? 0
35+
)
36+
self.location = BGGeo.LocationEvent(TSLocationEvent(locationDictionary: obj.location, location: clLocation))
37+
self.geofence = BGGeo.Geofence(obj.geofence)
3838
self.extras = obj.extras as? [String: Any]
3939

40-
self.latitude = obj.geofence.latitude
41-
self.longitude = obj.geofence.longitude
42-
self.radius = obj.geofence.radius
43-
44-
let coords = self.location["coords"] as? [String: Any]
45-
if let lat = coords?["latitude"] as? Double,
46-
let lng = coords?["longitude"] as? Double {
40+
if let lat = coordsDict?["latitude"] as? Double,
41+
let lng = coordsDict?["longitude"] as? Double {
4742
self.triggerCoordinate = CLLocationCoordinate2D(latitude: lat, longitude: lng)
4843
} else {
4944
self.triggerCoordinate = nil
5045
}
51-
let heading = (coords?["heading"] as? NSNumber)?.doubleValue
46+
let heading = (coordsDict?["heading"] as? NSNumber)?.doubleValue
5247
self.triggerHeading = (heading != nil && heading! >= 0) ? heading : nil
5348
}
5449
}

Sources/BackgroundGeolocation/SwiftInterface/Events/LocationEvent.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,28 +36,44 @@ extension BGGeo {
3636
public let confidence: Int?
3737
}
3838

39+
public struct GeofenceTrigger {
40+
public let identifier: String?
41+
public let action: String?
42+
public let timestamp: String?
43+
public let extras: [String: Any]?
44+
}
45+
3946
public let timestamp: Date?
4047
public let timestampString: String
4148

4249
public let data: [String: Any]
4350
public let location: CLLocation
51+
public let uuid: String?
4452
public let event: String?
4553
public let isMoving: Bool
54+
public let mock: Bool?
4655
public let sample: Bool
56+
public let age: Int?
57+
public let extras: [String: Any]?
4758
public let odometer: Double?
4859
public let odometerError: Double?
4960

5061
public let coords: Coords?
5162
public let battery: Battery?
5263
public let activity: Activity?
64+
public let geofence: GeofenceTrigger?
5365

5466
public init(_ obj: TSLocationEvent) {
5567
self.timestampString = obj.timestamp
5668
self.timestamp = ISO8601DateFormatter.fullParsing.date(from: obj.timestamp)
5769
self.data = obj.toDictionary() as? [String: Any] ?? [:]
5870
self.location = obj.location
71+
self.uuid = data["uuid"] as? String
5972
self.isMoving = obj.isMoving
73+
self.mock = data["mock"] as? Bool
6074
self.sample = data.keys.contains("sample")
75+
self.age = data.intOpt("age")
76+
self.extras = data["extras"] as? [String: Any]
6177
self.odometer = data["odometer"] as? Double
6278
self.odometerError = data["odometer_error"] as? Double
6379
self.event = obj.event
@@ -97,6 +113,17 @@ extension BGGeo {
97113
} else {
98114
activity = nil
99115
}
116+
117+
if let g = data["geofence"] as? [String: Any] {
118+
geofence = GeofenceTrigger(
119+
identifier: g["identifier"] as? String,
120+
action: g["action"] as? String,
121+
timestamp: g["timestamp"] as? String,
122+
extras: g["extras"] as? [String: Any]
123+
)
124+
} else {
125+
geofence = nil
126+
}
100127
}
101128
}
102129
}

0 commit comments

Comments
 (0)