onDisconnectRemoveValue doesn't delete (key: value) after log out firebase? - ios

I am using FBSDKLoginManager().logOut() and try! FIRAuth.auth()!.signOut() for log out account in Swift. But onDisconnectRemoveValue doesn't remove (key: true) from Firebase "online/key" child node. Should I remove this key manually or what's wrong with this case?

If you CMD+CLICK on onDisconnectRemoveValue you will be navigated to the firebase documentation , :-
Ensure the data at this location is removed when
the client is disconnected (due to closing the app, navigating to a new page, or network issues).
So when you log out the user the network link to firebase is still alive, you haven't closed your app yet and neither have you navigated to another app leaving your firebase app in background.
So, try using this:-
FIRDatabase.database().reference().child("your_Path").updateChildValues(["online" : false], withCompletionBlock: {(err, ref) in
if err == nil{
// Sign out your user
}
})

Related

Firebase Dynamiclinks is unable to parse universal link

I have an app on TestFlight that I'm trying to setup passwordless signin using emailid from FireBase.
I've done the following:
Enabled Passwordless sign in on Firebase console
Created a Dynamic link on Firebase.
httpsdotslashslash<custom domain>/login/?link=https://<custom domain>/login&isi=<app id from itunes connect. Looks like this: 167*******>&ibi=<bundleid>
Confirmed that https://<custom domain>/apple-app-site-association returns correctly. Note that I'm using Firebase hosting to host this site. And have confirmed that https://<firebase-projectid>.firebase.app/apple-app-site-association also returns the above. i.e. custom domain is correctly resolving to this firebase hosting url.
In Info, Added a url type with identifier 'appLink' and 'url scheme'
Ensured that my GoogleService-Info is current
Enabled Associated Domains in 'Signing and Capabilities' in my project Target under 'All' (not just Debug or Release)
Added applinks:<customdomain> and activitycontinuation:<customdomain>
In my 'SceneDelegate', I'm handling the dynamic link with
private func handleIncomingDynamicLink(_ incomingURL: URL) {
DynamicLinks.dynamicLinks().handleUniversalLink(incomingURL) { dynamicLink, error in
guard error == nil else {
return print("ⓧ Error in \(#function): \(error!.localizedDescription)")
}
guard let link = dynamicLink?.url?.absoluteString else { return }
if Auth.auth().isSignIn(withEmailLink: link) {
// Save the link as it will be used in the next step to complete login
UserDefaults.standard.set(link, forKey: "Link")
}
}
}
In my viewcontroller, have copied the code from Firebase's quickstart PasswordlessViewController.swift as is.
Observed behavior:
The email link is being sent. Here's what it looks like though. Not sure if this is right.
httpsdotslashslash<custom domain>/?link=https://<firebase-projectid>.firebaseapp.com/__/auth/action?apiKey=<web api key>&mode=signIn&oobCode=25CRdL7n3qXNatDT2nEypR9UWy17E_G0ChlmqrkpznsAAAGGLYK3jQ&continueUrl=https://<custom domain>/login&lang=en&ibi=<bundleid>&ifl=https://<firebase-projectid>.firebaseapp.com/__/auth/action?apiKey=<web api key>&mode=signIn&oobCode=25CRdL7n3qXNatDT2nEypR9UWy17E_G0ChlmqrkpznsAAAGGLYK3jQ&continueUrl=https://<custom domain>/login&lang=en
Clicking on this url in my email correctly opens my app. handleIncomingDynamicLink() is also called correctly. But it throws the error ⓧ Error in handleIncomingDynamicLink(_:): The operation couldn’t be completed. Universal link URL could not be parsed by Dynamic Links.
Have tried a bunch of things.
But, unable to proceed beyond this stage. Not sure what is wrong with my link. What is it supposed to look like when behaving correctly? Should i be doing something with the oobCode? Why are there two redirections?
Happy to assist with any other information. Thank you!
I was able to solve it by adding all my url prefixes to info.plist.
<key>FirebaseDynamicLinksCustomDomains</key>
<array>
<string>https://<custom domain>/login</string>
<string>https://<custom domain>/</string>
</array>
More details at https://firebase.google.com/docs/dynamic-links/custom-domains#console

Firebase Realtime Database Connection Refresh

