amplify-swift
amplify-swift copied to clipboard
PinPoint events are not cached while offline and are not sent later
Describe the bug
When my app is offline events that are recorded are not cached anywhere and are not sent when the app goes back online or in a subsequent app session (restarting it).
When debugging I got to AWSPinpointAnalyticsPlugin+ClientBehavior.swift -> flushEvents().
public func flushEvents() {
if !isEnabled {
log.warn("Cannot flushEvents. Analytics is disabled. Call Amplify.Analytics.enable() to enable")
return
}
pinpoint.submitEvents().continueWith { (task) -> Any? in
guard task.error == nil else {
// TODO: some error mapping
let error = task.error! as NSError
Amplify.Hub.dispatchFlushEvents(AnalyticsErrorHelper.getDefaultError(error))
return nil
}
if let pinpointEvents = task.result as? [AWSPinpointEvent] {
// TODO: revist this, this is exposing internal implementation
Amplify.Hub.dispatchFlushEvents(pinpointEvents)
}
return nil
}
}
If internet is down the task has an error and there are no pinpointEvents parsed from the task.result, it flushes an "error" event but ignores the rest.
As far as I can tell this is what loses the events. If there is some caching behavior I didn't see it.
I'm having trouble understanding if this is a bug or if this was never implemented.
We are porting our code from AWSMobileAnalytics and this behavior was definitely supported there. Documented in the FAQ here:
https://aws.amazon.com/mobileanalytics/faqs/
Q: Is data cached when a user’s device is offline? Yes, when using the AWS Mobile SDK, data is cached on the user’s device and is uploaded when a network connection is next established.
It's a big deal for us and I couldn't understand if this is a known regression in Amplify iOS.
By the way, it seems that Amplify Android SDK does have support for "retryable errors" (though we also found a bug in the implementation there preventing offline events from being cached).
Steps To Reproduce
Steps to reproduce the behavior:
1. Setup Amplify with pinpoint
2. Start app without internet and record events
3. Go online and record more events
4. Offline events were not sent.
Expected behavior
Expected offline events to be cached and sent later on.
Amplify Framework Version
1.28.1
Amplify Categories
Analytics, Auth
Dependency manager
Swift PM
Swift version
5.6.1
CLI version
10.0.0
Xcode version
13.4.1 13F100
Relevant log output
No response
Is this a regression?
Yes
Regression additional context
We are porting our code from AWSMobileAnalytics and this behavior was definitely supported there. Documented in the FAQ here:
https://aws.amazon.com/mobileanalytics/faqs/
Q: Is data cached when a user’s device is offline? Yes, when using the AWS Mobile SDK, data is cached on the user’s device and is uploaded when a network connection is next established.
Device
iPhone 8 Simulator / real device
iOS Version
15.5
Specific to simulators
No response
Additional context
No response
Hi @frankiesimon, thanks for opening this issue.
Amplify Analytics on iOS works like this:
- Events recorded with
Amplify.Analytics.record(event:)are cached. - After a configurable amount of time has passed (60 seconds by default), events will be submitted to Pinpoint.
- If the submission fails for an event, Amplify will try to submit it again on the next run.
Amplify will attempt to submit an event a total of 4 times (the initial attempt + 3 retries). Events that exceed that threshold will be discarded.
Currently, there is no distinction between a connectivity-related failure and any other retryable error, so if the device is offline during 4 attempts, those events will be discarded.
I've verified that as long as the connectivity is resumed before the 3rd retry, events are successfully submitted.
As this has been the behaviour from the very beginning, we will discuss among the teams and post updates in here.
In the meantime, if you need an urgent solution you can disable the automatic flush of events by adding autoFlushEventsInterval: 0 to the amplifyconfiguration.json file:
"analytics": {
"plugins": {
"awsPinpointAnalyticsPlugin": {
"pinpointAnalytics": {
"appId": "[AppId]",
"region": "[Region]"
},
"pinpointTargeting": {
"region": "[Region]"
},
"autoFlushEventsInterval": 0
}
}
}
Then you can temporary implement your own automatic submission of events that checks for the network connectivity.
For example, you can create a NWPathMonitor object:
private let networkMonitor = NWPathMonitor()
Then after calling Amplify.configure(), you can start monitoring for connectivity changes and create a Timer for flushing events:
networkMonitor.start(queue: DispatchQueue(label: "NetworkMonitor"))
Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { [weak networkMonitor] _ in
guard let networkMonitor = networkMonitor else { return }
if networkMonitor.currentPath.status == .satisfied {
Amplify.Analytics.flushEvents()
} else {
print("No connectivity, skipping.")
}
}
Thank you -- this is extremely helpful. I do believe this behavior needs to be clearly documented somewhere in the context of writing how to record events or how the flush interval affects this behavior. Could you point to where in the Amplify code this retry behavior actually happens?
I think that's it's problematic to tie the flush and offline behaviors together. In our case, one of the main reasons to move from AWSMobileAnalytics to Amplify is the ability to switch to a much-shorter-than-60-seconds interval of flushing events. With the behavior you described, while we reap the benefits of a shorter interval with online sessions we are actually punished with the offline sessions since a shorter interval leaves less chance to recover.
As for the suggested solution of calling flushEvents on our own - could you help me understand something? Will the cached events remain eligible for "flush"ing as long as the app is active? When would they be deleted/lost? If I restart the app are the events from the previous session still up for flushing or are they gone?
I confirmed the suggested solution works and cached events were sent both if 1. I went online during the session AND 2. I restarted the app after going online.
I still would like to understand if these events are persisted across sessions and what would be a reason to drop events if a user doesn't go online for a long while.
Amplify Analytics uses the AWSPinpoint SDK underneath, which defines the retry logic I mentioned. Specifically, in the AWSPinpointEventRecorder class.
Events are persisted in a local SQLite db, so they will remain available even if the app is closed. If the app is deleted however, they will be lost.
Additionally, there is a 5MB file size limit for the database: if said size is reached, the oldest event will be deleted when a new one is saved.
But since an event is deleted from the database every time it is successfully submitted to Pinpoint, it is highly unlikely that you would reach that size during the offline period.
Thank you for the reference, seeing the retrying code really helps. I think the documentation should reference this behavior in the context of recording events. Even as I know it now, I can't seem to find any relevant Google results except mentions of missing feature / bugs in the JS / Android repositories. It's probably sensible to explain in the context of either recording events or the flush interval - I believe offline events can be important to many scenarios.