fetching firestore data in applicationDidFinishLaunchingWithOptions - swift

I'm using firebase firestore and authentication .
My app is basically managing orders, when a user sends a new order to firestore it gets a openOrder default Boolean var, I have another app that manage this order and once my other app reads the order the boolean var changes value.
All of that works.
My issue is when a user closes completly the app and then reopens it I need to check if the openOrder is true or not and according to that set my rootViewController .
I'm using a completion handler to fetch the openOrder var and check if it is true or not but applicationDidFinishLaunchingWithOptions returns true before I set my local vars according to the firestore functions.
my code is :
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
let reValue = loadPriviousDishesIfUserQuitsAppBeforeClosingTab(completion: { success in
guard success! else { return }
//here I have all the values I need and need to return only here
})
return true
}
func loadPriviousDishesIfUserQuitsAppBeforeClosingTab(completion: #escaping (Bool?) ->()) {
db = Firestore.firestore()
let myUserID = Auth.auth().currentUser?.uid
db.collection("users").whereField("user", isEqualTo: myUserID!).whereField("openOrder", isEqualTo: true).getDocuments { (querySnapshot, error) in
if let err = error {
print(err.localizedDescription)
completion(nil)
}
else {
for doc in (querySnapshot?.documents)! {
guard let restID = doc.data()[ResttId"]
else {return}
myRestaurant.restID = restID as? String
self.setMyRestMenu(completion: { success in
guard success else { return }
//here I set all my values using fetching all the data from firestore,
})
}
completion(true)
}
}
}

You can show a loading activity above the rootViewController until get that value , then
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let reValue = loadPriviousDishesIfUserQuitsAppBeforeClosingTab(completion: { success in
guard success! else { return }
//here I have all the values I need and need to return only here
let stor = UIStoryboard.init(name: "Main", bundle: nil)
let welcomeView = stor.instantiateViewController(withIdentifier: "orderView")
let nav = UINavigationController(rootViewController: welcomeView )
nav.navigationBar.isHidden = true
self.window?.rootViewController = nav
})
return true
}
Edit : set storyboardID here

Related

Firebase iOS clicking on shortened link returns ' Receive failed with error "Software caused connection abort" '

