Which method is better to decide which user is logged in?
let ubiquityIdentityToken = NSFileManager.defaultManager().ubiquityIdentityToken
returns a token, and client can compare wether it is the same than last time. The advantage that it returns token if device is offline too.
accountStatusWithCompletionHandler returns only a status value, but not an ID or token about who is logged in. So in offline mode it is useless.
Am I right?
My other problem, that sometimes even user is logged in and online ubiquityIdentityToken returns nil.
How do you retrieve user ID at launch?
If you're using CloudKit then the CKContainer accountStatusWithCompletionHandler method is how you should check whether the user is logged into iCloud or not (supported since iOS 8.0). The CloudKit Quick Start shows an example of how to use it.
fetchUserRecordIDWithCompletionHandler is how you should get the user's record ID, which is scoped to that CloudKit container but the same for that iCloud account across devices.
In iOS 9.0, you'll also have CKAccountChangeNotification, which will notify your app when the iCloud status on the device changes.
Related
I'm looking for a way to access the user's login state on an iOS app when it is in the Not Running(not launched).
For the iPhone-app, on sign-in and sign-out, a Userdefault value is updated
UserDefaults.standard.set(self.isLogin, forKey: UserDefaults.UserDefaultsKeys.isLogin.rawValue)
Currently, there is a session in place to send a message to watch when both (iPhone and Watch) are in an active state based on the user's login state (true/false)
session.sendMessage(["isLogin" : false], replyHandler: nil)
The target is to ensure that if a user is signed out of iPhone-app when the watch is not in range, and once the watch is in close proximity, the watch should get the user login state even if the iPhone app is in a "Not Running" state.
Consider WCSession/updateApplicationContext
The system sends context data when the opportunity arises, with the
goal of having the data ready to use by the time the counterpart wakes
up.
Auth.auth().signIn(withEmail:
In my case, When I first time open app and sign in firebase account like above code
then I reopen app
let user = Auth.auth().currentUser
user is not nil and then I reopen app get currentUser again user is nil that mean I did not sign in
I have the other app do like this, but the user always not nil.
I wonder why this happens.
Firebase automatically restores the authenticated user when the app restarts. But this requires a call to the servers, which may take some time. If you check Auth.auth().currentUser while the check is still in progress, you will get nil as the result.
To ensure you get the value once this initial check is done, use an auth state listener as shown in the first snippet in the documentation on getting the current user. From there:
Auth.auth().addStateDidChangeListener { auth, user in
// ...
}
Once this listener fires for the first time:
either the user variable will have a value if the user authentication state could be restored,
or it is nil if the user was not signed in last time the app ran, or the authentication state couldn't be restored (for example, because the account is disabled, or the password was changed).
I think it a big possibility you might be signed in on another device or signed in a different profile and didn't know, you might have some sync problems or security issues that did a safety measurement until you fix the security issue
When a user logs out, I do:
FIRInstanceID.instanceID().delete(handler: { (error) in })
Which should (?) invalidate the token and unsubscribe from all topics.
It works, but logging in with the same device and calling FIRInstanceID.instanceID().token() (from notification when it's ready), I get the same token (not really a problem, but unexpected). However; subscribing to topics (upon login for instance) seems to be cached in the device from the previous login, so it doesn't make a network call, meaning the token will not be associated with any topics on the FCM side. Assume it's the same user doing a relog; the topics he/she wants to subscribe to are the same as before delete().
I can verify this by querying https://iid.googleapis.com/iid/info/<token> with the token: Even after calling subscribeToTopic in my app, the list of topics remains empty. Normally this call results in the -5 error (described here https://github.com/firebase/quickstart-ios/issues/146) when subscribing to multiple topics, but nothing happens, indicating to me that the application thinks it's already subscribed to the topics and hence does nothing.
So - how can I unsubscribe from all topics upon logout, and successfully resubscribe when logging in? Looping the topics and doing unsubscribeFromTopic seems a little hacky to me.
Instead you can disconnect the user from getting push notifications by doing FiRMessaging.messaging.disconnect()
And connect the user when he logs back in
I understand that by default Firebase invalidates a login token after 24 hours. However, I am finding the behavior strange after this time period. When the app is run it checks to see if the user is logged in and if so it goes into the app otherwise it stays on the login screen:
if self.ref.authData != nil
{
self.performSegueWithIdentifier("mainTabSegue", sender: self)
}
This works fine unless the token has expired after 24 hours. What will happen then is that the app will still see that authData is not nil and it will send it to the next VC. The next VC makes uses of the UID which then causes the app to crash. Running the app again will then show that authData is in fact nil and the user will be asked to login as is expected.
So the question is why, after the 24 hour period, is authData not nil when the user is clearly not logged in? The Firebase documentation seems to indicate that checking authData as above is the correct way to determine whether a user is logged in.
Before your segue if you extract the uid from authData then you can pass that user to the first view controller. If you pass the user object to your first VC constructed in the App Delegate, then your app won't crash. I vaguely remember something similar happening to my app (i.e. where it thinks the user is logged in but then changes back to login). I'm not sure why this happened but it's possible the app tried to start where it left off?
You can also change the token expiration length on Firebase, as you may know.
I'm creating a cloudkit app, and have been trying multiple ways to get the NSUbiquityIdentityDidChangeNotification, but I never am able to get this notification.
I've tried both of these code versions under the delegate didFinish and the viewDidLoad methods. And I tried calling it from another notification - UIApplicationDidBecomeActiveNotification. I also put import Foundation at top of files.
Here's the basic code I've tried:
NSNotificationCenter.defaultCenter().addObserver(self,
selector: "handleIdentityChanged:",
name: NSUbiquityIdentityDidChangeNotification,
object: nil)
// And this one I tried too from another post here on SO:
var localeChangeObserver = NSNotificationCenter.defaultCenter().addObserverForName(NSUbiquityIdentityDidChangeNotification, object: nil, queue: NSOperationQueue.mainQueue()) { _ in
println("The user’s iCloud login changed: should refresh all user data.")
}
Does anyone know how to get this notification to work for only a cloudkit app in swift? I really just want to detect the iCloud status change and then initiate fetching the userID if there's been a change.
Not that I need to access the ubiquityIdentityToken, but I was wondering why not store the token and every-time the app starts compare the current token with the one in local storage to see if it's a different account or nil? Therefore, why is getting the notification necessary?
Also, the code for getting the token only seems to work if I turn on "iCloud Documents", which I don't need. Does anyone know the implications of having that turned on for a social app that doesn't need it? And is there another way to get the token without enabling iCloud Documents?
This is the code I used to get token and placed in the delegate didFinish method, but only works if iCloud documents is turned on:
var token = NSFileManager.defaultManager().ubiquityIdentityToken
println("token is \(token!)")
On iOS, when I sign out of iCloud, my app is killed. So there seems not really to be a need to receive a NSUbiquityIdentityDidChangeNotification. Like you have said, it seems to be sufficient to compare the current token to the saved token.
On the Apple TV though, my app was not killed when I logged out of iCloud. Here I had noticed the notification was not fired, like you described. Since the app is not killed, a notification would be in order. (Did Apple forget to kill apps on Apple TV when iCloud account is changed?)
With Apple TV there is no iCloud documents container available (unless I explicitly share one from an iOS app). I found that on the dev center website, for the app identifier, iCloud was shown as "Configurable" and not "Enabled" if no document container was selected. I wonder if this has an effect on receiving notifications.
Both on the Apple TV and iOS, I can also confirm that the iCloud token is nil when not using documents (here: key-value-store only). Now that makes it difficult for Apple TV apps (because the app is not killed on iCloud account change, like on iOS) to detect account changes.
I have just noticed that my Apple TV app does received several NSUbiquitousKeyValueStoreDidChangeExternallyNotification when I log into another iCloud account, to reflect the changes. I guess this is as good as it gets. These notifications come with the NSUbiquitousKeyValueStoreChangeReasonKey key in userInfo, and a value of NSUbiquitousKeyValueStoreAccountChange indicates the account has changed.
Sorry for not being able to provide a direct solution, maybe it helped to share my experience.
To be notified in iOS when a user logs in or out of iCloud while using your app, use CKAccountChangedNotification instead of NSUbiquityIdentityChanged notification.
(Longer explanation: https://stackoverflow.com/a/38689094/54423.)