react-native-bg-geolocation 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BgGeolocation.podspec +39 -0
- package/LICENSE +20 -0
- package/README.md +366 -0
- package/android/build.gradle +69 -0
- package/android/src/main/AndroidManifest.xml +53 -0
- package/android/src/main/java/com/bggeolocation/BgGeolocationActivityRecognitionReceiver.kt +116 -0
- package/android/src/main/java/com/bggeolocation/BgGeolocationBootReceiver.kt +44 -0
- package/android/src/main/java/com/bggeolocation/BgGeolocationForegroundService.kt +373 -0
- package/android/src/main/java/com/bggeolocation/BgGeolocationGeofenceReceiver.kt +55 -0
- package/android/src/main/java/com/bggeolocation/BgGeolocationHeadlessTask.kt +138 -0
- package/android/src/main/java/com/bggeolocation/BgGeolocationModule.kt +1030 -0
- package/android/src/main/java/com/bggeolocation/BgGeolocationMotionStateMachine.kt +159 -0
- package/android/src/main/java/com/bggeolocation/BgGeolocationPackage.kt +31 -0
- package/android/src/main/res/drawable/bg_geo_notification.xml +9 -0
- package/ios/BgGeolocation.h +14 -0
- package/ios/BgGeolocation.mm +709 -0
- package/ios/engine/AtomicBoolean.swift +48 -0
- package/ios/engine/BGActivityChangeEvent.swift +20 -0
- package/ios/engine/BGActivityConfig.swift +71 -0
- package/ios/engine/BGAppConfig.swift +92 -0
- package/ios/engine/BGAppState.swift +147 -0
- package/ios/engine/BGAuthorization.swift +85 -0
- package/ios/engine/BGAuthorizationAlertPresenter.swift +39 -0
- package/ios/engine/BGAuthorizationConfig.swift +50 -0
- package/ios/engine/BGAuthorizationEvent.swift +40 -0
- package/ios/engine/BGBackgroundTaskManager.swift +143 -0
- package/ios/engine/BGCLRouter.swift +101 -0
- package/ios/engine/BGCallback.swift +19 -0
- package/ios/engine/BGConfig.swift +440 -0
- package/ios/engine/BGConfigModuleBase.swift +180 -0
- package/ios/engine/BGConfigOLD.swift +582 -0
- package/ios/engine/BGConnectivityChangeEvent.swift +15 -0
- package/ios/engine/BGCrashDetector.swift +122 -0
- package/ios/engine/BGCurrentPositionRequest.swift +87 -0
- package/ios/engine/BGDataStore.swift +75 -0
- package/ios/engine/BGDatabase.swift +677 -0
- package/ios/engine/BGDatabasePool.swift +220 -0
- package/ios/engine/BGDatabaseQueue.swift +215 -0
- package/ios/engine/BGDateUtils.swift +26 -0
- package/ios/engine/BGDeviceInfo.swift +54 -0
- package/ios/engine/BGDeviceManager.swift +65 -0
- package/ios/engine/BGEnabledChangeEvent.swift +11 -0
- package/ios/engine/BGEnv.swift +17 -0
- package/ios/engine/BGEventBus.swift +83 -0
- package/ios/engine/BGEventManager.swift +169 -0
- package/ios/engine/BGEventNames.swift +51 -0
- package/ios/engine/BGGeofence.swift +233 -0
- package/ios/engine/BGGeofenceDAO.swift +152 -0
- package/ios/engine/BGGeofenceEvent.swift +42 -0
- package/ios/engine/BGGeofenceLocationRequest.swift +94 -0
- package/ios/engine/BGGeofenceManager.swift +315 -0
- package/ios/engine/BGGeofenceTransition.swift +97 -0
- package/ios/engine/BGGeofencesChangeEvent.swift +26 -0
- package/ios/engine/BGGeolocationConfig.swift +136 -0
- package/ios/engine/BGHeartbeatEvent.swift +31 -0
- package/ios/engine/BGHeartbeatService.swift +51 -0
- package/ios/engine/BGHttpConfig.swift +105 -0
- package/ios/engine/BGHttpErrorCodes.swift +63 -0
- package/ios/engine/BGHttpEvent.swift +34 -0
- package/ios/engine/BGHttpRequest.swift +126 -0
- package/ios/engine/BGHttpResponse.swift +93 -0
- package/ios/engine/BGHttpService.swift +428 -0
- package/ios/engine/BGKalmanFilter.swift +105 -0
- package/ios/engine/BGLMActionNames.swift +55 -0
- package/ios/engine/BGLicenseManager.swift +26 -0
- package/ios/engine/BGLiveActivityManager.swift +327 -0
- package/ios/engine/BGLocation.swift +311 -0
- package/ios/engine/BGLocationAuthorization.swift +427 -0
- package/ios/engine/BGLocationDAO.swift +252 -0
- package/ios/engine/BGLocationErrors.swift +28 -0
- package/ios/engine/BGLocationEvent.swift +43 -0
- package/ios/engine/BGLocationFilter.swift +82 -0
- package/ios/engine/BGLocationFilterConfig.swift +57 -0
- package/ios/engine/BGLocationHelper.swift +54 -0
- package/ios/engine/BGLocationManager.swift +662 -0
- package/ios/engine/BGLocationMetricsEngine.swift +116 -0
- package/ios/engine/BGLocationRequestService.swift +459 -0
- package/ios/engine/BGLocationSatisfier.swift +14 -0
- package/ios/engine/BGLocationStreamEvent.swift +27 -0
- package/ios/engine/BGLog.swift +337 -0
- package/ios/engine/BGLogLevel.swift +26 -0
- package/ios/engine/BGLoggerConfig.swift +60 -0
- package/ios/engine/BGMotionActivity.swift +31 -0
- package/ios/engine/BGMotionActivityClassifier.swift +108 -0
- package/ios/engine/BGMotionActivityManagerAdapter.swift +40 -0
- package/ios/engine/BGMotionActivitySource.swift +46 -0
- package/ios/engine/BGMotionDetector.swift +377 -0
- package/ios/engine/BGMotionPermissionManager.swift +50 -0
- package/ios/engine/BGNativeLogger.swift +48 -0
- package/ios/engine/BGNotificaitons.swift +37 -0
- package/ios/engine/BGOdometer.swift +66 -0
- package/ios/engine/BGPersistenceConfig.swift +29 -0
- package/ios/engine/BGPolygonStreamRequest.swift +48 -0
- package/ios/engine/BGPowerSaveChangeEvent.swift +12 -0
- package/ios/engine/BGPropertySpec.swift +29 -0
- package/ios/engine/BGProviderChangeEvent.swift +31 -0
- package/ios/engine/BGQueue.swift +50 -0
- package/ios/engine/BGRPC.swift +194 -0
- package/ios/engine/BGReachability.swift +58 -0
- package/ios/engine/BGResultSet.swift +157 -0
- package/ios/engine/BGSchedule.swift +228 -0
- package/ios/engine/BGScheduleEvent.swift +13 -0
- package/ios/engine/BGScheduler.swift +116 -0
- package/ios/engine/BGSingleLocationRequest.swift +49 -0
- package/ios/engine/BGStreamLocationRequest.swift +42 -0
- package/ios/engine/BGTemplate.swift +54 -0
- package/ios/engine/BGTimerService.swift +46 -0
- package/ios/engine/BGTrackingAudioManager.swift +286 -0
- package/ios/engine/BGTrackingService.swift +879 -0
- package/ios/engine/BGWatchPositionRequest.swift +63 -0
- package/ios/engine/DatabaseQueue.swift +47 -0
- package/ios/engine/LogQuery.swift +10 -0
- package/ios/engine/SQLQuery.swift +65 -0
- package/ios/engine/TransistorAuthorizationToken.swift +182 -0
- package/ios/liveactivity/BGLiveTrackingAttributes.swift +52 -0
- package/ios/locationpush/BGLocationPushDeliverer.swift +260 -0
- package/ios/locationpush/BGLocationPushService.swift +161 -0
- package/ios/locationpush/BGLocationPushShared.swift +98 -0
- package/ios/locationpush/BGLocationPushSocketClient.swift +198 -0
- package/lib/module/NativeBgGeolocation.js +5 -0
- package/lib/module/NativeBgGeolocation.js.map +1 -0
- package/lib/module/events.js +20 -0
- package/lib/module/events.js.map +1 -0
- package/lib/module/index.js +706 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NativeBgGeolocation.d.ts +57 -0
- package/lib/typescript/src/NativeBgGeolocation.d.ts.map +1 -0
- package/lib/typescript/src/events.d.ts +18 -0
- package/lib/typescript/src/events.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +238 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +229 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/package.json +141 -0
- package/src/NativeBgGeolocation.ts +236 -0
- package/src/events.ts +17 -0
- package/src/index.tsx +935 -0
- package/src/types.ts +254 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import CoreLocation
|
|
3
|
+
|
|
4
|
+
@objc public final class BGHeartbeatEvent: NSObject {
|
|
5
|
+
|
|
6
|
+
@objc public private(set) var location: CLLocation?
|
|
7
|
+
@objc public private(set) var data: [AnyHashable: Any]?
|
|
8
|
+
|
|
9
|
+
@objc public init(location: CLLocation?) {
|
|
10
|
+
super.init()
|
|
11
|
+
guard let location = location else {
|
|
12
|
+
self.location = nil
|
|
13
|
+
self.data = nil
|
|
14
|
+
return
|
|
15
|
+
}
|
|
16
|
+
let rebuilt = CLLocation(coordinate: location.coordinate,
|
|
17
|
+
altitude: location.altitude,
|
|
18
|
+
horizontalAccuracy: location.horizontalAccuracy,
|
|
19
|
+
verticalAccuracy: location.verticalAccuracy,
|
|
20
|
+
course: location.course,
|
|
21
|
+
speed: location.speed,
|
|
22
|
+
timestamp: location.timestamp)
|
|
23
|
+
self.location = rebuilt
|
|
24
|
+
let tsLocation = BGLocation(location: rebuilt, type: "heartbeat", extras: nil)
|
|
25
|
+
self.data = tsLocation.toDictionary()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@objc public func toDictionary() -> [String: Any] {
|
|
29
|
+
return ["location": data ?? NSNull()]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
@objc public class BGHeartbeatService: NSObject {
|
|
4
|
+
|
|
5
|
+
private static var _sharedInstance: BGHeartbeatService?
|
|
6
|
+
private static let lock = NSLock()
|
|
7
|
+
|
|
8
|
+
@objc public var timer: Timer?
|
|
9
|
+
@objc public var callback: ((Any?) -> Void)?
|
|
10
|
+
|
|
11
|
+
@objc public class func sharedInstance() -> BGHeartbeatService {
|
|
12
|
+
lock.lock()
|
|
13
|
+
defer { lock.unlock() }
|
|
14
|
+
if _sharedInstance == nil {
|
|
15
|
+
_sharedInstance = BGHeartbeatService()
|
|
16
|
+
}
|
|
17
|
+
return _sharedInstance!
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@objc public override init() {
|
|
21
|
+
super.init()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@objc public var isRunning: Bool {
|
|
25
|
+
return timer != nil && timer!.isValid
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@objc public func startWithInterval(_ interval: TimeInterval, callback: @escaping (Any?) -> Void) {
|
|
29
|
+
stop()
|
|
30
|
+
self.callback = callback
|
|
31
|
+
timer = Timer.scheduledTimer(timeInterval: interval,
|
|
32
|
+
target: self,
|
|
33
|
+
selector: #selector(onHeartbeat(_:)),
|
|
34
|
+
userInfo: nil,
|
|
35
|
+
repeats: true)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@objc public func stop() {
|
|
39
|
+
timer?.invalidate()
|
|
40
|
+
timer = nil
|
|
41
|
+
callback = nil
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@objc public func evaluate() {
|
|
45
|
+
onHeartbeat(timer)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@objc private func onHeartbeat(_ timer: Timer?) {
|
|
49
|
+
callback?(nil)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
@objc public class BGHttpConfig: BGConfigModuleBase {
|
|
4
|
+
|
|
5
|
+
@objc public var url: String = ""
|
|
6
|
+
@objc public var method: String = "POST"
|
|
7
|
+
@objc public var headers: [String: String] = [:]
|
|
8
|
+
@objc public var params: [String: Any] = [:]
|
|
9
|
+
@objc public var rootProperty: String = "location"
|
|
10
|
+
@objc public var timeout: Double = 60.0
|
|
11
|
+
@objc public var autoSync: Bool = true
|
|
12
|
+
@objc public var autoSyncThreshold: Int = 0
|
|
13
|
+
@objc public var batchSync: Bool = false
|
|
14
|
+
@objc public var maxBatchSize: Int = -1
|
|
15
|
+
@objc public var disableAutoSyncOnCellular: Bool = false
|
|
16
|
+
|
|
17
|
+
@objc public override func applyDefaults() {
|
|
18
|
+
url = ""
|
|
19
|
+
method = "POST"
|
|
20
|
+
headers = [:]
|
|
21
|
+
params = [:]
|
|
22
|
+
rootProperty = "location"
|
|
23
|
+
timeout = 60.0
|
|
24
|
+
autoSync = true
|
|
25
|
+
autoSyncThreshold = 0
|
|
26
|
+
batchSync = false
|
|
27
|
+
maxBatchSize = -1
|
|
28
|
+
disableAutoSyncOnCellular = false
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@objc public var hasValidUrl: Bool {
|
|
32
|
+
return !url.isEmpty && URL(string: url) != nil
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@objc public var timeoutSeconds: TimeInterval {
|
|
36
|
+
return timeout
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@objc public var effectiveBatchSize: Int {
|
|
40
|
+
return maxBatchSize > 0 ? maxBatchSize : 50
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@objc public var isImmediateSyncEnabled: Bool {
|
|
44
|
+
return autoSync && autoSyncThreshold == 0
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@objc public func fullUrlWithParams() -> String {
|
|
48
|
+
guard !url.isEmpty else { return url }
|
|
49
|
+
if params.isEmpty { return url }
|
|
50
|
+
var components = URLComponents(string: url)
|
|
51
|
+
let queryItems = params.map { URLQueryItem(name: $0.key, value: "\($0.value)") }
|
|
52
|
+
let existing = components?.queryItems ?? []
|
|
53
|
+
components?.queryItems = existing + queryItems
|
|
54
|
+
return components?.url?.absoluteString ?? url
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@objc public func validateAndCleanUrl(_ rawUrl: String) -> String? {
|
|
58
|
+
let trimmed = rawUrl.trimmingCharacters(in: .whitespaces)
|
|
59
|
+
guard !trimmed.isEmpty, URL(string: trimmed) != nil else { return nil }
|
|
60
|
+
return trimmed
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@objc public func headersWithAuth(_ auth: BGAuthorization?) -> [String: String] {
|
|
64
|
+
var result = headers
|
|
65
|
+
if let auth = auth, let token = auth.accessToken, !token.isEmpty {
|
|
66
|
+
result["Authorization"] = "Bearer \(token)"
|
|
67
|
+
}
|
|
68
|
+
return result
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@objc public override func sensitivePropertyNames() -> [String] {
|
|
72
|
+
return ["headers"]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@objc public override func deprecatedPropertyMappings() -> [String: String] {
|
|
76
|
+
return [
|
|
77
|
+
"httpRootProperty": "rootProperty",
|
|
78
|
+
"httpTimeout": "timeout"
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@objc public override func propertySpecs() -> [BGPropertySpecImpl] {
|
|
83
|
+
return [
|
|
84
|
+
BGPropertySpec(name: "url", type: "string"),
|
|
85
|
+
BGPropertySpec(name: "method", type: "string"),
|
|
86
|
+
BGPropertySpec(name: "headers", type: "object"),
|
|
87
|
+
BGPropertySpec(name: "params", type: "object"),
|
|
88
|
+
BGPropertySpec(name: "rootProperty", type: "string"),
|
|
89
|
+
BGPropertySpec(name: "timeout", type: "double"),
|
|
90
|
+
BGPropertySpec(name: "autoSync", type: "bool"),
|
|
91
|
+
BGPropertySpec(name: "autoSyncThreshold", type: "int"),
|
|
92
|
+
BGPropertySpec(name: "batchSync", type: "bool"),
|
|
93
|
+
BGPropertySpec(name: "maxBatchSize", type: "int"),
|
|
94
|
+
BGPropertySpec(name: "disableAutoSyncOnCellular", type: "bool")
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@objc public override func validateConfiguration() -> Bool {
|
|
99
|
+
return true
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@objc public override var description: String {
|
|
103
|
+
return "<BGHttpConfig url=\(url) method=\(method) autoSync=\(autoSync)>"
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
public let BGHttpServiceErrorDomain = "BGHttpServiceErrorDomain"
|
|
4
|
+
public let BGHttpErrorKeyUnderlying = "BGHttpErrorKeyUnderlying"
|
|
5
|
+
public let BGHttpErrorKeyStatus = "BGHttpErrorKeyStatus"
|
|
6
|
+
public let BGHttpErrorKeyResponseBody = "BGHttpErrorKeyResponseBody"
|
|
7
|
+
public let BGHttpErrorKeyURL = "BGHttpErrorKeyURL"
|
|
8
|
+
public let BGHttpErrorKeyFromURL = "BGHttpErrorKeyFromURL"
|
|
9
|
+
public let BGHttpErrorKeyToURL = "BGHttpErrorKeyToURL"
|
|
10
|
+
|
|
11
|
+
@objc public final class BGHttpErrorCodes: NSObject {
|
|
12
|
+
|
|
13
|
+
@objc public class func localizedDescription(forErrorCode code: Int) -> String {
|
|
14
|
+
switch code {
|
|
15
|
+
case 1: return "Invalid URL"
|
|
16
|
+
case 2: return "Network connection failed"
|
|
17
|
+
case 3: return "Sync operation already in progress"
|
|
18
|
+
case 4: return "HTTP response error"
|
|
19
|
+
case 5: return "HTTP redirect disallowed"
|
|
20
|
+
default: return ""
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@objc public class func error(withCode code: Int, description: String?, userInfo: [AnyHashable: Any]?) -> NSError {
|
|
25
|
+
var info: [String: Any] = [:]
|
|
26
|
+
info[NSLocalizedDescriptionKey] = description ?? localizedDescription(forErrorCode: code)
|
|
27
|
+
if let userInfo = userInfo {
|
|
28
|
+
for (key, value) in userInfo {
|
|
29
|
+
if let key = key as? String { info[key] = value }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return NSError(domain: BGHttpServiceErrorDomain, code: code, userInfo: info)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@objc public class func invalidURLError(_ url: String?) -> NSError {
|
|
36
|
+
let userInfo: [AnyHashable: Any] = [BGHttpErrorKeyURL: url ?? ""]
|
|
37
|
+
return error(withCode: 1, description: localizedDescription(forErrorCode: 1), userInfo: userInfo)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@objc public class func noNetworkError() -> NSError {
|
|
41
|
+
return error(withCode: 2, description: localizedDescription(forErrorCode: 2), userInfo: nil)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@objc public class func syncInProgressError() -> NSError {
|
|
45
|
+
return error(withCode: 3, description: localizedDescription(forErrorCode: 3), userInfo: nil)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@objc public class func responseError(withStatus status: Int, url: String?, bodyBytes: Any?, underlying: Error?) -> NSError {
|
|
49
|
+
var userInfo: [AnyHashable: Any] = [BGHttpErrorKeyStatus: status]
|
|
50
|
+
if let url = url { userInfo[BGHttpErrorKeyURL] = url }
|
|
51
|
+
if let bodyBytes = bodyBytes { userInfo[BGHttpErrorKeyResponseBody] = bodyBytes }
|
|
52
|
+
if let underlying = underlying { userInfo[BGHttpErrorKeyUnderlying] = underlying }
|
|
53
|
+
return error(withCode: 4, description: localizedDescription(forErrorCode: 4), userInfo: userInfo)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@objc public class func redirectError(from fromURL: String?, to toURL: String?) -> NSError {
|
|
57
|
+
let userInfo: [AnyHashable: Any] = [
|
|
58
|
+
BGHttpErrorKeyFromURL: fromURL ?? "",
|
|
59
|
+
BGHttpErrorKeyToURL: toURL ?? ""
|
|
60
|
+
]
|
|
61
|
+
return error(withCode: 5, description: localizedDescription(forErrorCode: 5), userInfo: userInfo)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
@objc public final class BGHttpEvent: NSObject {
|
|
4
|
+
|
|
5
|
+
@objc public private(set) var isSuccess: Bool = false
|
|
6
|
+
@objc public private(set) var statusCode: Int = 0
|
|
7
|
+
@objc public private(set) var requestData: String?
|
|
8
|
+
@objc public private(set) var responseText: String?
|
|
9
|
+
@objc public private(set) var error: Error?
|
|
10
|
+
|
|
11
|
+
@objc public init(statusCode: Int, requestData: String?, responseData: Data?, error: Error?) {
|
|
12
|
+
super.init()
|
|
13
|
+
self.isSuccess = (statusCode >= 200 && statusCode < 205)
|
|
14
|
+
self.statusCode = statusCode
|
|
15
|
+
self.requestData = requestData
|
|
16
|
+
if let responseData = responseData {
|
|
17
|
+
self.responseText = String(data: responseData, encoding: .utf8) ?? ""
|
|
18
|
+
} else {
|
|
19
|
+
self.responseText = ""
|
|
20
|
+
}
|
|
21
|
+
self.error = error
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@objc public func toDictionary() -> [String: Any] {
|
|
25
|
+
let dict = NSMutableDictionary()
|
|
26
|
+
dict["success"] = isSuccess
|
|
27
|
+
dict["status"] = statusCode
|
|
28
|
+
dict["responseText"] = responseText ?? ""
|
|
29
|
+
if let error = error as NSError? {
|
|
30
|
+
dict["error"] = error.localizedDescription
|
|
31
|
+
}
|
|
32
|
+
return dict as! [String: Any]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
@objc public final class BGHttpRequest: NSObject {
|
|
4
|
+
|
|
5
|
+
@objc public private(set) var records: [Any]
|
|
6
|
+
@objc public var completion: ((BGHttpRequest, BGHttpResponse) -> Void)?
|
|
7
|
+
@objc public private(set) var requestData: Any?
|
|
8
|
+
@objc public private(set) var url: URL?
|
|
9
|
+
|
|
10
|
+
@objc public static func execute(_ records: [Any], callback: @escaping (BGHttpRequest, BGHttpResponse) -> Void) -> BGHttpRequest {
|
|
11
|
+
let request = BGHttpRequest(records: records, callback: callback)
|
|
12
|
+
request.run()
|
|
13
|
+
return request
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@objc public init(records: [Any], callback: ((BGHttpRequest, BGHttpResponse) -> Void)?) {
|
|
17
|
+
self.records = records
|
|
18
|
+
super.init()
|
|
19
|
+
let urlString = BGConfig.sharedInstance().http.url
|
|
20
|
+
if !urlString.isEmpty {
|
|
21
|
+
self.url = URL(string: urlString)
|
|
22
|
+
}
|
|
23
|
+
self.completion = callback
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@objc public func run() {
|
|
27
|
+
let config = BGConfig.sharedInstance()
|
|
28
|
+
let http = config.http
|
|
29
|
+
|
|
30
|
+
guard let url = url else { return }
|
|
31
|
+
let request = NSMutableURLRequest(url: url,
|
|
32
|
+
cachePolicy: .reloadIgnoringLocalCacheData,
|
|
33
|
+
timeoutInterval: http.timeoutSeconds)
|
|
34
|
+
request.httpMethod = http.method
|
|
35
|
+
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
|
|
36
|
+
|
|
37
|
+
// Build the JSON payload from the records' "location" entries.
|
|
38
|
+
let rootProperty = http.rootProperty
|
|
39
|
+
var payload: Any
|
|
40
|
+
if http.batchSync {
|
|
41
|
+
var array: [Any] = []
|
|
42
|
+
for record in records {
|
|
43
|
+
if let dict = record as? [AnyHashable: Any], let location = dict["location"] {
|
|
44
|
+
array.append(location)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
payload = array
|
|
48
|
+
} else {
|
|
49
|
+
payload = (records.first as? [AnyHashable: Any])?["location"] ?? [:]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
var requestObject: Any
|
|
53
|
+
if rootProperty == "." {
|
|
54
|
+
requestObject = payload
|
|
55
|
+
} else {
|
|
56
|
+
requestObject = [rootProperty ?? "": payload]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Merge #params (only valid when the payload is an object).
|
|
60
|
+
let params = http.params
|
|
61
|
+
if !params.isEmpty {
|
|
62
|
+
if var dict = requestObject as? [AnyHashable: Any] {
|
|
63
|
+
dict.merge(params) { _, new in new }
|
|
64
|
+
requestObject = dict
|
|
65
|
+
} else {
|
|
66
|
+
let logger = BGLog.sharedInstance()
|
|
67
|
+
if logger.shouldLog(2) {
|
|
68
|
+
let message = "Cannot attach HTTP #params to an HTTP request with JSON data of type [Array], not an {Object]. Specify an #httpRootProperty other than '.' (eg: 'data')"
|
|
69
|
+
logger.log(2, tag: 9, function: "-[BGHttpRequest run]", message: message)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Custom headers.
|
|
75
|
+
let headers = http.headers
|
|
76
|
+
for (key, value) in headers {
|
|
77
|
+
request.setValue(String(format: "%@", "\(value)"), forHTTPHeaderField: "\(key)")
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Authorization strategy: apply auth headers if configured.
|
|
81
|
+
if let token = config.authorization.accessToken, !token.isEmpty {
|
|
82
|
+
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Serialize body.
|
|
86
|
+
var jsonError: NSError?
|
|
87
|
+
let body: Data?
|
|
88
|
+
do {
|
|
89
|
+
body = try JSONSerialization.data(withJSONObject: requestObject, options: [])
|
|
90
|
+
} catch let error as NSError {
|
|
91
|
+
jsonError = error
|
|
92
|
+
body = nil
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if let jsonError = jsonError {
|
|
96
|
+
let logger = BGLog.sharedInstance()
|
|
97
|
+
if logger.shouldLog(1) {
|
|
98
|
+
let message = String(format: "JSON error composing HTTP POST data: %@", jsonError.localizedDescription)
|
|
99
|
+
logger.log(1, tag: 0, function: "-[BGHttpRequest run]", message: message)
|
|
100
|
+
}
|
|
101
|
+
let response = BGHttpResponse(status: 200, data: nil, error: jsonError)
|
|
102
|
+
completion?(self, response)
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
guard let body = body else { return }
|
|
107
|
+
|
|
108
|
+
let logger = BGLog.sharedInstance()
|
|
109
|
+
if logger.shouldLog(1) {
|
|
110
|
+
let bodyString = String(data: body, encoding: .utf8) ?? ""
|
|
111
|
+
let message = String(format: "%@", bodyString)
|
|
112
|
+
logger.log(1, tag: 0, function: "-[BGHttpRequest run]", message: message)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
request.setValue(String(format: "%lu", body.count), forHTTPHeaderField: "Content-Length")
|
|
116
|
+
request.httpBody = body
|
|
117
|
+
|
|
118
|
+
let session = URLSession(configuration: .default)
|
|
119
|
+
let task = session.dataTask(with: request as URLRequest) { [weak self] (data, response, error) in
|
|
120
|
+
guard let self = self else { return }
|
|
121
|
+
let httpResponse = BGHttpResponse(data: data, response: response, error: error)
|
|
122
|
+
self.completion?(self, httpResponse)
|
|
123
|
+
}
|
|
124
|
+
task.resume()
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
@objc public final class BGHttpResponse: NSObject {
|
|
4
|
+
|
|
5
|
+
@objc public var error: Error?
|
|
6
|
+
@objc public var data: Data?
|
|
7
|
+
@objc public var response: URLResponse?
|
|
8
|
+
@objc public var status: Int = 0
|
|
9
|
+
|
|
10
|
+
@objc public init(data: Data?, response: URLResponse?, error: Error?) {
|
|
11
|
+
super.init()
|
|
12
|
+
self.data = data
|
|
13
|
+
self.response = response
|
|
14
|
+
self.error = error
|
|
15
|
+
if let httpResponse = response as? HTTPURLResponse {
|
|
16
|
+
self.status = httpResponse.statusCode
|
|
17
|
+
}
|
|
18
|
+
handleResponse()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@objc public init(status: Int, data: Data?, error: Error?) {
|
|
22
|
+
super.init()
|
|
23
|
+
self.data = data
|
|
24
|
+
self.response = nil
|
|
25
|
+
self.error = error
|
|
26
|
+
self.status = status
|
|
27
|
+
handleResponse()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@objc public func handleResponse() {
|
|
31
|
+
let config = BGConfig.sharedInstance()
|
|
32
|
+
var requestURL: URL? = nil
|
|
33
|
+
let urlString = config.http.url
|
|
34
|
+
if !urlString.isEmpty {
|
|
35
|
+
requestURL = URL(string: urlString)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Redirect detection (301, 302, 307, 308) → flag redirect error.
|
|
39
|
+
if [301, 302, 307, 308].contains(status) {
|
|
40
|
+
let requestUrlString = requestURL?.absoluteString ?? ""
|
|
41
|
+
let responseUrlString = (response as? HTTPURLResponse)?.url?.absoluteString ?? ""
|
|
42
|
+
if !requestUrlString.isEmpty && !responseUrlString.isEmpty {
|
|
43
|
+
self.error = NSError(domain: NSURLErrorDomain, code: status, userInfo: [NSLocalizedDescriptionKey: "Redirect from \(requestUrlString) to \(responseUrlString)"])
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// NSURLErrorUserCancelledAuthentication (-1012) → treat as 401.
|
|
48
|
+
if let err = error as NSError?, err.code == -1012 {
|
|
49
|
+
self.status = 401
|
|
50
|
+
} else if [200, 201, 204].contains(status) {
|
|
51
|
+
// Success — log and return.
|
|
52
|
+
let logger = BGLog.sharedInstance()
|
|
53
|
+
if logger.shouldLog(3) {
|
|
54
|
+
let message = String(format: "Response: %ld", status)
|
|
55
|
+
logger.log(3, tag: 1, function: "-[BGHttpResponse handleResponse]", message: message)
|
|
56
|
+
}
|
|
57
|
+
return
|
|
58
|
+
} else if status == 410 {
|
|
59
|
+
// Gone — destroy auth token for host if present.
|
|
60
|
+
if let host = requestURL?.host, !host.isEmpty,
|
|
61
|
+
let hostURL = URL(string: "https://\(host)") {
|
|
62
|
+
if TransistorAuthorizationToken.hasToken(forHost: host) {
|
|
63
|
+
TransistorAuthorizationToken.destroy(url: hostURL)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Build a descriptive error for any non-success response.
|
|
69
|
+
var bodyString = ""
|
|
70
|
+
if let data = data {
|
|
71
|
+
bodyString = String(data: data, encoding: .utf8) ?? ""
|
|
72
|
+
}
|
|
73
|
+
var message = String(format: "HTTP ERROR: %ld", status)
|
|
74
|
+
if !bodyString.isEmpty {
|
|
75
|
+
message = message + String(format: "* %@\n", bodyString)
|
|
76
|
+
}
|
|
77
|
+
if let err = error as NSError? {
|
|
78
|
+
message = message + String(format: "* %@\n*\n%@", err.localizedDescription, err.userInfo)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let logger = BGLog.sharedInstance()
|
|
82
|
+
if logger.shouldLog(2) {
|
|
83
|
+
let logMessage = String(format: "%@", message)
|
|
84
|
+
logger.log(2, tag: 9, function: "-[BGHttpResponse handleResponse]", message: logMessage)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if error == nil {
|
|
88
|
+
self.error = NSError(domain: NSURLErrorDomain,
|
|
89
|
+
code: status,
|
|
90
|
+
userInfo: [NSUnderlyingErrorKey: message])
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|