Skip to content
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

Open
wants to merge 47 commits into
base: master
Choose a base branch
from
Open

Offline ramp up #404

wants to merge 47 commits into from

Conversation

KennyHuRadar
Copy link
Contributor

@KennyHuRadar KennyHuRadar commented Oct 16, 2024

This PR implements offline ramp ups.

  • Config calls will sync down all 3 configurations of RTOs for the user, default, on-trip and in-geofence
  • As we sync nearby geofences, we will also store nearby geofences into RadarState
  • We deviate from the tech spec by not currently storing the entire RadarUser, instead we simply use the existing geofenceIds methods from RadarState to denote the geofences we are in. It should also be noted that this would affect the results reported by users using anonymous tracks, but the behavior "makes sense".
  • if the sendLocation network call fails, we instead use the RadarOfflineManager, written in swift, to contextualize the location lat longs.
    • The RadarOfflineManager is written as a stand in for the server that has access to all the information the client has stored.
    • It takes the lat long and nearby geofences to perform point in circle calculations. From those calculation, it determines if the user is in a geofence or not.
    • It then takes the stored RTO configurations of the user to ramp up or ramp down as needed.
  • The main change in the "critical path" of the track lifecycle is that the SDK will update its RTO from config even if sendLocation returns a network error. This allows the RadarOfflineManager to ramp up or ramp down the SDK.
  • Since RTO needs to be turned on anyways to this feature to be relevant, we simply use RadarOfflineManager to override the RTO, we do not need an new fields to manage.

@KennyHuRadar KennyHuRadar changed the title Kenny/Offline ramp up [WIP] Kenny/Offline ramp up Oct 16, 2024
@@ -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"];
Copy link
Contributor Author

@KennyHuRadar KennyHuRadar Oct 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we pass down the type as circle and polygon from the server and parse it as such, however, we were setting the dict with the capitalized version, this causes issues. We need to make sure this does not impact our cross-platform SDKs.

Copy link
Contributor

Choose a reason for hiding this comment

The 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)

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.

[RadarNotificationHelper removePendingNotificationsWithCompletionHandler: ^{
[RadarNotificationHelper addOnPremiseNotificationRequests:requests];
}];
if (NSClassFromString(@"XCTestCase") == nil) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.

@KennyHuRadar KennyHuRadar marked this pull request as ready for review October 17, 2024 20:27
Copy link
Contributor

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 from error to info.

Copy link
Contributor Author

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.

Comment on lines 84 to 110
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;
}
}

Copy link
Contributor

@lmeier lmeier Oct 24, 2024

Choose a reason for hiding this comment

The 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

@KennyHuRadar KennyHuRadar changed the title [WIP] Kenny/Offline ramp up Kenny/Offline ramp up Oct 28, 2024
RadarSDK/Radar.m Outdated
}];
}
}];
if (config != nil) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we removing the status check? if (status == RadarStatusSuccess)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is one of the changes I highlighted regarding the track lifecycle. When the server track request fails, we fallback to the offline manager to act as a stand-in. In these cases, although we return a non-success status, we still receive a valid config, which the rest of the track lifecycle should process.

Therefore, instead of relying on the status code, we should check if the config is non-nil. A non-nil config only occurs either from a successful server-side track or when the offline manager handles the network error.

self.sending = NO;

if (config != nil) {
[self updateTrackingFromMeta:config.meta];
Copy link
Contributor

Choose a reason for hiding this comment

The 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

Copy link
Contributor Author

@KennyHuRadar KennyHuRadar Oct 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems like each instance of updateTrackingFromMeta in unwrapping config, I'm changing the method to updateTrackingFromConfig and capturing the `config != nil' inside the new method.

import CoreLocation

@objc(RadarOfflineManager) class RadarOfflineManager: NSObject {
@objc public static func contextualizeLocation(_ location: CLLocation, completionHandler: @escaping (RadarConfig?) -> Void) {
Copy link
Contributor

Choose a reason for hiding this comment

The 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 updateConfigFromOfflineLocation (or something in that ballpark)

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updateTrackingOptionsFromOfflineLocation

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this supposed to be empty?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.

@@ -190,4 +193,31 @@ + (BOOL)notificationPermissionGranted {
return [[NSUserDefaults standardUserDefaults] boolForKey:kNotificationPermissionGranted];
}

+ (void)setNearbyGeofences:(NSArray<RadarGeofence *> *_Nullable)nearbyGeofences {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing that makes me mildly uncomfortable is that presently I've thought of those geofences synced as CLRegions as the "nearbyGeofences". Which if I were to preserve that understanding, I'd want this to be called something else.

Maybe I / we should be thinking of those as "monitored regions"? In which case, maybe we'd (at some point? now?) want to abstract that monitoring logic into a monitorRegions function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

per offline discussion, no change required as of now.

"type": "Point"
},
"_id": "5ca7dd72208530002b30683c",
"description": "S3 Test Monk's Café",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lmao

@lmeier lmeier changed the title Kenny/Offline ramp up Offline ramp up Nov 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants