Hi,
I've seen this bug come and go (last week I could regenerate it all day, and today - nothing...)
While observing the notifications UIApplicationProtectedDataWillBecomeUnavailable and UIApplicationProtectedDataDidBecomeAvailable the behavior seems to me somewhat peculiar and that might, maybe, hmmm... give some clue.
When the application is in the foreground and the device becomes locked (can be via the lock key or just enough time passes), then UIApplicationProtectedDataWillBecomeUnavailable is NOT called. additionally, the keychain is not locked at this stage.
I'm testing if the keychain is unlocked by adding a key with UnlockedThisDeviceOnly access
So now we have the app in the background, it goes there thinking that the keychain is unlocked, and since it does not get the notification it thinks the device is unlocked too.
When the app come back, then first we get the UIApplicationProtectedDataDidBecomeAvailable notification (twice) and the keychain is unlocked, then applicationWillEnterForeground and applicationDidBecomeAvailable.
1st question - why isn't the WillBecomeUnavailable event called at the time of occurance?
2nd question - why isn't there an event DID become unavailable - is the app unavailable when it's locked?
3rdd question - when does the keychain actually get locked? there's a time difference between willBecomeUnavailable and the actual keychain lock
NOW, in my app there's a timer to test that when X time passes the user gets a session timeout.
So when the app comes back from background the timer is called and sends an internal "logout" notification.
When this notification is sent, the app perfoms logout and reloads the data from the keychain.
And this is the crux.
My assumptions is that since notifications are asynchroneous, if the logout notification is received BEFORE the DidBecomeAvailable notification, then I will not read the data.
Moreover, if for example the app is awaken by a post notification, then the same thing can occur.
And in a widget the notifications are definitely not working properly, or rather - the widgets are available when the device is locked so there should certainly be a didBecomeLocked event in this case or a synchroneous query
Conclusion:
I think that in addition to the lock state notification, there should be a synchroneous lock state query, and the keychain should work with both this synchroneous query and the notification to make sure to fix mismatch.
Adding an item to the keychain to see if it's locked is a bit ugly... 🙂