-
Notifications
You must be signed in to change notification settings - Fork 27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Offline ramp up #404
base: master
Are you sure you want to change the base?
Offline ramp up #404
Changes from 8 commits
27fc369
9206728
e2efd53
429b4ed
1a38ecd
e84a9f0
61f6b8e
eb7e5d8
decb2a8
6e54e28
e6e3c6a
65b9250
9142abf
3397fc6
c4e115b
e8522f3
d404d57
3fd5b00
c2fb45d
4015e47
c4828b8
a87cea2
28ce949
be8c1d2
bdfcc07
5ac6d23
c5572b3
b6dc7fa
9b4f71d
af9e4f7
f9f874c
aa12fd4
39af20d
25d1094
6dd7481
4f277fd
7f8c280
25e455d
fc75f16
f2baf95
3785164
85e1abe
761ef37
b1cae04
880cbe7
b6f811b
999b459
41c7a36
ac5011e
4769e5e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -228,7 +228,7 @@ - (NSDictionary *)dictionaryValue { | |
RadarCircleGeometry *circleGeometry = (RadarCircleGeometry *)self.geometry; | ||
[dict setValue:@(circleGeometry.radius) forKey:@"geometryRadius"]; | ||
[dict setValue:[circleGeometry.center dictionaryValue] forKey:@"geometryCenter"]; | ||
[dict setValue:@"Circle" forKey:@"type"]; | ||
[dict setValue:@"circle" forKey:@"type"]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we pass down the type as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I vaguely remember fixing this (maybe in RN, changing it to expect capitalization) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm it might be easier to just change the fromJson within the native SDK to accept both capitalized and non capitalized names of the shape to avoid introducing knock on effects downstream, this can help avoid introducing a breaking change. |
||
} else if ([self.geometry isKindOfClass:[RadarPolygonGeometry class]]) { | ||
RadarPolygonGeometry *polygonGeometry = (RadarPolygonGeometry *)self.geometry; | ||
[dict setValue:@(polygonGeometry.radius) forKey:@"geometryRadius"]; | ||
|
@@ -237,7 +237,7 @@ - (NSDictionary *)dictionaryValue { | |
// Nest coordinate array; Per GeoJSON spec: for type "Polygon", the "coordinates" member must be an array of LinearRing coordinate arrays. | ||
[dict setValue:@[[RadarGeofence arrayForGeometryCoordinates:polygonGeometry._coordinates]] forKey:@"coordinates"]; | ||
} | ||
[dict setValue:@"Polygon" forKey:@"type"]; | ||
[dict setValue:@"polygon" forKey:@"type"]; | ||
} | ||
|
||
return dict; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -519,6 +519,8 @@ - (void)replaceSyncedGeofences:(NSArray<RadarGeofence *> *)geofences { | |
return; | ||
} | ||
|
||
[RadarState setNearbyGeofences:geofences]; | ||
|
||
RadarTrackingOptions *options = [Radar getTrackingOptions]; | ||
NSUInteger numGeofences = MIN(geofences.count, options.beacons ? 9 : 19); | ||
NSMutableArray *requests = [NSMutableArray array]; | ||
|
@@ -585,10 +587,11 @@ - (void)replaceSyncedGeofences:(NSArray<RadarGeofence *> *)geofences { | |
} | ||
} | ||
} | ||
|
||
[RadarNotificationHelper removePendingNotificationsWithCompletionHandler: ^{ | ||
[RadarNotificationHelper addOnPremiseNotificationRequests:requests]; | ||
}]; | ||
if (NSClassFromString(@"XCTestCase") == nil) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this method is now ran in our test suite, so we need to suppress this chunk in tests as test env does not have a notification manager. |
||
[RadarNotificationHelper removePendingNotificationsWithCompletionHandler: ^{ | ||
[RadarNotificationHelper addOnPremiseNotificationRequests:requests]; | ||
}]; | ||
} | ||
} | ||
|
||
- (void)removeSyncedGeofences { | ||
|
@@ -889,8 +892,15 @@ - (void)sendLocation:(CLLocation *)location stopped:(BOOL)stopped source:(RadarL | |
completionHandler:^(RadarStatus status, NSDictionary *_Nullable res, NSArray<RadarEvent *> *_Nullable events, RadarUser *_Nullable user, | ||
NSArray<RadarGeofence *> *_Nullable nearbyGeofences, RadarConfig *_Nullable config, RadarVerifiedLocationToken *_Nullable token) { | ||
self.sending = NO; | ||
|
||
[self updateTrackingFromMeta:config.meta]; | ||
|
||
if (config != nil) { | ||
[self updateTrackingFromMeta:config.meta]; | ||
} | ||
|
||
if (status != RadarStatusSuccess || !config) { | ||
return; | ||
} | ||
|
||
[self replaceSyncedGeofences:nearbyGeofences]; | ||
}]; | ||
}; | ||
|
@@ -970,13 +980,16 @@ - (void)sendLocation:(CLLocation *)location stopped:(BOOL)stopped source:(RadarL | |
beacons:beacons | ||
completionHandler:^(RadarStatus status, NSDictionary *_Nullable res, NSArray<RadarEvent *> *_Nullable events, RadarUser *_Nullable user, | ||
NSArray<RadarGeofence *> *_Nullable nearbyGeofences, RadarConfig *_Nullable config, RadarVerifiedLocationToken *_Nullable token) { | ||
self.sending = NO; | ||
if (status != RadarStatusSuccess || !config) { | ||
return; | ||
} | ||
self.sending = NO; | ||
|
||
if (config != nil) { | ||
[self updateTrackingFromMeta:config.meta]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we should adopt the pattern of doing the check in the function that's called, like: #409 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it seems like each instance of |
||
} | ||
if (status != RadarStatusSuccess || !config) { | ||
return; | ||
} | ||
|
||
[self updateTrackingFromMeta:config.meta]; | ||
[self replaceSyncedGeofences:nearbyGeofences]; | ||
[self replaceSyncedGeofences:nearbyGeofences]; | ||
}]; | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// | ||
// Header.h | ||
// RadarSDK | ||
// | ||
// Created by Kenny Hu on 10/16/24. | ||
// Copyright © 2024 Radar Labs, Inc. All rights reserved. | ||
// | ||
#import <Foundation/Foundation.h> | ||
#import <CoreLocation/CoreLocation.h> | ||
#import "RadarConfig.h" | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
@interface RadarOfflineManager : NSObject | ||
|
||
+ (void)contextualizeLocation:(CLLocation *)location completionHandler:(void (^)(RadarConfig *))completionHandler; | ||
|
||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
// | ||
// File.swift | ||
// RadarSDK | ||
// | ||
// Created by Kenny Hu on 10/16/24. | ||
// Copyright © 2024 Radar Labs, Inc. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
import CoreLocation | ||
|
||
@objc(RadarOfflineManager) class RadarOfflineManager: NSObject { | ||
@objc public static func contextualizeLocation(_ location: CLLocation, completionHandler: @escaping (RadarConfig?) -> Void) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I find this function name to be quite opaque. I feel like this is more something in the vain of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I intentionally kept it more vague, it was my understanding that this might become a place where stuff like client side event generation and trip updates may occur? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
var newGeofenceIds = [String]() | ||
var newGeofenceTags = [String]() | ||
// get all the nearby geofences | ||
let nearbyGeofences = RadarState.nearbyGeofences() | ||
if (nearbyGeofences == nil) { | ||
return completionHandler(nil) | ||
} | ||
// for each of the nearby geofences, check if the location is inside the geofence | ||
for geofence in nearbyGeofences! { | ||
var center: RadarCoordinate? | ||
var radius: Double = 100 | ||
|
||
if let geometry = geofence.geometry as? RadarCircleGeometry { | ||
center = geometry.center | ||
radius = geometry.radius | ||
} else if let geometry = geofence.geometry as? RadarPolygonGeometry { | ||
center = geometry.center | ||
radius = geometry.radius | ||
} else { | ||
// something went wrong with the geofence geometry | ||
continue | ||
} | ||
if (isPointInsideCircle(center: center!.coordinate, radius: radius, point: location)) { | ||
newGeofenceIds.append(geofence._id) | ||
if (geofence.tag != nil) { | ||
newGeofenceTags.append(geofence.tag!) | ||
} | ||
} | ||
} | ||
RadarState.setGeofenceIds(newGeofenceIds) | ||
// if there are no rampup geofence tags, we never had ramped up so we do not need to ramp down (reason a little more about this) | ||
let rampUpGeofenceTagsOptional = RadarSettings.sdkConfiguration()?.inGeofenceTrackingOptionsTags | ||
if let rampUpGeofenceTags = rampUpGeofenceTagsOptional { | ||
let inRampedUpGeofence = !Set(rampUpGeofenceTags).isDisjoint(with: Set(newGeofenceTags)) | ||
let sdkConfig = RadarSettings.sdkConfiguration() | ||
var newTrackingOptions: RadarTrackingOptions? = nil | ||
|
||
if inRampedUpGeofence { | ||
// ramp up | ||
newTrackingOptions = sdkConfig?.inGeofenceTrackingOptions | ||
} else { | ||
// ramp down if needed | ||
if let onTripOptions = sdkConfig?.onTripTrackingOptions, | ||
let _ = Radar.getTripOptions() { | ||
newTrackingOptions = onTripOptions | ||
} else { | ||
newTrackingOptions = sdkConfig?.defaultTrackingOptions | ||
} | ||
} | ||
if (newTrackingOptions != nil) { | ||
let metaDict: [String: Any] = ["trackingOptions": newTrackingOptions?.dictionaryValue() as Any] | ||
let configDict: [String: Any] = ["meta": metaDict] | ||
if let radarConfig = RadarConfig.fromDictionary(configDict) { | ||
completionHandler(radarConfig) | ||
} | ||
} | ||
} | ||
return completionHandler(nil) | ||
} | ||
|
||
private static func isPointInsideCircle(center: CLLocationCoordinate2D, radius: Double, point: CLLocation) -> Bool { | ||
let centerLocation = CLLocation(latitude: center.latitude, longitude: center.longitude) | ||
|
||
let distance = centerLocation.distance(from: point) | ||
|
||
return distance <= radius | ||
} | ||
} |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this supposed to be empty? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. its auto-generated as we introduce swift into the repo, not removing as it may come in useful when we want to directly test swift stuff in the future and it is a pain to manually add back in these autogenerated files in xcode with all the obscure linking we need to do via the UI. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
// | ||
// Use this file to import your target's public headers that you would like to expose to Swift. | ||
// | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -74,6 +74,38 @@ - (instancetype)initWithDict:(NSDictionary *)dict { | |
if (useOpenedAppConversion && [useOpenedAppConversion isKindOfClass:[NSNumber class]]) { | ||
_useOpenedAppConversion = [(NSNumber *)useOpenedAppConversion boolValue]; | ||
} | ||
NSObject *inGeofenceTrackingOptionsObj = dict[@"inGeofenceTrackingOptions"]; | ||
_inGeofenceTrackingOptions = nil; | ||
if (inGeofenceTrackingOptionsObj && [inGeofenceTrackingOptionsObj isKindOfClass:[NSDictionary class]]) { | ||
RadarTrackingOptions *radarTrackingOptions = [RadarTrackingOptions trackingOptionsFromObject:inGeofenceTrackingOptionsObj]; | ||
if (radarTrackingOptions) { | ||
_inGeofenceTrackingOptions = radarTrackingOptions; | ||
} | ||
} | ||
|
||
NSObject *defaultTrackingOptionsObj = dict[@"defaultTrackingOptions"]; | ||
_defaultTrackingOptions = nil; | ||
if (defaultTrackingOptionsObj && [defaultTrackingOptionsObj isKindOfClass:[NSDictionary class]]) { | ||
RadarTrackingOptions *radarTrackingOptions = [RadarTrackingOptions trackingOptionsFromObject:defaultTrackingOptionsObj]; | ||
if (radarTrackingOptions) { | ||
_defaultTrackingOptions = radarTrackingOptions; | ||
} | ||
} | ||
|
||
NSObject *onTripTrackingOptionsObj = dict[@"onTripTrackingOptions"]; | ||
_onTripTrackingOptions = nil; | ||
if (onTripTrackingOptionsObj && [onTripTrackingOptionsObj isKindOfClass:[NSDictionary class]]) { | ||
RadarTrackingOptions *radarTrackingOptions = [RadarTrackingOptions trackingOptionsFromObject:onTripTrackingOptionsObj]; | ||
if (radarTrackingOptions) { | ||
_onTripTrackingOptions = radarTrackingOptions; | ||
} | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See server PR comment for an alternative struct that I think we should prefer |
||
NSObject *inGeofenceTrackingOptionsTagsObj = dict[@"inGeofenceTrackingOptionsTags"]; | ||
_inGeofenceTrackingOptionsTags = nil; | ||
if (inGeofenceTrackingOptionsTagsObj && [inGeofenceTrackingOptionsTagsObj isKindOfClass:[NSArray class]]) { | ||
_inGeofenceTrackingOptionsTags = (NSArray *)inGeofenceTrackingOptionsTagsObj; | ||
} | ||
|
||
return self; | ||
} | ||
|
@@ -90,7 +122,10 @@ - (NSDictionary *)dictionaryValue { | |
dict[@"useRadarModifiedBeacon"] = @(_useRadarModifiedBeacon); | ||
dict[@"useLocationMetadata"] = @(_useLocationMetadata); | ||
dict[@"useOpenedAppConversion"] = @(_useOpenedAppConversion); | ||
|
||
dict[@"inGeofenceTrackingOptions"] = [_inGeofenceTrackingOptions dictionaryValue]; | ||
dict[@"defaultTrackingOptions"] = [_defaultTrackingOptions dictionaryValue]; | ||
dict[@"onTripTrackingOptions"] = [_onTripTrackingOptions dictionaryValue]; | ||
dict[@"inGeofenceTrackingOptionsTags"] = _inGeofenceTrackingOptionsTags; | ||
return dict; | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if there's a network error and
replay: all
is on, we should change the log level of a network error fromerror
toinfo
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how deeply do we want to couple these offline server fallback behaviors with the idea of
replay
? Asking this as I'm already creating new flags.