I store the information whether the user has premium subscription in the KeyChain
.
I just have a global function called doesUserHavePremium
that reads a value from the KeyChain
.
It appears that sometimes that value is read from the KeyChain
incorrectly—even though I am 100% sure it is set, it returns nil
. This happens only when the app was launched, then left for some time (probably screen got locked), and then I reentered the app again without relaunching it from scratch.
I tried the following things:
- loosening the
KeyChain
access the the least restrictive - the following piece of code to notify UI elements when
false/true
is read for that value after previously not being able to access it
let protectedDataAvailabilityNotificationName = UIApplication.protectedDataDidBecomeAvailableNotification
func observeProtectedDataAvailability() {
let selector = #selector(Self.protectedDataAvailableNotification)
switch UIApplication.shared.isProtectedDataAvailable {
case true: break
case false:
NotificationCenter.default.addObserver(self,
selector: selector,
name: protectedDataAvailabilityNotificationName,
object: nil)
}
}
@objc func protectedDataAvailableNotification(notification: NSNotification) {
NotificationCenter.default.removeObserver(self, name: protectedDataAvailabilityNotificationName, object: nil)
dataDidBecomeAvailable()
}
func dataDidBecomeAvailable() {
refreshSubscriptionStatusSubscribers()
}
But it doesn't seem to work. The problem is that I cannot debug it, because the app has to enter background in order for this problem to manifest itself.
I have read several threads related to this issue here and on GitHub and it seems like there isn't any great solution. I am looking for some workarounds. Any hints are welcome. I will try the following now:
- Try to store that value in
UserDefaults
instead—it is not really a secret like a password/token, but I need to be sure it cannot be tampered with. Not sure ifUserDefaults
suffer from the same problem. - Try to refactor to code to wrap the
doesUserHavePremium
and cache its return value in a property. That seems promising, because it never happens during fresh start thus I could always set that property on the app startup.