I'm just going to paste in a couple of my files so that you can test this really easily and see what's going on. I'm clicking the button and it's making the shortened dynamic link. Then, I'm typing out the DynamicLink in the notes app and then I press the link. I get redirected to the app and the following error is returned:
[connection] nw_read_request_report [C1] Receive failed with error "Software caused connection abort"
Side note: all of this is being tested on an iPhone 7 (a physical device, not the simulator).
FirebaseTestApp and AppDelegate:
import SwiftUI
import Firebase
#main
struct FirebaseTestApp: App {
#UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class AppDelegate: NSObject, UIApplicationDelegate {
var functionMaster: functions = functions()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
FirebaseApp.configure()
return true
}
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
let dynamicLink = DynamicLinks.dynamicLinks().dynamicLink(fromCustomSchemeURL: url)
if dynamicLink != nil {
print("Dynamic link : \(String(describing: dynamicLink?.url))")
return true
}
return false
}
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
print("Successful penetration")
guard let inComingURL = userActivity.webpageURL else { return false }
print("Incoming Web Page URL: \(inComingURL)")
self.functionMaster.handleIncomingDynamicLink(inComingURL)
return true
}
}
functions class:
import Foundation
import Firebase
import UIKit
class functions: ObservableObject {
func makeDynamicLink() {
var components = URLComponents()
components.scheme = "https"
components.host = "www.firebase-test.com" //this can be some random domain right? It doesn't have to actually exist yet?
components.path = "/data"
let stringifiedNumber = String(123)
components.queryItems = [stringifiedNumber]
let dynamicLinksDomainURIPrefix = "https://something.page.link"
guard let linkParameter = components.url else { return }
print("I am sharing \(linkParameter)")
guard let linkBuilder = DynamicLinkComponents(link: linkParameter, domainURIPrefix: dynamicLinksDomainURIPrefix) else { return }
if let myBundleId = Bundle.main.bundleIdentifier {
linkBuilder.iOSParameters = DynamicLinkIOSParameters(bundleID: myBundleId)
}
linkBuilder.iOSParameters?.appStoreID = "962194608"
linkBuilder.socialMetaTagParameters = DynamicLinkSocialMetaTagParameters()
linkBuilder.socialMetaTagParameters?.title = testLocation.name
linkBuilder.socialMetaTagParameters?.descriptionText = testLocation.address
linkBuilder.shorten { [weak self] (url, warnings, error) in
if let error = error{
print("Firebase encountered an error: \(error)")
return
}
if let warnings = warnings {
for warning in warnings {
print("Firebase Warning: \(warning)")
}
}
guard let url = url else { return }
print("The short URL is: \(url.absoluteString)")
self?.showShareSheet(url: url)
}
guard let longDynamicLink = linkBuilder.url else { return }
print("The long URL is: \(longDynamicLink)")
}
func showShareSheet(url: URL) {
let promoText = "Check out this thing I've marked in FirebaseTest!"
let activityVC = UIActivityViewController(activityItems: [promoText, url], applicationActivities: nil)
UIApplication.shared.windows.first?.rootViewController?.present(activityVC, animated: true)
}
func handleIncomingDynamicLink(_ dynamicLink: URL) {
_ = DynamicLinks.dynamicLinks().handleUniversalLink(dynamicLink) { (dynamiclink, error) in
guard error == nil else {
print("Found an error: \(error?.localizedDescription ?? "")")
return
}
print("Dynamic link : \(String(describing: dynamiclink?.url))")
let path = dynamiclink?.url?.path
var id = 0
if let query = dynamiclink?.url?.query {
let dataArray = query.components(separatedBy: "=")
id = Int(dataArray[1]) ?? 0
}
if path == "data" {
//Write code here
}
}
}
}
ContentView:
import SwiftUI
struct ContentView: View {
#ObservedObject var functionMaster: functions = functions()
var body: some View {
Button("Click me to run some firebase stuff") {
functionMaster.makeDynamicLink()
}
.padding()
}
}
In browser, when I navigate to https://something.page.link/apple-app-site-association, I get this:
https://i.stack.imgur.com/6Ndo0.png
Try installing the files for the the simulator you want to test on, update Xcode, delete all other versions.

"applicationWillTerminate" is not called in swift

I am working on a chatting application, where i have to maintain user "offline" or "online" status.I want the user status will be offline when user enters in background and also if user will kill the app.In my app in the background case it is working fine. but it is not working fine when i kill the app.My code is: -
func applicationDidEnterBackground(_ application: UIApplication) {
if let uid: Int = UserDefaults.standard.value(forKey: "User_Id") as? Int{
reference(.User).whereField(kREGISTERUSEID, isEqualTo: "\(uid)").getDocuments { (snapshot, err) in
if let err = err {
}
else {
let document = snapshot!.documents.first
document!.reference.updateData([
kISONLINE: false
])
}
}
}
}
func applicationDidBecomeActive(_ application: UIApplication) {
if let uid: Int = UserDefaults.standard.value(forKey: "User_Id") as? Int{
reference(.User).whereField(kREGISTERUSEID, isEqualTo: "\(uid)").getDocuments { (snapshot, err) in
if let err = err {
}
else {
let document = snapshot!.documents.first
document!.reference.updateData([
kISONLINE: true
])
}
}
}
}
func applicationWillTerminate(_ application: UIApplication) {
if let uid: Int = UserDefaults.standard.value(forKey: "User_Id") as? Int{
reference(.User).whereField(kREGISTERUSEID, isEqualTo: "\(uid)").getDocuments { (snapshot, err) in
if let err = err {
}
else {
let document = snapshot!.documents.first
document!.reference.updateData([
kISONLINE: false
])
}
}
}
}
I am using firebase here.Also i want to make user offline if user will not go to the chat screen or will not send message to any one (like whatsapp) for 20 seconds .
Please help me to implement it. Thanks
Your api or firebase call must be performed on the background mode. Go the link you will find how to crate a background task.
Can I make an api call when the user terminates the app?

