Parse iOS SDK: saveEventually and logout - ios

I'm writing an app using Parse which saves data to the server over a connection with unknown stability. As such I'm using saveEventually.
I'm trying to deal with the situation where the app is minimised, and then terminated by the system before a save is complete. The trouble is, when
opening the app afresh, I issue a logout command, since users are required to log in. It appears that this logout command can overtake the save commands, removing the session from the server, causing the queued save commands to fail. (I also can't see any way of handling these failures after an app restart, since I don't see any delegate based handlers, and blocks are cleared.)
I can't see any user-facing solutions to this either, since there is no guarantee when saveEventually might decide to run, and I can't block the user indefinitely.
Has anybody dealt with this? Any suggestions are most welcome.

Related

iOS background uploads - how to verify responses received if app crashes?

When performing background uploads in iOS, is there a mechanism in place that will re-deliver upload results from the system if the app crashes while handling said results? I was hoping to see some sort of confirmation system in place telling the OS that the app has finished processing all of the data it was sent, etc but I do not see something like this in the documentation.
What I am worried about is that my users will upload 100 items and once these are all uploaded in the background my app will launch (in background mode), allowing me to handle the responses. If the app crashes while processing the response data, the other unprocessed response data is forever lost now.
This is of course an issue when doing uploads in the app as well - if you upload something and crash while handling the response you'd lose that upload (and any other uploads in flight at the time), but you risk losing way too much data in the background IMO.
For an upload task in a background session, as soon as you reconnect to the existing session by “creating” a session with the same session name, your app should get delegate calls just as though it were still running from before.
Obviously you will have to persist any app-specific data structures that tie the task’s identifier and session name to the specific content you’re uploading, since anything previously in memory is obviously gone at that point.

Intermittent & Temporary iOS Keychain Failure

We have an app that heavily relies on being able to access a user's session token using iOS's Keychain. When our app opens, the first thing that's checked is whether the token is available - and if not, we show the user a login screen. We do not use any 3rd party library for this, and use Keychain's SecItemAdd() / SecItemCopyMatching() directly with the following options:
kSecClassGenericPassword
kSecAttrAccessibleAlwaysThisDeviceOnly
We see little to no issues with this during normal usage.
The Problem
We've had users reporting that upon opening their app, they're shown the login screen (suggesting Keychain could not find a value), when they were in fact logged in. We found that in this instance, we found that upon killing and relaunching the app, users were back to normal (session was found in Keychain). Once we found this, we tried added an exponential backoff to keep querying Keychain, thinking that it may had only been unavailable for the first few seconds. This did not work, and proved to us that Keychain seems to be unavailable for the entire app launch session. We are not able to reproduce this issue. It seems to happen very intermittently.
Upon further investigation, we found that commonly, users had received VoIP Pushes prior to having this issue. We're still not able to reproduce this issue reliably, but upon debugging, found that our Keychain.session was found to be nil when receiving these pushes (we rely on it there as well), which is before the user would open their app to see that they are pseudo- "logged out."
We use PushKit and PKPushRegistry in order to do VoIP Push. This requires our app to have "Background Modes: Voice over IP" enabled. We use all of this as suggested by Apple's documentation. Here's a sample:
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) {
guard let _ = Keychain.session else {
print("Session token not available")
return
}
handle(notification: payload.dictionaryPayload)
}
This code relies on Keychain.session being immediately available upon receiving a VoIP Push. As mentioned before, we've seen instances where it is not available, and so this function logs out and simply returns.
I've also found this relevant Apple forum post, suggesting that this could be an iOS bug.
Can anyone help us with this issue? Any help would be greatly appreciated. Even if this is in fact an iOS bug, we're open to workarounds.
I think this topic is driving the iOS dev community literally crazy since long time. I've read so many articles and thread about this that I lost the count.
So, I'm dealing with this issue too, and based on the #naqi answer, I started moving in that direction and I might have found a working solution.
Assumptions:
The issue seems to happen when the app is getting suspended, due any sort of reason (eg. the app in background from long time and due memory pressure the OS send it in suspended state, or the app is in bg and user upgrades to a new version from the store or automatic app update does it). I'm no longer fully sure what suspended means, but I strongly suspect that the app may get resumed/awaken without actually getting presented on the screen (due bg task, but not necessarily).
One Apple stuff member on the Apple developer forum, had the brave to reply saying something like "try to put a delay between addDidFinishLaunchingWithOptions and appDidBecomeActive". Apart from the madness here, reading that comment made me consider that maybe when the app gets resumed it triggers the addDidFinishLaunchingWithOptions but of course not the appDidBecomeActive (as it may not be resumed by the user but from any sort of event handled by the OS in background, for which we may not have the control seems, or any other feature like bg task or silent push notifications etc).
Extra info:
According with a quite old penetration test article I've found on the web (https://resources.infosecinstitute.com/iphone-penetration-testing-3/) querying the keychain actually would address the query to another process called securityd running on the device, which magically schedule the queries and performs them to the keychain db and, finally returns back the content to the requesting app, assuming that the app is still alive when the query gets completed on the keychain db. This made me think that, if that is still true, such process may fall in sort of "DDoS" attack where your app (or maybe all the apps in execution? Not clear how it actually works) performs too many queries. This may lead to false positive errors like secItemNotFound or similar. Again, as nobody confirmed this, keep it as my personal consideration.
Approach:
From the #Naqi answer, I could exclude the points 2,3 and 4, keeping the point 1 as only still valid reason. Said that, I decided to move any CRUD operation dealing with the keychain, from the addDidFinishLaunchingWithOptions to appDidBecomeActive, by handling the app setup instructions (whatever needed by your app) in there, in order to make sure that eventual background events of suspending/resuming actions won't affect the keychain at all as the appDidBecomeActive would not get called unless user actually makes the app to get opened and shown on the screen. Of course this should happen only on app launch, so you can use a bool flag or similar semaphore approach in appDidBecomeActive to make sure your setup logic is executed only once.
Combined with the above approach, In the App Delegate I also explicitly implemented application(shouldSaveApplicationState:) by returning false and application(shouldRestoreApplicationState: still returning false (Even though I assume by default should be already like this). This may not necessarily be useful but it was worth it to try too.
Summarising, on some affected devices which were randomly failing the keychain access, seems now not to happen anymore since weeks and, consider this was happening for some, almost every 2/3 days let's say, I assume it might have attenuated the issue at least.
I can say that the monitored devices were all in different configurations, with a lot of apps installed or not, with storage memory almost full or not, with a lot of apps in bg or none at all, any form factor and different iOS versions. Keychain backup to cloud not enabled, and items attribute on the keychain at least as kSecAttrAccessibleWhenUnlockedThisDeviceOnly
Open point not fully verified: Supposedly the access to keychain should be thread safe, but I found different thread on the web contradicting each other. Maybe that could somehow impact your logic and cause unexpected state
(Congrats if you managed to read up to here :) )
Hope this helps to further understand what's going on with this keychain tragedy in iOS
I was having similar issues and after talking to engineers at WWDC we identified few issues that is leading to this behavior.
We were calling Keychain too often and this can lead to a situation where it gets expensive and some calls would not finish or timeout
If an app does not provide Access Group for keychain or does not add it in save transaction with keychain then Xcode generates one but this is dynamic and can change between dev environments
If you provided an access group later in the life of your app, then your old keychain value will remain associated with the app and a new value will be created with the access group you will now provided
This also means that when you query keychain you have to provide an access group otherwise you will get a reference to what ever was found first.
Edit: formatting

