Firebase Connectivity Test Shows Disconnected At Start Even When Connected. How Do I Change This? - swift

I am trying to make sure Firebase has a connection before continuing to load the app. I'm using the code from Firebase's own code sample. I have placed it in the ViewDidLoad function on my home view controller:
let connectedRef = Database.database().reference(withPath: ".info/connected")
connectedRef.observe(.value, with: { snapshot in
if let connected = snapshot.value as? Bool, connected {
print("Connected")
} else {
print("Not connected")
// show alert here
}
})
The problem is that the above code always shows "Not Connected" before then showing "Connected". This is a problem because when the app is not connected, it's supposed to show an alert, and the alert will then fire every time the user opens the app.
Is this expected behavior? If so, is there a way around it?
How do I check for Firebase connectivity without Firebase returning that it's not connected first every time?

The behavior you're observing is expected. The Firebase SDK can't establish its connection immediately upon startup. There is always going to be some latency between launch and whenever a connection is first available.
Also, I don't think this strategy is a good idea, because mobile connections can be intermittent and flakey. Firebase can not possibly ensure that your app will retain a good connection even after it's first established. Your app will be easier to use if you assume that it doesn't have a connection all the time. Users have come to expect some level of offline usage, and Realtime Database supports that to some degree with offline data persistence.

Related

Firebase Authentication using facebook and google signin not working when internet is turned off and turned back on

I have a strange issue with firebase authentication. SigninCredential callback never gets called when I turn off and turn back on the internet. Only after a few hours(probably after 4 hours) callback gets triggered
val mCredential = FacebookAuthProvider.getCredential(mAccesstoken!!.token)
mFirebaseAuth.signInWithCredential(mCredential)
.addOnCompleteListener(this) { it ->
if (it.isSuccessful) {
// Sign in success, update UI with the signed-in user's information
Log.d(TAG, "signInWithCredential:success")
getCurrentUser(mFirebaseAuth.currentUser)
} else {
// If sign in fails, display a message to the user.// unable to retrieve details of the user
Log.w(
TAG,
"signInWithCredential:failure:: FirebaseAuthentication failed even though accesstoken present",
it.exception
)
Toast.makeText(
baseContext, "Authentication failed.",
Toast.LENGTH_SHORT
).show()
}
}
If I do a fresh install of the app and perform a sign in it works properly provided that the internet is turned on while I try to login into the app
First check for internet connection before executing the above code.
I figured out the issue. I added an active internet condition before performing a login that fixed sign-in problem. check the internet available on the device then only perform the login
I did face another issue that is addOnCompleteListener never gets called because I was waiting on the main thread instead I used executor to perform the task on a background thread

Handle the state of IKEv2 Personal VPN with Swift

I've implemented the possibility of opening VPN connection with IKEv2. The connection works great after I close (kill) the app. However, I want to get the current connection status after I launch the app again. How can I do that?
Unfortunately this returns invalid status when I try to call:
NEVPNManager.shared().connection.status
but I see (and it's correct and it's true) Personal VPN in active state here:
Could you provide how to get actual status of my Personal VPN?
I've resolved this case with this implementation specially for IKEv2
// special case for IKEv2
NEVPNManager.shared().loadFromPreferences { error in
if error != nil {
//
} else {
print(NEVPNManager.shared().connection.status)
}
}
I've added this into my connection manager and now the app handles correctly the status of Personal VPN after relaunch of the app

Firebase Auth: user profile changes are not synced to other devices?

Scenario:
I am logged in with Firebase Auth, using the email provider. I change my user's photoURL and displayName using this Swift code:
let changeRequest = Auth.auth().currentUser?.createProfileChangeRequest()
changeRequest?.photoURL = someURL
changeRequest?.displayName = someName
changeRequest?.commitChanges { (error) in
// ...
}
From then on, when I use Auth.auth().currentUser.photoURL and Auth.auth().currentUser.displayName to render the user's name and avatar, it all works as expected. No problem at all.
However, I was also logged in on another device, before I made the changes to photoURL and displayName. And on that device, the old user information is shown, even when killing and restarting the app. All of that while I am using the Auth.auth().addStateDidChangeListener API as well.
Only when I logout and log back in, is the user info refreshed. I guess it's using the locally cached data, but it's not fetching the fresh user info from the server or something?
Am I doing something wrong? Do I need to force a refresh when the app starts up or something?
Edit: I've tried again a few times and yeah it's 100% reproducible.
Log in on two devices. Output Auth.auth().currentUser.displayName somewhere on screen (or to the console).
On device A, change the displayName. When I refresh the UI, the new name is shown. When I restart the app, the new name is shown.
On device B, the old name is still shown, even after killing and restarting the app. Only when I logout and back in, is the new name finally shown.
In the end I created a new top level Users collection, where I am now storing the user profile data. And with a snapshot listener, updating info on one device also updates it on the others. Too bad this was necessary, but it works.