How to fix push notification title not showing correct string in swift

i've been facing some problem with FCM push notifications showing in my app.
here is the Data that the FCM send me :
["roomId": 253539,
"type": ROOM_SEND_MESSAGE,
"aps": {
alert = {
"loc-args" = (
"TEST STRING"
);
"loc-key" = "CHANNEL_MESSAGE_IMAGE";
};
"content-available" = 1;
sound = default;
},"messageId": 15638864319517014,
"gcm.message_id": 1563886435277772]
here is the problem :
whenever i receive a notification it is supposed to show me the parameters in "loc-args" but instead it shows me the value in "loc-key"
check the image bellow
the image for my problem
and here is the code in my appDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if SMLangUtil.loadLanguage() == "fa" {
IGGlobal.languageFileName = "localizationsFa"
} else {
IGGlobal.languageFileName = "localizationsEn"
}
let stringPath : String! = Bundle.main.path(forResource: IGGlobal.languageFileName, ofType: "json")
MCLocalization.load(fromJSONFile: stringPath, defaultLanguage: SMLangUtil.loadLanguage())
MCLocalization.sharedInstance().language = SMLangUtil.loadLanguage()
if SMLangUtil.loadLanguage() == "fa" {
UITableView.appearance().semanticContentAttribute = .forceRightToLeft
} else {
UITableView.appearance().semanticContentAttribute = .forceLeftToRight
}
SMUserManager.clearKeychainOnFirstRun()
SMUserManager.loadFromKeychain()
realmConfig()
Fabric.with([Crashlytics.self])
_ = IGDatabaseManager.shared
_ = IGWebSocketManager.sharedManager
_ = IGFactory.shared
_ = IGCallEventListener.sharedManager // detect cellular call state
UITabBar.appearance().tintColor = UIColor.white
let tabBarItemApperance = UITabBarItem.appearance()
tabBarItemApperance.setTitleTextAttributes(convertToOptionalNSAttributedStringKeyDictionary([convertFromNSAttributedStringKey(NSAttributedString.Key.foregroundColor):UIColor.red]), for: UIControl.State.normal)
tabBarItemApperance.setTitleTextAttributes(convertToOptionalNSAttributedStringKeyDictionary([convertFromNSAttributedStringKey(NSAttributedString.Key.foregroundColor):UIColor.white]), for: UIControl.State.selected)
UserDefaults.standard.setValue(false, forKey:"_UIConstraintBasedLayoutLogUnsatisfiable")
pushNotification(application)
detectBackground()
IGGlobal.checkRealmFileSize()
return true
}
/******************* Notificaton Start *******************/
func pushNotification(_ application: UIApplication){
FirebaseApp.configure()
Messaging.messaging().isAutoInitEnabled = true
Messaging.messaging().delegate = self
Messaging.messaging().shouldEstablishDirectChannel = true
if #available(iOS 10.0, *) { // For iOS 10 display notification (sent via APNS)
/**
* execute following code in "IGRecentsTableViewController" and don't execute here,
* for avoid from show permission alert in start of app when user not registered yet
**/
//UNUserNotificationCenter.current().delegate = self
//let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound, .carPlay]
//UNUserNotificationCenter.current().requestAuthorization(options: authOptions,completionHandler: {_, _ in })
} else {
let settings: UIUserNotificationSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
self.voipRegistration()
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
voipRegistration()
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
print("||||||NOTIFICATION||||||||")
print(userInfo)
if let roomId = userInfo["roomId"] as? String {
let unreadCount = IGRoom.updateUnreadCount(roomId: Int64(roomId)!)
application.applicationIconBadgeNumber = unreadCount
}
}
/******************* Notificaton End *******************/
for any one facing the same or similar problem i found the answer to my question :D
becoz all notifications are being handled by the os it self all i had to do was to go deeper to find the problem
all i had to do was to re create the localizable files and boom every thing goes in order :)