I have two controllers one is login and another is tableView Controller where I am displaying users information.
When app starts it first checks if user is logged in or not.
If user is logged in, it will fetch users data from firebase realtime database and display in tableView. If not then it will show login controller where user have to login using email and password.
Now once i logged in i can see users data into TableViewController from firebase, But if i closed app without log out and open it after some time like 20 min later or next day when the app launch it check if user is logged in or not in my case it shows logged in and display tableViewController but it is empty it does not fetch users data from firebase realtime database.
If i will logged out and login again then i can see users data coming from firebase but if i close app and open it again after sometime it will not show any data into my tableViewController.
I dont want users to login every time in application to get firebase data if user is logged in it must show the data from firebase real time database.
Following is code to check if user is logged in or not
if Auth.auth().currentUser?.uid == nil{
print("Logout call")
perform(#selector(handleLogout), with: nil, afterDelay: 0)
}else{
print("User Logged In")
fethUserAndSetupNavbarTitle()
}
following code will get data from firebase realtime database.
guard let uid = Auth.auth().currentUser?.uid else {
return
}
let dbReference = Database.database().reference().child("Users").child(uid)
dbReference.observeSingleEvent(of: .value, with: { (snapshot)
in
if let dictionary = snapshot.value as? [String:AnyObject] {
let user = Users(dictionary: dictionary)
print("setup navbar called")
self.setupNavBarWithUser(user)
}
}, withCancel: { (error) in
print(error.localizedDescription)
return
})
Please let me know if i am unable to describe the problem.
Thank you in advance.
I might know what the problem is.
The Firebase Token gets disabled after one hour or so.
You need to enable Token Service API to allow refreshing the token once it expires.
Go to https://console.developers.google.com/ , click onto "Enable APIs And Services", search for Token Service API and enable it.

Firebase data retrieval sporadically not returning

I am building a project with Firebase that involves users. There is a known Firebase bug where even after a user has been deleted from the Firebase Authentication section, the user is still able to access the app and still has read/write permissions to the database. I believe it has something to do with the token being stored by Firebase on the user's device.
As a work around to this, I have implemented a "user checkup" where my app will check to see if the userID of the logged in user exists in my real-time database. This way all i have to do is delete the userID node from the real-time database and that will cause this checkup to fail and thus log the user out permanently.
The problem i am having is that the call to observeSingleEvent(of: .value...) is sporadically not returning. By this i mean that sometimes it returns immediately as is expected, however sometimes it just doesn't return at all. For example yesterday morning it wasn't working at all. Yesterday afternoon and night it worked fine. Now today it is no longer working again. Code below...
func checkDatabaseForFIRUser(withId: String, callback: #escaping (Bool)-> Void) {
let fireUsersRef = fireRootRef.child("all-users").child(withId)
fireUsersRef.observeSingleEvent(of: .value, with: {(snapshot)-> Void in
print("user check result -> \(snapshot)")
if snapshot.exists() {
print("SNAP EXISTS")
callback(true)
} else {
print("SNAP NOT EXISTS")
callback(false)
}
}, withCancel: {(error)-> Void in
print("CANCEL BLOCK ERROR = \(error) and localized description = \(error.localizedDescription)")
})
}
I have also noticed that during the times when I am unable to retrieve data I am also unable to write anything to the database, it is as if nothing at all works. My Firebase Rules have been set to "Public" so anyone can read/write. And I have a working internet connection on my device
EDIT: everything works fine on Simulator. Issue is only occurring on the actual device
EDIT #2: This issue seems to have subsided, I haven't noticed it for at least a few weeks now
I bet that by the time you are calling observeSingleEvent, you are log out, so probably that's why it is not returning anything.

Error adding Firebase users Swift

By following the information on this link, I am currently working on trying to create a user using Firebase, as shown in the code below. However, it appears no user is created when the app is run. Any input is greatly appreciated. The Firebase URL was taken from my test database.
let ref = Firebase(url: "https://test-96df2.firebaseio.com")
ref.createUser("bobtony#example.com", password: "correcthorsebatterystaple",
withValueCompletionBlock: { error, result in
if error != nil {
// There was an error creating the account
print("error creating account")
} else {
let uid = result["uid"] as? String
print("Successfully created user account with uid: \(uid)")
}
})
Edit:
The propagated error is:
Error Code: AUTHENTICATION_DISABLED) Projects created at
console.firebase.google.com must use the new Firebase Authentication
SDKs available from firebase.google.com/docs/auth/
However, as shown below, the email/password authentication is enabled in the Firebase console.
You have already authorized the email registration as I can see in your screenshot. So the problem doesnt realies on AUTHENTICATION_DISABLED.
From your error message and the snippet code I can see that you have created a new project but you are trying to use the legacy firebase SDK and so you will have compatibility issues. Also, you are looking into the old firebase documentation.
First of all you will need to make sure you have configured your project as documented in new firebase start guide.
After, you can take a look at the documentation for creating a new user with the new sdk. You can find the 3.x create user call bellow.
FIRAuth.auth()?.createUserWithEmail(email, password: password) { (user, error) in
// ...
}