Firebase Offline Support: upload posts while user is offline and sync when user comes online in iOS Swift app

I am using firebase in an iOS-Swift project in which I have to enable offline support for uploading posts, in the post there is a picture and caption just like Instagram, so what I want is when user is offline and he/she wants to upload a post, his/her picture get saved in cache and when user comes online that photo get uploaded and give back a download url that we can use for saving posts-details it in database.
sample code is:
let photoIDString = UUID().uuidString
let storageRef = Storage.storage().reference(forURL: "storage ref URL").child("posts").child(photoIDString)
storageRef.putData(imageData, metadata: nil, completion: { (metadata, error) in
guard let metadata = metadata else {
return
}
if error != nil {
return
}
storageRef.downloadURL(completion: { ( url, error ) in
guard let downloadURL = url else {
return
}
let photoUrl = downloadURL.absoluteString
self.sendDataToDatabase(photoUrl: photoUrl)
})
}
)
I want to know what changes should I make in my code to provide the offline capability. Any code snippet will help more.
The problem is better view as re-send to server when there is an error.
For your offline case, you can check if the error return is a network error, or manually check network connection availability.
You can create a re-send array of object
e.g
var resendList : [YourObjectType]
// when failed to send to server
resendList.append(yourFailedObject)
And then, 2 solutions:
Check the network connectivity manually and reupload in when the app become active in func applicationDidBecomeActive(_ application: UIApplication) in appDelegate. For checking connectivity you can try the method here: Check for internet connection with Swift But this has a problem that, the user has to go out the app and back again with network connected
Keep track(listen to notification) on the connectivity change, using a suggestion method by https://stackoverflow.com/a/27310748/4919289 and reupload it to server
and loop through all objects in resendList and re-upload again.
I am not an iOS developer, but I can share logical flow and some references.
When user clicks on upload: Check if network is available?
if yes: upload the post.
if no:
save the post to app storage or offline database
set broadcast receiver to receive broadcast when device comes online. This link may be helpful.
upload post when device comes online.
If you are looking for solution that is offered by Firebase, you may find more details here.
Firebase offers you plenty of ways to do this in their documentation. https://firebase.google.com/docs/database/ios/offline-capabilities
When uploading to the firebase server, it will queue itself and wait until it has a internet connection again to upload. If this happens to timeout or you want to do it your own way just attempt to upload with a completionHandler on the setValue or updateChild functions - if not successfully and the error message is because of internet, add it to a local cache to the phone with the data and the path to the firebase server.
onLoad, attempt the same upload again until it succeeds, once it succeeds - clear the local cache.

Sinch: user does not receive the call for the first time

I implemented Sinch + PushKit + CallKit, everything works fine, but there is one script that does not work correctly. The first user uses the application and removes it from the device's memory, the second user calls for the first time, the first user does not receive the call, if the second user immediately calls the second time, the first user receives a call (later the first user also receives a call). If the first user opens the application (that is, becomes online for the system), the first user will not receive the call again. How can I fix it?
initialization of the sinch client
open func setup() {
guard sinch == nil else { return }
guard let userID = UserRealmManager().getUser()?.id else { return }
sinch = Sinch.client(withApplicationKey: key, applicationSecret: secret, environmentHost: host, userId: userID)
sinch?.delegate = self
sinch?.call().delegate = self
sinch?.setSupportCalling(true)
sinch?.enableManagedPushNotifications()
sinch?.setSupportPushNotifications(true)
sinch?.start()
sinch?.startListeningOnActiveConnection()
}
Update: I also found that if I restart the iPhone then calls through CallKit start to show up in 2-4 minutes, I decided to test it on the famous messangers such as What's app and Telegram and they have exactly the same behavior. Of course, I think it needs to be asked as an additional question.
My devices are iPhone 6 and 7.
This was my mistake, since I initialized SinchManager (this is the manager that manages SINClient) only in MainTabBarController viewDidLoad(), after I began to initialize it in AppDelegate didFinishLaunchingWithOptions, everything works fine.