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,709 @@
|
|
|
1
|
+
//
|
|
2
|
+
// BgGeolocation.mm
|
|
3
|
+
//
|
|
4
|
+
// ObjC++ TurboModule — delegates to our Swift engine (ios/engine/).
|
|
5
|
+
// No binary dependency, no billing, works in DEBUG and RELEASE.
|
|
6
|
+
//
|
|
7
|
+
#import "BgGeolocation.h"
|
|
8
|
+
// Spec import MUST be in the .mm (C++), not in the .h (which Swift module scan reads as plain C).
|
|
9
|
+
#import <BgGeolocationSpec/BgGeolocationSpec.h>
|
|
10
|
+
#import <Foundation/Foundation.h>
|
|
11
|
+
#import <CoreLocation/CoreLocation.h>
|
|
12
|
+
#import <CoreMotion/CoreMotion.h>
|
|
13
|
+
#import <UIKit/UIKit.h>
|
|
14
|
+
// These must come BEFORE BgGeolocation-Swift.h so the generated header
|
|
15
|
+
// can reference MFMailCompose and UNUserNotification types.
|
|
16
|
+
#import <MessageUI/MessageUI.h>
|
|
17
|
+
#import <UserNotifications/UserNotifications.h>
|
|
18
|
+
#import <AudioToolbox/AudioToolbox.h>
|
|
19
|
+
|
|
20
|
+
// Auto-generated ObjC bridge for all @objc Swift classes in this pod.
|
|
21
|
+
#import "BgGeolocation-Swift.h"
|
|
22
|
+
|
|
23
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
24
|
+
#import <React/RCTBridge+Private.h>
|
|
25
|
+
#import <React/RCTUtils.h>
|
|
26
|
+
using namespace facebook;
|
|
27
|
+
using namespace facebook::react;
|
|
28
|
+
#endif
|
|
29
|
+
|
|
30
|
+
// Declare NativeBgGeolocationSpec conformance here so it stays out of the header.
|
|
31
|
+
@interface BgGeolocation () <NativeBgGeolocationSpec>
|
|
32
|
+
@end
|
|
33
|
+
|
|
34
|
+
// ─── Event name constants (match src/events.ts) ──────────────────────────────
|
|
35
|
+
static NSString *const EVENT_LOCATION = @"location";
|
|
36
|
+
static NSString *const EVENT_WATCHPOSITION = @"watchposition";
|
|
37
|
+
static NSString *const EVENT_PROVIDERCHANGE = @"providerchange";
|
|
38
|
+
static NSString *const EVENT_MOTIONCHANGE = @"motionchange";
|
|
39
|
+
static NSString *const EVENT_ACTIVITYCHANGE = @"activitychange";
|
|
40
|
+
static NSString *const EVENT_GEOFENCESCHANGE = @"geofenceschange";
|
|
41
|
+
static NSString *const EVENT_HTTP = @"http";
|
|
42
|
+
static NSString *const EVENT_SCHEDULE = @"schedule";
|
|
43
|
+
static NSString *const EVENT_GEOFENCE = @"geofence";
|
|
44
|
+
static NSString *const EVENT_HEARTBEAT = @"heartbeat";
|
|
45
|
+
static NSString *const EVENT_POWERSAVECHANGE = @"powersavechange";
|
|
46
|
+
static NSString *const EVENT_CONNECTIVITYCHANGE = @"connectivitychange";
|
|
47
|
+
static NSString *const EVENT_ENABLEDCHANGE = @"enabledchange";
|
|
48
|
+
static NSString *const EVENT_NOTIFICATIONACTION = @"notificationaction";
|
|
49
|
+
static NSString *const EVENT_AUTHORIZATION = @"authorization";
|
|
50
|
+
static NSString *const EVENT_LOCATIONPUSH = @"locationpush";
|
|
51
|
+
|
|
52
|
+
// Bridges the app's background-push handler (AppDelegate) to JS and back.
|
|
53
|
+
// AppDelegate posts BACKGROUND with userInfo {requestId, locationQueryId};
|
|
54
|
+
// the module emits the JS "locationpush" event. When JS calls
|
|
55
|
+
// finishLocationPush(requestId) the module posts FINISHED so AppDelegate can
|
|
56
|
+
// invoke the stored UIBackgroundFetchResult completion handler.
|
|
57
|
+
static NSString *const NOTIF_LOCATIONPUSH_BACKGROUND = @"BGLocationPushBackground";
|
|
58
|
+
static NSString *const NOTIF_LOCATIONPUSH_FINISHED = @"BGLocationPushFinished";
|
|
59
|
+
|
|
60
|
+
@implementation BgGeolocation {
|
|
61
|
+
BOOL _ready;
|
|
62
|
+
NSInteger _watchId;
|
|
63
|
+
BGLocationManager *_engine; // our Swift engine instance
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
RCT_EXPORT_MODULE();
|
|
67
|
+
|
|
68
|
+
+ (BOOL)requiresMainQueueSetup { return YES; }
|
|
69
|
+
|
|
70
|
+
- (instancetype)init {
|
|
71
|
+
self = [super init];
|
|
72
|
+
if (self) {
|
|
73
|
+
_ready = NO;
|
|
74
|
+
_watchId = -1;
|
|
75
|
+
_engine = [BGLocationManager sharedInstance];
|
|
76
|
+
}
|
|
77
|
+
return self;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
- (NSArray<NSString *> *)supportedEvents {
|
|
81
|
+
return @[
|
|
82
|
+
EVENT_LOCATION, EVENT_WATCHPOSITION, EVENT_PROVIDERCHANGE, EVENT_MOTIONCHANGE,
|
|
83
|
+
EVENT_ACTIVITYCHANGE, EVENT_GEOFENCESCHANGE, EVENT_HTTP, EVENT_SCHEDULE,
|
|
84
|
+
EVENT_GEOFENCE, EVENT_HEARTBEAT, EVENT_POWERSAVECHANGE, EVENT_CONNECTIVITYCHANGE,
|
|
85
|
+
EVENT_ENABLEDCHANGE, EVENT_NOTIFICATIONACTION, EVENT_AUTHORIZATION,
|
|
86
|
+
EVENT_LOCATIONPUSH,
|
|
87
|
+
];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ─── Event listeners ──────────────────────────────────────────────────────────
|
|
91
|
+
|
|
92
|
+
- (void)registerEventListeners {
|
|
93
|
+
__typeof(self) __weak me = self;
|
|
94
|
+
|
|
95
|
+
[_engine onLocation:^(BGLocation *location) {
|
|
96
|
+
[me sendEventWithName:EVENT_LOCATION body:[location toDictionary]];
|
|
97
|
+
} failure:^(NSError *error) {
|
|
98
|
+
[me sendEventWithName:EVENT_LOCATION body:@{@"error": @(error.code)}];
|
|
99
|
+
}];
|
|
100
|
+
|
|
101
|
+
[_engine onMotionChange:^(BGLocation *location) {
|
|
102
|
+
[me sendEventWithName:EVENT_MOTIONCHANGE body:@{
|
|
103
|
+
@"isMoving": @(location.isMoving),
|
|
104
|
+
@"location": [location toDictionary]
|
|
105
|
+
}];
|
|
106
|
+
} failure:nil];
|
|
107
|
+
|
|
108
|
+
[_engine onActivityChange:^(BGMotionActivity *activity) {
|
|
109
|
+
[me sendEventWithName:EVENT_ACTIVITYCHANGE body:@{
|
|
110
|
+
@"activity": activity.name ?: @"unknown",
|
|
111
|
+
@"confidence": @(activity.confidence)
|
|
112
|
+
}];
|
|
113
|
+
}];
|
|
114
|
+
|
|
115
|
+
[_engine onHeartbeat:^(BGHeartbeatEvent *event) {
|
|
116
|
+
[me sendEventWithName:EVENT_HEARTBEAT body:[event toDictionary]];
|
|
117
|
+
}];
|
|
118
|
+
|
|
119
|
+
[_engine onGeofence:^(BGGeofenceEvent *event) {
|
|
120
|
+
[me sendEventWithName:EVENT_GEOFENCE body:[event toDictionary]];
|
|
121
|
+
}];
|
|
122
|
+
|
|
123
|
+
[_engine onHttp:^(NSDictionary *response) {
|
|
124
|
+
[me sendEventWithName:EVENT_HTTP body:response];
|
|
125
|
+
}];
|
|
126
|
+
|
|
127
|
+
[_engine onProviderChange:^(BGProviderChangeEvent *event) {
|
|
128
|
+
[me sendEventWithName:EVENT_PROVIDERCHANGE body:[event toDictionary]];
|
|
129
|
+
}];
|
|
130
|
+
|
|
131
|
+
[_engine onSchedule:^(BGScheduleEvent *event) {
|
|
132
|
+
// BGScheduleEvent.state is Any? — safely cast to dict or send an empty marker
|
|
133
|
+
NSDictionary *body = [event.state isKindOfClass:[NSDictionary class]]
|
|
134
|
+
? (NSDictionary *)event.state : @{};
|
|
135
|
+
[me sendEventWithName:EVENT_SCHEDULE body:body];
|
|
136
|
+
}];
|
|
137
|
+
|
|
138
|
+
[_engine onPowerSaveChange:^(BOOL isPowerSave) {
|
|
139
|
+
[me sendEventWithName:EVENT_POWERSAVECHANGE body:@(isPowerSave)];
|
|
140
|
+
}];
|
|
141
|
+
|
|
142
|
+
[_engine onConnectivityChange:^(BOOL connected) {
|
|
143
|
+
[me sendEventWithName:EVENT_CONNECTIVITYCHANGE body:@{@"connected": @(connected)}];
|
|
144
|
+
}];
|
|
145
|
+
|
|
146
|
+
[_engine onEnabledChange:^(BOOL enabled) {
|
|
147
|
+
[me sendEventWithName:EVENT_ENABLEDCHANGE body:@(enabled)];
|
|
148
|
+
}];
|
|
149
|
+
|
|
150
|
+
[_engine onAuthorization:^(BGAuthorizationEvent *event) {
|
|
151
|
+
[me sendEventWithName:EVENT_AUTHORIZATION body:[event toDictionary]];
|
|
152
|
+
}];
|
|
153
|
+
|
|
154
|
+
// Relay AppDelegate background location-pushes to JS. Registered once.
|
|
155
|
+
// Native captures the location with our engine, then hands it to JS — JS owns
|
|
156
|
+
// delivery (socket/REST). Only runs while the app process is alive; kill-state
|
|
157
|
+
// pushes are handled entirely by the LocationPushExtension.
|
|
158
|
+
static dispatch_once_t onceToken;
|
|
159
|
+
dispatch_once(&onceToken, ^{
|
|
160
|
+
[[NSNotificationCenter defaultCenter]
|
|
161
|
+
addObserverForName:NOTIF_LOCATIONPUSH_BACKGROUND
|
|
162
|
+
object:nil
|
|
163
|
+
queue:[NSOperationQueue mainQueue]
|
|
164
|
+
usingBlock:^(NSNotification *note) {
|
|
165
|
+
__strong __typeof(me) strongMe = me;
|
|
166
|
+
if (!strongMe) return;
|
|
167
|
+
NSString *requestId = note.userInfo[@"requestId"] ?: @"";
|
|
168
|
+
id queryId = note.userInfo[@"locationQueryId"] ?: [NSNull null];
|
|
169
|
+
|
|
170
|
+
BGCurrentPositionRequest *request =
|
|
171
|
+
[BGCurrentPositionRequest requestWithSuccess:^(id locationObj) {
|
|
172
|
+
BGLocation *tsLocation = (BGLocation *)locationObj;
|
|
173
|
+
CLLocation *loc = tsLocation.location;
|
|
174
|
+
NSDictionary *dict = [tsLocation toDictionary];
|
|
175
|
+
|
|
176
|
+
// Deliver NATIVELY (socket → REST) so it works even when the RN bridge
|
|
177
|
+
// isn't alive on a background/kill-state wake. JS is notified after,
|
|
178
|
+
// with delivered=YES so the host app does NOT re-send.
|
|
179
|
+
void (^afterDeliver)(BOOL) = ^(BOOL delivered) {
|
|
180
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
181
|
+
[strongMe sendEventWithName:EVENT_LOCATIONPUSH body:@{
|
|
182
|
+
@"requestId": requestId,
|
|
183
|
+
@"locationQueryId": queryId,
|
|
184
|
+
@"location": dict ?: [NSNull null],
|
|
185
|
+
@"delivered": @(delivered),
|
|
186
|
+
}];
|
|
187
|
+
// Release the app without depending on JS calling finishLocationPush.
|
|
188
|
+
[[NSNotificationCenter defaultCenter]
|
|
189
|
+
postNotificationName:NOTIF_LOCATIONPUSH_FINISHED
|
|
190
|
+
object:nil
|
|
191
|
+
userInfo:@{@"requestId": requestId}];
|
|
192
|
+
});
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
if (loc != nil) {
|
|
196
|
+
CLLocationCoordinate2D c = loc.coordinate;
|
|
197
|
+
NSString *ts = [strongMe iso8601StringFromDate:loc.timestamp];
|
|
198
|
+
[BGLocationPushDeliverer deliverWithLatitude:c.latitude
|
|
199
|
+
longitude:c.longitude
|
|
200
|
+
accuracy:MAX(loc.horizontalAccuracy, 0)
|
|
201
|
+
speed:loc.speed
|
|
202
|
+
heading:loc.course
|
|
203
|
+
altitude:loc.altitude
|
|
204
|
+
timestampISO:ts
|
|
205
|
+
queryId:(queryId == [NSNull null] ? @"" : queryId)
|
|
206
|
+
completion:^(BOOL ok) { afterDeliver(ok); }];
|
|
207
|
+
} else {
|
|
208
|
+
afterDeliver(NO);
|
|
209
|
+
}
|
|
210
|
+
} failure:^(NSInteger code) {
|
|
211
|
+
[strongMe sendEventWithName:EVENT_LOCATIONPUSH body:@{
|
|
212
|
+
@"requestId": requestId,
|
|
213
|
+
@"locationQueryId": queryId,
|
|
214
|
+
@"error": @(code),
|
|
215
|
+
@"delivered": @(NO),
|
|
216
|
+
}];
|
|
217
|
+
[[NSNotificationCenter defaultCenter]
|
|
218
|
+
postNotificationName:NOTIF_LOCATIONPUSH_FINISHED
|
|
219
|
+
object:nil
|
|
220
|
+
userInfo:@{@"requestId": requestId}];
|
|
221
|
+
}];
|
|
222
|
+
request.timeout = 20;
|
|
223
|
+
request.samples = 1;
|
|
224
|
+
request.desiredAccuracy = 10;
|
|
225
|
+
request.persist = NO;
|
|
226
|
+
[strongMe->_engine getCurrentPosition:request];
|
|
227
|
+
}];
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
#pragma mark - Core
|
|
232
|
+
|
|
233
|
+
- (void)ready:(NSDictionary *)config success:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
234
|
+
if (_ready) {
|
|
235
|
+
BOOL resetFlag = config[@"reset"] ? [config[@"reset"] boolValue] : YES;
|
|
236
|
+
if (resetFlag) [[BGConfig sharedInstance] updateWithDictionary:config];
|
|
237
|
+
success(@[[BGConfig sharedInstance].toDictionary]);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
_ready = YES;
|
|
241
|
+
|
|
242
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
243
|
+
// Set view controller now that RN window is ready
|
|
244
|
+
UIViewController *root =
|
|
245
|
+
[[[[UIApplication sharedApplication] delegate] window] rootViewController];
|
|
246
|
+
if (root) { self->_engine.viewController = root; }
|
|
247
|
+
|
|
248
|
+
@try {
|
|
249
|
+
BGConfig *cfg = [BGConfig sharedInstance];
|
|
250
|
+
BOOL resetFlag = config[@"reset"] ? [config[@"reset"] boolValue] : YES;
|
|
251
|
+
|
|
252
|
+
if (cfg.isFirstBoot) {
|
|
253
|
+
[cfg updateWithDictionary:config];
|
|
254
|
+
} else if (resetFlag) {
|
|
255
|
+
[cfg resetConfig:@{}];
|
|
256
|
+
[cfg updateWithDictionary:config];
|
|
257
|
+
} else {
|
|
258
|
+
[cfg updateWithDictionary:config];
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
[self registerEventListeners];
|
|
262
|
+
[self->_engine ready];
|
|
263
|
+
success(@[cfg.toDictionary]);
|
|
264
|
+
} @catch (NSException *e) {
|
|
265
|
+
failure(@[e.reason ?: @"ready_error"]);
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
- (void)configure:(NSDictionary *)config success:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
271
|
+
[self ready:config success:success failure:failure];
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
- (void)reset:(NSDictionary *)config success:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
275
|
+
BGConfig *cfg = [BGConfig sharedInstance];
|
|
276
|
+
@try {
|
|
277
|
+
if (config.count > 0) { [cfg resetConfig:@{}]; [cfg updateWithDictionary:config]; }
|
|
278
|
+
else { [cfg reset]; }
|
|
279
|
+
success(@[cfg.toDictionary]);
|
|
280
|
+
} @catch (NSException *e) { failure(@[e.reason ?: @"reset_error"]); }
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
- (void)setConfig:(NSDictionary *)config success:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
284
|
+
[[BGConfig sharedInstance] updateWithDictionary:config];
|
|
285
|
+
success(@[[BGConfig sharedInstance].toDictionary]);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
- (void)getState:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
289
|
+
success(@[[_engine getState]]);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
#pragma mark - Lifecycle
|
|
293
|
+
|
|
294
|
+
- (void)start:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
295
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
296
|
+
[self->_engine start];
|
|
297
|
+
success(@[[self->_engine getState]]);
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
- (void)stop:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
302
|
+
[_engine stop];
|
|
303
|
+
success(@[[_engine getState]]);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
- (void)startSchedule:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
307
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
308
|
+
[self->_engine startSchedule];
|
|
309
|
+
success(@[[self->_engine getState]]);
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
- (void)stopSchedule:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
314
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
315
|
+
[self->_engine stopSchedule];
|
|
316
|
+
success(@[[self->_engine getState]]);
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
- (void)startGeofences:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
321
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
322
|
+
[self->_engine startGeofences];
|
|
323
|
+
success(@[[BGConfig sharedInstance].toDictionary]);
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
#pragma mark - Background task
|
|
328
|
+
|
|
329
|
+
- (void)beginBackgroundTask:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
330
|
+
success(@[@([_engine createBackgroundTask])]);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
- (void)finish:(double)taskId success:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
334
|
+
[_engine stopBackgroundTask:(NSUInteger)taskId];
|
|
335
|
+
success(@[@(taskId)]);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
#pragma mark - Motion / Location
|
|
339
|
+
|
|
340
|
+
- (void)changePace:(BOOL)isMoving success:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
341
|
+
[_engine changePace:isMoving];
|
|
342
|
+
success(@[]);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
- (void)getCurrentPosition:(NSDictionary *)options success:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
346
|
+
BGCurrentPositionRequest *request = [BGCurrentPositionRequest requestWithSuccess:^(id location) {
|
|
347
|
+
success(@[[(BGLocation *)location toDictionary]]);
|
|
348
|
+
} failure:^(NSInteger code) {
|
|
349
|
+
failure(@[@(code)]);
|
|
350
|
+
}];
|
|
351
|
+
if (options[@"timeout"]) request.timeout = [options[@"timeout"] doubleValue];
|
|
352
|
+
if (options[@"maximumAge"]) request.maximumAge = [options[@"maximumAge"] doubleValue];
|
|
353
|
+
if (options[@"persist"]) request.persist = [options[@"persist"] boolValue];
|
|
354
|
+
if (options[@"samples"]) request.samples = [options[@"samples"] intValue];
|
|
355
|
+
if (options[@"desiredAccuracy"]) request.desiredAccuracy = [options[@"desiredAccuracy"] doubleValue];
|
|
356
|
+
if (options[@"extras"]) request.extras = options[@"extras"];
|
|
357
|
+
[_engine getCurrentPosition:request];
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
- (void)watchPosition:(NSDictionary *)options success:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
361
|
+
// interval is private(set) — use the factory that accepts it up front
|
|
362
|
+
double interval = options[@"interval"] ? [options[@"interval"] doubleValue] : 60000.0;
|
|
363
|
+
BGWatchPositionRequest *request = [BGWatchPositionRequest requestWithInterval:interval
|
|
364
|
+
success:^(id location) {
|
|
365
|
+
[self sendEventWithName:EVENT_WATCHPOSITION body:[(BGLocation *)location toDictionary]];
|
|
366
|
+
} failure:^(NSInteger code) {}];
|
|
367
|
+
if (options[@"desiredAccuracy"]) request.desiredAccuracy = [options[@"desiredAccuracy"] doubleValue];
|
|
368
|
+
if (options[@"persist"]) request.persist = [options[@"persist"] boolValue];
|
|
369
|
+
if (options[@"extras"]) request.extras = options[@"extras"];
|
|
370
|
+
if (options[@"timeout"]) request.timeout = [options[@"timeout"] doubleValue];
|
|
371
|
+
[_engine watchPosition:request]; _watchId = 0;
|
|
372
|
+
success(@[]);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
- (void)stopWatchPosition:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
376
|
+
if (_watchId >= 0) { [_engine stopWatchPosition:_watchId]; _watchId = -1; }
|
|
377
|
+
success(@[]);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
#pragma mark - Permissions
|
|
381
|
+
|
|
382
|
+
- (void)requestPermission:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
383
|
+
__typeof(self) __weak weakSelf = self;
|
|
384
|
+
[_engine requestPermission:^{
|
|
385
|
+
__typeof(self) __strong strongSelf = weakSelf;
|
|
386
|
+
NSDictionary *state = [strongSelf->_engine getProviderState];
|
|
387
|
+
success(@[state[@"status"] ?: @(3)]);
|
|
388
|
+
} failure:^(NSError *error) {
|
|
389
|
+
failure(@[@(error.code)]);
|
|
390
|
+
}];
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
- (void)requestMotionPermission:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
394
|
+
CMMotionActivityManager *motionManager = [CMMotionActivityManager new];
|
|
395
|
+
[motionManager queryActivityStartingFromDate:[NSDate date]
|
|
396
|
+
toDate:[NSDate date]
|
|
397
|
+
toQueue:NSOperationQueue.mainQueue
|
|
398
|
+
withHandler:^(__unused NSArray<CMMotionActivity *> *activities, __unused NSError *error) {
|
|
399
|
+
success(@[@([CMMotionActivityManager authorizationStatus])]);
|
|
400
|
+
}];
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
- (void)requestTemporaryFullAccuracy:(NSString *)purpose success:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
404
|
+
[_engine requestTemporaryFullAccuracy:purpose success:^{
|
|
405
|
+
success(@[@(2)]); // 2 = full accuracy
|
|
406
|
+
} failure:^(NSError *error) {
|
|
407
|
+
failure(@[error.localizedDescription ?: @"accuracy_error"]);
|
|
408
|
+
}];
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
- (void)getProviderState:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
412
|
+
success(@[[_engine getProviderState]]);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
#pragma mark - HTTP & Persistence
|
|
416
|
+
|
|
417
|
+
- (void)getLocations:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
418
|
+
[_engine getLocations:^(NSArray *records) { success(@[records]); }
|
|
419
|
+
failure:^(NSError *error) { failure(@[error.localizedDescription ?: @"get_locations_error"]); }];
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
- (void)getCount:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
423
|
+
success(@[@([_engine getCount])]);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
- (void)destroyLocations:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
427
|
+
[_engine destroyLocations];
|
|
428
|
+
success(@[]);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
- (void)destroyLocation:(NSString *)uuid success:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
432
|
+
[_engine destroyLocation:uuid success:^{ success(@[]); }
|
|
433
|
+
failure:^(NSError *error) { failure(@[error.localizedDescription ?: @"destroy_error"]); }];
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
- (void)insertLocation:(NSDictionary *)location success:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
437
|
+
NSDictionary *coords = location[@"coords"] ?: @{};
|
|
438
|
+
CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(
|
|
439
|
+
[coords[@"latitude"] doubleValue], [coords[@"longitude"] doubleValue]);
|
|
440
|
+
CLLocation *cl = [[CLLocation alloc] initWithCoordinate:coord
|
|
441
|
+
altitude:[coords[@"altitude"] doubleValue]
|
|
442
|
+
horizontalAccuracy:[coords[@"accuracy"] doubleValue]
|
|
443
|
+
verticalAccuracy:[coords[@"altitudeAccuracy"] doubleValue]
|
|
444
|
+
timestamp:[NSDate date]];
|
|
445
|
+
BGLocation *loc = [[BGLocation alloc] initWithLocation:cl type:@"manual" extras:location[@"extras"]];
|
|
446
|
+
[_engine insertLocation:loc success:^(BGLocation *l) { success(@[[l uuid]]); }
|
|
447
|
+
failure:^(NSError *error) { failure(@[error.localizedDescription ?: @"insert_error"]); }];
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
- (void)sync:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
451
|
+
[_engine sync:^(NSArray *records) { success(@[records]); }
|
|
452
|
+
failure:^(NSError *error) { failure(@[error.localizedDescription ?: @"sync_error"]); }];
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
#pragma mark - Odometer
|
|
456
|
+
|
|
457
|
+
- (void)getOdometer:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
458
|
+
success(@[@([_engine getOdometer])]);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
- (void)setOdometer:(double)value success:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
462
|
+
BGCurrentPositionRequest *request = [BGCurrentPositionRequest requestWithSuccess:^(id location) {
|
|
463
|
+
success(@[[(BGLocation *)location toDictionary]]);
|
|
464
|
+
} failure:^(NSInteger code) {
|
|
465
|
+
failure(@[@(code)]);
|
|
466
|
+
}];
|
|
467
|
+
[_engine setOdometer:value request:request];
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
#pragma mark - Geofences
|
|
471
|
+
|
|
472
|
+
- (void)addGeofence:(NSDictionary *)config success:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
473
|
+
BGGeofence *gf = [self buildGeofence:config];
|
|
474
|
+
if (!gf) { failure(@[@"Invalid geofence data"]); return; }
|
|
475
|
+
[_engine addGeofence:gf success:^{ success(@[]); }
|
|
476
|
+
failure:^(NSError *error) { failure(@[error.localizedDescription ?: @"geofence_error"]); }];
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
- (void)addGeofences:(NSArray *)geofences success:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
480
|
+
NSMutableArray *list = [NSMutableArray new];
|
|
481
|
+
for (NSDictionary *params in geofences) {
|
|
482
|
+
BGGeofence *gf = [self buildGeofence:params];
|
|
483
|
+
if (!gf) { failure(@[@"Invalid geofence data"]); return; }
|
|
484
|
+
[list addObject:gf];
|
|
485
|
+
}
|
|
486
|
+
[_engine addGeofences:list success:^{ success(@[]); }
|
|
487
|
+
failure:^(NSError *error) { failure(@[error.localizedDescription ?: @"geofences_error"]); }];
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
- (BGGeofence *)buildGeofence:(NSDictionary *)params {
|
|
491
|
+
if (!params[@"identifier"]) return nil;
|
|
492
|
+
return [BGGeofence circleWithIdentifier:params[@"identifier"]
|
|
493
|
+
radius:[params[@"radius"] doubleValue]
|
|
494
|
+
latitude:[params[@"latitude"] doubleValue]
|
|
495
|
+
longitude:[params[@"longitude"] doubleValue]
|
|
496
|
+
notifyOnEntry:params[@"notifyOnEntry"] ? [params[@"notifyOnEntry"] boolValue] : YES
|
|
497
|
+
notifyOnExit:params[@"notifyOnExit"] ? [params[@"notifyOnExit"] boolValue] : YES
|
|
498
|
+
notifyOnDwell:params[@"notifyOnDwell"] ? [params[@"notifyOnDwell"] boolValue] : NO
|
|
499
|
+
loiteringDelay:params[@"loiteringDelay"] ? [params[@"loiteringDelay"] doubleValue] : 0
|
|
500
|
+
extras:params[@"extras"]];
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
- (void)removeGeofence:(NSString *)identifier success:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
504
|
+
[_engine removeGeofence:identifier success:^{ success(@[]); }
|
|
505
|
+
failure:^(NSError *error) { failure(@[error.localizedDescription ?: @"remove_geofence_error"]); }];
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
- (void)removeGeofences:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
509
|
+
[_engine removeGeofences:@[] success:^{ success(@[]); }
|
|
510
|
+
failure:^(NSError *error) { failure(@[error.localizedDescription ?: @"remove_geofences_error"]); }];
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
- (void)getGeofences:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
514
|
+
[_engine getGeofences:^(NSArray *geofences) {
|
|
515
|
+
NSMutableArray *result = [NSMutableArray new];
|
|
516
|
+
for (BGGeofence *g in geofences) [result addObject:[g toDictionary]];
|
|
517
|
+
success(@[result]);
|
|
518
|
+
} failure:^(NSError *error) { failure(@[error.localizedDescription ?: @"get_geofences_error"]); }];
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
- (void)getGeofence:(NSString *)identifier success:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
522
|
+
[_engine getGeofence:identifier success:^(BGGeofence *g) { success(@[[g toDictionary]]); }
|
|
523
|
+
failure:^(NSError *error) { failure(@[error.localizedDescription ?: @"get_geofence_error"]); }];
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
- (void)geofenceExists:(NSString *)identifier callback:(RCTResponseSenderBlock)callback {
|
|
527
|
+
[_engine geofenceExists:identifier callback:^(BOOL exists) { callback(@[@(exists)]); }];
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
#pragma mark - Logging
|
|
531
|
+
|
|
532
|
+
- (void)log:(NSString *)level message:(NSString *)message {
|
|
533
|
+
[_engine log:level message:message];
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
- (void)setLogLevel:(double)value success:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
537
|
+
[[BGConfig sharedInstance] updateWithDictionary:@{@"logLevel": @(value)}];
|
|
538
|
+
success(@[[BGConfig sharedInstance].toDictionary]);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
- (void)getLog:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
542
|
+
LogQuery *query = [[LogQuery alloc] init];
|
|
543
|
+
[_engine getLog:query success:^(NSDictionary *result) {
|
|
544
|
+
// Convert log entries array to JSON string for JS compatibility
|
|
545
|
+
NSArray *entries = result[@"log"] ?: @[];
|
|
546
|
+
NSData *data = [NSJSONSerialization dataWithJSONObject:entries options:0 error:nil];
|
|
547
|
+
NSString *json = data ? [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] : @"[]";
|
|
548
|
+
success(@[json]);
|
|
549
|
+
} failure:^(NSError *error) {
|
|
550
|
+
failure(@[error.localizedDescription ?: @"get_log_error"]);
|
|
551
|
+
}];
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
- (void)destroyLog:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
555
|
+
[_engine destroyLog];
|
|
556
|
+
success(@[]);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
- (void)emailLog:(NSString *)email success:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
560
|
+
[_engine emailLog:email success:^{ success(@[]); }
|
|
561
|
+
failure:^(NSError *error) { failure(@[error.localizedDescription ?: @"email_log_error"]); }];
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
#pragma mark - Utility
|
|
565
|
+
|
|
566
|
+
- (void)isPowerSaveMode:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
567
|
+
success(@[@([_engine isPowerSaveMode])]);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
- (void)getSensors:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
571
|
+
success(@[@{
|
|
572
|
+
@"platform": @"ios",
|
|
573
|
+
@"accelerometer": @([_engine isAccelerometerAvailable]),
|
|
574
|
+
@"gyroscope": @([_engine isGyroAvailable]),
|
|
575
|
+
@"magnetometer": @([_engine isMagnetometerAvailable]),
|
|
576
|
+
@"motionHardware": @([_engine isMotionHardwareAvailable]),
|
|
577
|
+
@"motionAuthorizationStatus": @([CMMotionActivityManager authorizationStatus]),
|
|
578
|
+
}]);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
- (void)getDeviceInfo:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
582
|
+
success(@[@{
|
|
583
|
+
@"platform": @"ios",
|
|
584
|
+
@"manufacturer": @"Apple",
|
|
585
|
+
@"model": [[UIDevice currentDevice] model],
|
|
586
|
+
@"version": [[UIDevice currentDevice] systemVersion],
|
|
587
|
+
@"framework": @"react-native",
|
|
588
|
+
}]);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
- (void)getLocationPushToken:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
592
|
+
NSString *token = [[NSUserDefaults standardUserDefaults]
|
|
593
|
+
stringForKey:@"BGLocationManager_locationPushToken"];
|
|
594
|
+
success(@[token ?: [NSNull null]]);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
- (void)getApnsDeviceToken:(RCTResponseSenderBlock)success failure:(RCTResponseSenderBlock)failure {
|
|
598
|
+
NSString *token = [[NSUserDefaults standardUserDefaults]
|
|
599
|
+
stringForKey:@"BGLocationManager_apnsDeviceToken"];
|
|
600
|
+
success(@[token ?: [NSNull null]]);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
- (void)setLocationPushConfig:(NSDictionary *)config
|
|
604
|
+
success:(RCTResponseSenderBlock)success
|
|
605
|
+
failure:(RCTResponseSenderBlock)failure {
|
|
606
|
+
[_engine setLocationPushConfig:(config ?: @{})];
|
|
607
|
+
success(@[]);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
- (NSString *)iso8601StringFromDate:(NSDate *)date {
|
|
611
|
+
static NSISO8601DateFormatter *formatter;
|
|
612
|
+
static dispatch_once_t once;
|
|
613
|
+
dispatch_once(&once, ^{
|
|
614
|
+
formatter = [[NSISO8601DateFormatter alloc] init];
|
|
615
|
+
formatter.formatOptions = NSISO8601DateFormatWithInternetDateTime |
|
|
616
|
+
NSISO8601DateFormatWithFractionalSeconds;
|
|
617
|
+
});
|
|
618
|
+
return [formatter stringFromDate:(date ?: [NSDate date])];
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
- (void)finishLocationPush:(NSString *)requestId
|
|
622
|
+
success:(RCTResponseSenderBlock)success
|
|
623
|
+
failure:(RCTResponseSenderBlock)failure {
|
|
624
|
+
[[NSNotificationCenter defaultCenter]
|
|
625
|
+
postNotificationName:NOTIF_LOCATIONPUSH_FINISHED
|
|
626
|
+
object:nil
|
|
627
|
+
userInfo:@{@"requestId": requestId ?: @""}];
|
|
628
|
+
success(@[]);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
- (void)playSound:(double)soundId {
|
|
632
|
+
[_engine playSound:(SystemSoundID)soundId];
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
#pragma mark - Lifecycle
|
|
636
|
+
|
|
637
|
+
- (void)dealloc {
|
|
638
|
+
[_engine removeListeners];
|
|
639
|
+
_engine = nil;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
643
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
644
|
+
(const facebook::react::ObjCTurboModule::InitParams &)params {
|
|
645
|
+
return std::make_shared<facebook::react::NativeBgGeolocationSpecJSI>(params);
|
|
646
|
+
}
|
|
647
|
+
#endif
|
|
648
|
+
|
|
649
|
+
@end
|
|
650
|
+
|
|
651
|
+
// ─── Native kill-state / background launch bootstrap ─────────────────────────
|
|
652
|
+
// A TurboModule is instantiated LAZILY — only when JS first touches it. On an
|
|
653
|
+
// iOS location relaunch after system termination (significant-location-change
|
|
654
|
+
// or stationary-region exit), the OS launches the app in the background and expects the queued Core
|
|
655
|
+
// Location event to be delivered to a CLLocationManager that, at that instant,
|
|
656
|
+
// does not exist yet. Waiting for the React Native bridge + JS bundle to boot
|
|
657
|
+
// and import this module often loses the brief background wake window.
|
|
658
|
+
//
|
|
659
|
+
// This standalone class eagerly constructs the engine at class-load time and
|
|
660
|
+
// also observes UIApplicationDidFinishLaunchingNotification — recreating the
|
|
661
|
+
// CLLocationManager, installing the BGCLRouter delegate, and (via auto-resume)
|
|
662
|
+
// re-arming SLC/region + native HTTP delivery — independently of React Native.
|
|
663
|
+
// (It lives in its own class because BgGeolocation's +load is already provided
|
|
664
|
+
// by the RCT_EXPORT_MODULE() macro, so a second +load there would collide.)
|
|
665
|
+
@interface BGLaunchBootstrap : NSObject
|
|
666
|
+
@end
|
|
667
|
+
|
|
668
|
+
@implementation BGLaunchBootstrap
|
|
669
|
+
|
|
670
|
+
+ (void)bootstrapFromNotification:(NSNotification *)note phase:(NSString *)phase {
|
|
671
|
+
BOOL launchedForLocation =
|
|
672
|
+
note.userInfo[UIApplicationLaunchOptionsLocationKey] != nil;
|
|
673
|
+
BOOL inBackground =
|
|
674
|
+
[UIApplication sharedApplication].applicationState == UIApplicationStateBackground;
|
|
675
|
+
if (launchedForLocation || inBackground) {
|
|
676
|
+
[[NSUserDefaults standardUserDefaults] setBool:YES
|
|
677
|
+
forKey:@"BGLocationManager_didLaunchInBackground"];
|
|
678
|
+
[[NSUserDefaults standardUserDefaults] synchronize];
|
|
679
|
+
}
|
|
680
|
+
// Diagnostic marker — visible in the iOS unified log (`log show` /
|
|
681
|
+
// `log collect`) even for a location-triggered kill-state relaunch with no JS.
|
|
682
|
+
NSLog(@"[BGGEO] %@Launching: launchedForLocation=%d inBackground=%d",
|
|
683
|
+
phase, launchedForLocation, inBackground);
|
|
684
|
+
// Bootstrap the engine now, before/independent of the RN bridge. The
|
|
685
|
+
// singleton's setupCoreLocation runs auto-resume when tracking was persisted
|
|
686
|
+
// enabled; on a normal foreground launch with tracking disabled this just
|
|
687
|
+
// creates + configures the (idle) manager, which is harmless.
|
|
688
|
+
BGLocationManager *manager = [BGLocationManager sharedInstance];
|
|
689
|
+
if (launchedForLocation || inBackground) {
|
|
690
|
+
[manager ready];
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
+ (void)load {
|
|
695
|
+
// For a previously-enabled tracker, persisted config is enough to resume the
|
|
696
|
+
// engine before React Native or the application delegate has finished booting.
|
|
697
|
+
// CLLocationManager is created on the main thread by BGLocationManager.
|
|
698
|
+
(void)[BGLocationManager sharedInstance];
|
|
699
|
+
|
|
700
|
+
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
|
|
701
|
+
[center addObserverForName:UIApplicationDidFinishLaunchingNotification
|
|
702
|
+
object:nil
|
|
703
|
+
queue:[NSOperationQueue mainQueue]
|
|
704
|
+
usingBlock:^(NSNotification *note) {
|
|
705
|
+
[self bootstrapFromNotification:note phase:@"didFinish"];
|
|
706
|
+
}];
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
@end
|