iOS / HealthKit - server traffic logs tend to spike after app updates

So, I have an app that runs an HKObserverQuery in the background. We take data from HealthKit and push it up to our server for further analysis.
When we do an app update, however, we usually see a spike in requests made to our server. It usually times from when the app becomes available in the store, and usually dies down a few days later.
My question is - when an app is updated, does it try to resume a particular state or something after the installation occurs? Our data fetch/upload process is called in applicationDidFinishLaunching, and my only hunch is that after the update occurs it tries to resume a state, thus invoking that function.
Any insight in to this would be real helpful. Thanks!
EDIT: my curiosity exists because I can't imagine all of our users opening the app after an update occurs, especially the auto-updaters. So my question revolves around if iOS attempts to load the app after installing an update to verify it, henceforth calling applicationDidFinishLaunching, but without bringing the app to the foreground.

ios app startup and protected data events

We have been struggling with a sporadic issue in our iOS app for some time now, where it seems that occasionally our application will be launched and the keychain is not yet accessible. We have not found a way to reproduce this reliably.
As do many apps, we save some sensitive data (i.e., user credentials) to the keychain using kSecAttrAccessible = kSecAttrAccessibleWhenUnlocked. The app will read those values from the keychain during startup in the didFinishLaunchingWithOptions override of our app delegate. This works fine the large majority of the time.
But sometimes our users will report problems that indicate that the keychain values could not be read. If the user simply kills the app and restarts it, the values are still there and everything is working as normal. Our application does not run in the background, so I can't think of a case where it is started by anything other than a user action, which would mean the device is unlocked.
I am familiar with the application​Protected​Data​Did​Become​Available method, which is called by the OS when a device is unlocked and protected data like the keychain becomes accessible. I suppose we could check in didFinishLaunchingWithOptions if UIApplication.is​Protected​Data​Available is false, and if so defer the part of the app initialization that depends on the keychain to happen in the application​Protected​Data​Did​Become​Available method. I'm just always leery of coding a solution without being able to reproduce and test it.
Can anyone report that they have seen instances via app logs where the iOS protected data is not yet available in the didFinishLaunchingWithOptions method, but becomes available later?
With iOS 10.3.2 release I am seeing instances where it is randomly happening to some of my users as well. Our OAuth credentials are stored in Keychain which is accessed every time an API call is made. If OAuth does not exist I logout the user as we have sensitive data on the device.
I have a check for isProtectedDataAvailable on background refresh so I know it is not queuing up any API calls then.
Like you I really don't want to add a check/wait for protected data to be available on foreground. It defeats the purpose of having kSecAttrAccessibleWhenUnlocked
Here is one recent issue posted with regards to Keychain which suggests decryption time could be another culprit. https://github.com/evgenyneu/keychain-swift/issues/15

Parse.com listening for log out event

I am working on an ios app using parse as a backend.
I am having an issue where i am putting all of my save calls into a queue when i am offline and then using reachability's "kReachabilityChangedNotification" to trigger them as soon as they come online.
This usually works very well and is, for the most part, more preferable to parse's saveEventually function. However, it runs into trouble when the user is logged out before they come back online causing the save call to fail as we check for users in the before save.
So i was wondering if anyone had any ideas for how to listen for when the user is logged in/out, so i can also trigger the queue based on that.
You could check that currentUser is not null on the Client side, before the save. Also , if the user logs in and out via the app you can do that logic there.

Resources