Why does the function return the result ahead of time?

In appDelegate I will check whether the user is authorized through the userAuthorizedCheck() function. Depending on the result, redirect it to one or another stroyBoard. userAuthorizedCheck() should return the result only after the server has answered. The problem is that if i leave the last completion(false) in userAuthorizedCheck(), then it returns false first, and then it is checked. Even if the check was successful, then all the same, completion(false) is sent first and, as a result, the redirect is sent to the Authorization storyboard.
But if I remove the last completion(false), then I get Thread 1: signal SIGABRT, opposite func application (). print (tempToken) is triggered after checking userAuthorizedCheck (). If i put a breakpoint, i can see that in the userAuthorizedCheck () function, the last completion (false) works first.
AppDelegate:
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
var storyboardName: String = ""
userAuthorizedCheck(after: { (succesful) in
if (succesful == true){
storyboardName = "Main"
}else{
print(false)
storyboardName = "Authorization"
}
})
let storyboard = UIStoryboard(name: storyboardName, bundle: Bundle.main)
window = UIWindow(frame: UIScreen.main.bounds)
window!.makeKeyAndVisible()
window!.rootViewController = storyboard.instantiateInitialViewController()
return true
}
func userAuthorizedCheck(after completion: #escaping (Bool) -> Void) {
let username : String = UserDefaults.standard.string(forKey: "username") ?? ""
let password : String = UserDefaults.standard.string(forKey: "password") ?? ""
let tokenSaved : String = UserDefaults.standard.string(forKey: "token") ?? ""
var tempToken:String = ""
//
if(!username.isEmpty && !password.isEmpty)
{
let json: [String: String] = ["username": username, "password": password]
login(json: json, after: {(status, token, code) in
if(code == 200 && !token.isEmpty){
UserDefaults.standard.setValue(token, forKey: "token");
UserDefaults.standard.synchronize();
tempToken = token
print(tempToken)
completion(true)
}
else{
tempToken = ""
completion(false)
}
})
}
else
{
completion(false)
}
completion(false)//The problem in this line, as I repent
}
login(in another swift file):
func login(json:Any, after completion: #escaping (Bool, _ token: String, _ code:Int) -> Void){
guard let url = URL(string: ngrok+"/api/auth/token/create")else{return}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField:"Content-Type")
guard let httpBody = try? JSONSerialization.data(withJSONObject: json, options: [])else {return}
request.httpBody = httpBody
let sessionConfig = URLSessionConfiguration.default
sessionConfig.timeoutIntervalForRequest = 5.0
sessionConfig.timeoutIntervalForResource = 60.0
URLSession(configuration: sessionConfig).dataTask(with: request){(data, response, error) in
if error != nil{
print("server error")
completion(true, "", 0)
}
else if let response = response{
// print(response)
if let httpResponse = response as? HTTPURLResponse{
guard let data = data else{return}
do{
// print(data)
if(httpResponse.statusCode == 200){
if let json_response = try JSONSerialization.jsonObject(with: data, options: [])as? [String:Any]{
if let token = json_response["auth_token"]{
print(token as! String)
completion(true, "token",httpResponse.statusCode)
}
}
}
else if(httpResponse.statusCode == 400)
{
completion(true, "",httpResponse.statusCode)
print("The username or password you entered is incorrect")
}
else{
print("Unknown error")
completion(true, "", 0)
}
}
catch{
print("errasd")
print(error)
completion(true, "", 0)
}
}
}
}.resume()
}
I want the user Authorized Check () function to send the result only after the server has responded.
You cannot wait in didFinishLaunchingWithOptions for something asynchronous before returning true or false.
One option is to return true and load the storyboard after the response of the server
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
userAuthorizedCheck(after: { (succesful) in
let storyboardName = succesful ? "Main" : "Authorization"
let storyboard = UIStoryboard(name: storyboardName, bundle: Bundle.main)
self.window = UIWindow(frame: UIScreen.main.bounds)
window!.makeKeyAndVisible()
window!.rootViewController = storyboard.instantiateInitialViewController()
})
return true
}
And delete this line in userAuthorizedCheck
completion(false)//The problem in this line, as I repent
because it completes immediately which is not intended.