Apple Store build rejected while using CloudKit/iCloud

I just submited my app to the Apple Store and it failed submission because of the following issue and I am quite confuse about how to work around it.
From Apple - 17.2 Details - We noticed that your app requires users to
register with personal information to access non account-based
features. Apps cannot require user registration prior to allowing
access to app content and features that are not associated
specifically to the user.
Next Steps - User registration that requires the sharing of personal
information must be optional or tied to account-specific
functionality. Additionally, the requested information must be
relevant to the features.
My app uses CloudKit to save, retrieve and share records. But the app itself do not ask for any personal details neither share any personal details like emails, names, date of birth..., it just asks the user to have an iCloud account active on the device. Then CloudKit uses the iCloud credentials in order to work.
It becomes confusing because:
1 - I can't change the way CloudKit works and stop asking for the user to login on iCloud. Every app that uses CloudKit needs an user logged on iCloud.
2 - As other apps (facebookas an example) if you do not login the app cannot fundamentally work. So the login is not tied to specific functionality, but to the whole functionality of the app.
The code example bellow is called on an initial screen (before getting inside the app functional areas) every time the app starts to make sure the user has the iCloud going. If the user has iCloud I take him inside the app. If not I stop him and ask him to get iCloud sorted. But I guess that is what they are complaining about here - "User registration that requires the sharing of personal information must be optional or tied to account-specific functionality. Additionally, the requested information must be relevant to the features.".
Which puts myself in a quite confusing situation. Not sure how to resolve the issue. Has anyone has similar issues with CloudKit/iCloud/AppStore Submission? Any insights?
iCloud check code bellow:
func cloudKitCheckIfUserIsAuthenticated (result: (error: NSError?, tryAgain: Bool, takeUserToiCloud: Bool) -> Void){
let container = CKContainer.defaultContainer()
container.fetchUserRecordIDWithCompletionHandler{
(recordId: CKRecordID?, error: NSError?) in
dispatch_async(dispatch_get_main_queue()) {
if error != nil
{
if error!.code == CKErrorCode.NotAuthenticated.rawValue
{
// user not on icloud, taki him there
print("-> cloudKitCheckIfUserIsAuthenticated - error fetching ID - not on icloud")
// ERROR, USER MUST LOGIN TO ICLOUD - LOCK HIM OUTSIDE THE APP
result(error: error, tryAgain: false, takeUserToiCloud: true)
}
print("-> cloudKitCheckIfUserIsAuthenticated - error fetching ID - other error \(error?.description)")
// OTHER ERROR, TRY AGAIN
result(error: error, tryAgain: true, takeUserToiCloud: false)
}
else
{
let publicDatabase = CKContainer.defaultContainer().publicCloudDatabase
publicDatabase.fetchRecordWithID(recordId!,
completionHandler: {(record: CKRecord?, error: NSError?) in
if error != nil
{
// error getting user ID, try again
print("-> cloudKitCheckIfUserIsAuthenticated - error fetching user record - Error \(error)")
// ERROR, TRY AGAIN
result(error: error, tryAgain: true, takeUserToiCloud: false)
}
else
{
if record!.recordType == CKRecordTypeUserRecord
{
// valid record
print("-> cloudKitCheckIfUserIsAuthenticated - fetching user record - valid record found)")
// TAKE USER INSIDE APP
result(error: error, tryAgain: false, takeUserToiCloud: false)
}
else
{
// not valid record
print("-> cloudKitCheckIfUserIsAuthenticated - fetching user record - The record that came back is not a user record")
// ERROR, TRY AGAIN
result(error: error, tryAgain: true, takeUserToiCloud: false)
}
}
})
}
}
}
}
Initially my application would ask the user to login to iCloud on launch screen. If the users did not have an iCloud account functional they would not be able to get inside the app.
Solution
Let the user get inside the app and click on the main sections. In fact the app was completely useless but the user could see it's odd empty screens without the ability to save or load anything. By the time they tried to load or save things I would prompt them the needed to login on iCloud to make the app usable.
Practical outcome
I don't think apple's change request added anything of value to the UX. In fact it just added complexity for the user to understand what he can do and what he cannot.
As an example Facebook locks the user outside if the user do not provide his personal details because without this data the application has absolutely no use and that is my case... You could arguably say the user should be able to get inside, but what he would see or do? Then you would have to cater for all the exceptions this UX builds and throw warnings for the user to fix the account issue everywhere on an annoying pattern of warnings.
So I am not sure "how" Facebook could get it approved and I could not.
Although I got the app approved I disagree Apple feedback improved the application in any way.

Resources