Delivery and Retries
The SDK sends a request to Positive User for every ping, event and notification report. What happens when a request fails depends on the platform. This page explains what is retried, what is dropped and what your code sees.
What it is
iOS puts pings, events, screen events, product events and notification reports in a queue on the device. The queue is saved, so requests created offline or right before the app closes are sent later. A request waiting for a retry does not hold back newer requests. The logout event, the removal of the token at logout and the anonymous ping after logout are sent directly, outside the queue.
Android and Flutter send each request straight away and save only the requests that fail. Android saves failed events, screen events, product events and notification reports, but not pings. Flutter saves requests that failed because of a network error.
How it works
Results
The table shows how the iOS SDK handles each answer. Android and Flutter differ in places, see Platform differences.
| Server answer | What the SDK does |
|---|---|
| Success (2xx) | Removes the request from the queue. |
| Rejected (4xx other than 429), for example a wrong key or an invalid event | Removes the request. It would fail again. |
| Server error (5xx) or no network | Keeps the request and retries it later. |
| Rate limit (429) | Keeps the request and retries it no sooner than the server asks for in Retry-After. Before 1.3.0 the request was dropped. |
A request rejected by the firewall (HTTP 403, for example for content that looks like an attack) is also a rejection. On iOS and Flutter it is not retried and the event is lost. Android keeps it and sends it again, and the firewall blocks it again.
Checked before sending
On iOS from 1.3.0, some calls end with an error before any request: an event before the first successful ping, an invalid event or screen name, an empty product ID, and attribute values JSON cannot hold. Nothing is queued in these cases. See Errors.
Offline
On iOS, while the device has no network, nothing is sent and nothing is counted as a failed attempt. The queue starts again when the network returns.
Partly accepted events
The backend can accept an event and drop some of its attributes, for example an attribute with a value of the wrong type. The event is recorded and the request counts as a success.
Platform differences
| iOS | Android | Flutter | |
|---|---|---|---|
| Saved on the device | Every queued request | Failed events, screen events, product events and notification reports. Pings are not saved. | Requests that failed on network errors |
| Retries of server errors | Up to 4 attempts in total, also for 429, after about 30 s, 90 s and 210 s. Other requests keep going out in the meantime. The attempt count starts again after the app restarts. | Resent on every UserCom.getInstance() call, with no attempt limit | Not retried |
| Rejected requests (4xx) | Dropped, except 429 | Kept on the device and resent on every UserCom.getInstance() call, never dropped | Not retried |
| Result in your code | Completion handler on the main thread: true on success, false with a UserSDKError on rejection or after the last retry. UserSDK.errorHandler receives every error, also from calls without a completion | No result | No result |
A request waiting in the queue is not an error: its result comes when it is sent or finally dropped. A request that is still waiting when the app closes is sent after the next launch. On iOS its completion handler is not called in that case.
On iOS, logout drops the pings still waiting in the queue, and their completion reports loggedOut. Events still waiting are sent for the contact who logged out.