Getting Thread: signal SIGABRT

Building an app with a chat feature and getting a SIGBRT error. It doesn't seem to be talking to the Firebase database. I checked all my outlets and they seem to all be intact and I didn't see any broken outlets.
the error I'm getting in the debug area is
"2018-08-21 01:09:15.479487-0400 Split App[83668:9375919] *** Terminating app due to uncaught exception 'FIRAppNotConfigured', reason: 'Failed to get default Firebase Database instance. Must call [FIRApp configure] (FirebaseApp.configure() in Swift) before using Firebase Database.... libc++abi.dylib: terminating with uncaught exception of type NSException"
class DataService{
static let dataService = DataService()
private var _BASE_REF = Database.database().reference()
private var _ROOM_REF = Database.database().reference().child("rooms")
var BASE_REF: DatabaseReference {
return _BASE_REF
}
var ROOM_REF:DatabaseReference{
return _ROOM_REF
}
var storageRef: StorageReference{
return Storage.storage().reference()
}
var fileURL: String!
// store the thumbnail in database
func CreateNewRoom(user: User, caption: String, data: NSData){
let filePath = "\(user.uid)/\
(Int(NSDate.timeIntervalSinceReferenceDate))"
let metaData = StorageMetadata()
metaData.contentType = "image/jpg"
storageRef.child(filePath).putData(data as Data, metadata: metaData){
(metadata, error) in if error != nil {
print ("Error Uploading: \(String(describing:
error?.localizedDescription))")
return
}
//create a url for data (photo thumbnail image)
_ = metadata?.storageReference?.downloadURL(completion: error as!
(URL?, Error?) -> Void)
if Auth.auth().currentUser != nil {
let idRoom = self.BASE_REF.child("rooms").childByAutoId()
idRoom.setValue(["caption": caption,"thumbnailURLFromStorage":
self.storageRef.child(metadata!.path!).description, "fileURL" :
self.fileURL])
}
}
}
func fetchDataFromServer(callback: #escaping (Room) -> ()){
DataService.dataService.ROOM_REF.observe(.childAdded){ (snapshot) in
let room = Room(key: snapshot.key, snapshot: snapshot.value as!
Dictionary<String, Any>)
callback(room)
}
}
func SignUp(username:String, email: String, password: String, firstName:
String, lastName: String, data: NSData){
Auth.auth().createUser(withEmail: email, password: password, completion:
{ (user, error) in
if error != nil {
print(error!)
}
else {
print("Registration Successful")
}
let changeRequest =
Auth.auth().currentUser?.createProfileChangeRequest()
changeRequest?.displayName = username
changeRequest?.commitChanges(completion: {(error) in
if let error = error {
print (error.localizedDescription)
return
}
})
let filePath = "profileimage/\(String(describing:
Auth.auth().currentUser!.uid))"
let metadata = FirebaseStorage.StorageMetadata()
metadata.contentType = "image/jpeg"
self.storageRef.child(filePath).putData(data as Data, metadata:
metadata, completion: {(metadata, error) in
if let error = error {
print ("\(error.localizedDescription)")
return
}
_ = metadata?.storageReference?.downloadURL(completion: error as!
(URL?, Error?) -> Void)
if let error = error {
print (error.localizedDescription)
return
}
else {
print ("Sweet!")
}
let appDelegate: AppDelegate = UIApplication.shared.delegate as!
AppDelegate
appDelegate.login()
})
}
}
You should follow these steps
Step 1 Import Firebase to your AppDelegate.swift
import Firebase
Step 2 call configure() in didFinishLaunchingWithOptions method in AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FirebaseApp.configure()
return true
}
Hope it helps
You have not configured firebase yet .
So just inside you app delegate first import Firebase and inside the method didFinishLaunchingWithOptions configure firebase.
By writing a single line FirebaseApp.configure().