Downloading firebase data in the app delegate - ios

I have begun building an app that lets a user sign up and create a family. Right now I am downloading the data for the user from the log in screen and, from the appDelegate if the user is already logged in. However, when the user data is downloaded from the appDelegate I get nil values.
Whats weird is that I have the following written in each of my three view controllers(controlled by a tab controller):
var user: User? {
didSet {
print(user!.name!)
}
}
When the data eventually downloads from the appDelegate the correct name is printed. However, trying to use the user variable at any other time gives nil.
Also, when the data is downloaded from the log in screen it works perfectly. Very confused about this and would hugely appreciate any help. Thanks in advance.

I figured out what was wrong. Nothing to do with firebase.
While in the app delegate after downloading the user data, I tried to set the properties of the viewControllers in my tabViewController. What I actually did was create a new instance of each view controller, set its properties(which is why the didSet property observer fired) but then when setting the rootViewController I created a new instance of the tabController which I think then created new instances of the three viewControllers.
This is what the old code was in the appDelegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
FIRApp.configure()
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
if let user = FIRAuth.auth()?.currentUser {
downloadUserData(user)
self.window?.rootViewController = storyBoard.instantiateViewControllerWithIdentifier("Home")
} else {
window?.rootViewController = storyBoard.instantiateViewControllerWithIdentifier("SignIn")
}
return true
}
func downloadUserData(user: FIRUser) {
let ref = FIRDatabase.database().reference()
ref.child("users").queryOrderedByKey().queryEqualToValue(user.uid).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
for child in snapshot.children {
if let name = child.value!["name"] as? String, let familyID = child.value!["signedInTo"] as? String {
self.user = User(uid: user.uid, name: name)
ref.child("families").queryOrderedByKey().queryEqualToValue(familyID).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
for child in snapshot.children {
if let name = child.value!["name"] as? String {
self.family = Family(uid: familyID, name: name)
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
let tabVC = storyBoard.instantiateViewControllerWithIdentifier("Home") as? TabBarViewController
tabVC?.user = self.user
tabVC?.family = self.family
let choresNav = tabVC?.viewControllers![0] as? UINavigationController
let choresVC = choresNav?.topViewController as? ChoresViewController
choresVC?.user = self.user
choresVC?.family = self.family
}
}
})
}
}
})
}
This is the appDelegate now, along with a custom TabBarViewController:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
FIRApp.configure()
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
if let user = FIRAuth.auth()?.currentUser {
downloadUserData(user)
} else {
window?.rootViewController = storyBoard.instantiateViewControllerWithIdentifier("SignIn")
}
return true
}
func downloadUserData(user: FIRUser) {
let ref = FIRDatabase.database().reference()
ref.child("users").queryOrderedByKey().queryEqualToValue(user.uid).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
for child in snapshot.children {
if let name = child.value!["name"] as? String, let familyID = child.value!["signedInTo"] as? String {
self.user = User(uid: user.uid, name: name)
ref.child("families").queryOrderedByKey().queryEqualToValue(familyID).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
for child in snapshot.children {
if let name = child.value!["name"] as? String {
self.family = Family(uid: familyID, name: name)
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
let tabVC = storyBoard.instantiateViewControllerWithIdentifier("Home") as? TabBarViewController
tabVC?.user = self.user
tabVC?.family = self.family
self.window?.rootViewController = tabVC
}
}
})
}
}
})
}
TabBarController:
var user: User?
var family: Family?
override func viewDidLoad() {
super.viewDidLoad()
print(user?.name)
print(family?.name)
let navVC = self.viewControllers![0] as? UINavigationController
let itemsNav = self.viewControllers![1] as? UINavigationController
let leaderNav = self.viewControllers![2] as? UINavigationController
let choresVC = navVC!.topViewController as? ChoresViewController
let itemsVC = itemsNav!.topViewController as? ShoppingListViewController
let leaderVC = leaderNav!.topViewController as? LeaderBoardViewController
choresVC?.user = self.user
choresVC?.family = self.family
itemsVC!.user = self.user
itemsVC!.family = self.family
leaderVC!.user = self.user
leaderVC!.family = self.family
}
The code I have still isn't great but it has solved the problem. Hope it helps some of you out.

Related

Variables not loaded when pushing a modal from SceneDelegate

I'm trying to push a modal from the SceneDelegate. It all works well until the modal push runs.
I'm gettin the error below, and Xcode says that the variables imagePreview and DownloadURL are nil. Yes the variables are loaded from firebase, as if I print them it works.
2020-10-23 02:18:20.277808+0200 MyApp[30028:568894] Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file MyApp/DownloadViewController.swift, line 29
func pushFile(id: String) {
self.window?.rootViewController = DownloadViewController()
ref = Database.database().reference(withPath: "files")
ref.child(id).observeSingleEvent(of: .value, with: { [self] (snapshot) in
let value = snapshot.value as? NSDictionary
let preview = value?["a"] as? String ?? ""
let downloadURL = value?["d"] as? String ?? ""
if let tabBarController = window?.rootViewController! as? UIViewController { //use of unresolved identifier 'window'
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if let vc = storyboard.instantiateViewController(withIdentifier: "download") as? DownloadViewController {
vc.imagePreview = preview
vc.downloadURL = downloadURL
tabBarController.present(vc, animated: true)
}
}
})
}

Tap Push Notifications : can't be directed to specific ViewController

I'm working on app that includes a messenger feature ,the problem is every time i tap on the push notification im directed to the app itself rather than the message itself, i believe the code is correct, might it be a matter of swift version ....?
// AppDelegate
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
let gcmMessageIDKey = "gcm.message_id"
if let messageID = userInfo[gcmMessageIDKey] {
print("messageID: \(messageID)")
}
connectToFirebase()
Messaging.messaging().appDidReceiveMessage(userInfo)
if let dictString = userInfo["gcm.notification.customData"] as? String {
print("dictString \(dictString)")
if let dict = convertToDictionary(text: dictString) {
if let uid = dict["userId"] as? String,
let fullname = dict["username"] as? String,
let email = dict["email"] as? String,
let profileImageUrl = dict["profileImageurl"] as? String {
let user = User(uid: uid, fullname: fullname, email: email, profileImageurl: profileImageUrl, status: "")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let chatVC = storyboard.instantiateViewController(withIdentifier: IDENTIFIER_CHAT) as! ChatViewController
chatVC.partnerUserName = user.fullname
chatVC.partnerId = user.uid
chatVC.partnerUser = user
guard let currentVC = UIApplication.topViewController() else {
return
}
currentVC.navigationController?.pushViewController(chatVC, animated: true)
}
}
}
Extentions
extension UIApplication {
class func topViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let navigationController = controller as? UINavigationController {
return topViewController(controller: navigationController.visibleViewController)
}
if let tabController = controller as? UITabBarController {
if let selected = tabController.selectedViewController {
return topViewController(controller: selected)
}
}
if let presented = controller?.presentedViewController {
return topViewController(controller: presented)
}
return controller
}
}

How can I go to Inbox Tab when I clicked on the notification regardless of where I am in the app?

I am trying to go to "inbox" tab when I clicked on the notification.
This is the notificationManager:
func didReceiveNotification(notif: [AnyHashable : Any]) {
print("notification... \(notif)")
guard let msgData = notif["gcm.notification.messageData"] as? String else {
return
}
print("msgData: \(msgData)")
CommandManager.shared.processNotification(msgData)
}
This is the commandManager:
func processNotification(_ commands: String) {
var dict:[String:Any] = [:]
dict = dict.convertToDictionary(text: commands) ?? [:]
print("dict \(dict)")
sendMessageToFirestore(data: dict)
let inbox = EngageViewController()
//present the inbox tab
}
Use rootViewController
func processNotification(_ commands: String) {
var dict:[String:Any] = [:]
dict = dict.convertToDictionary(text: commands) ?? [:]
print("dict \(dict)")
sendMessageToFirestore(data: dict)
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = mainStoryboard.instantiateViewController(withIdentifier: "inboxPage") as! UIViewController
UIApplication.shared.keyWindow?.rootViewController = viewController
}
Edit
If it's a UITabBarController, use:
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = mainStoryboard.instantiateViewController(withIdentifier: "tabBarcontroller") as! UITabBarController
viewcontroller.selectedIndex = 1 //index of your inbox
UIApplication.shared.keyWindow?.rootViewController = viewController
If the initial view of app is TabBarController then you can do something like in
AppDelegate.swift
guard let tabBarController = self.window?.rootViewController as? UITabBarController else {
return
}
so,
tabBarController.selectedIndex = 1 //Whatever the index of inbox is

Firebase & Swift - delay change of ViewController until data is read

I have implemented a login to load data from a real-time database every time a user logs in. When the user has successfully logged in, the data is being read - the problem is that AppDelegate changes the ViewController faster than the data is being read. Therefore, nothing will be displayed until the app is closed again.
Is there a way to delay the change of the view until the data is read?
didFinishLaunchingOptions() inside AppDelegate:
let authListener = Auth.auth().addStateDidChangeListener { auth, user in
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if user != nil {
let controller = storyboard.instantiateViewController(withIdentifier: "RootPageViewController") as! RootPageViewController
self.window?.rootViewController = controller
self.window?.makeKeyAndVisible()
} else {
let vc = storyboard.instantiateViewController(withIdentifier: "SettingsTableViewController") as! SettingsTableViewController
vc.runResetData()
let controller = storyboard.instantiateViewController(withIdentifier: "ProfileViewController") as! ProfileViewController
self.window?.rootViewController = controller
self.window?.makeKeyAndVisible()
}
}
LoginViewController:
#IBAction func login(_ sender: Any) {
guard let email = emailInput.text else { return }
guard let password = passwordInput.text else { return }
Auth.auth().signIn(withEmail: email, password: password) { user, error in
if error == nil && user != nil {
self.readData()
self.dismiss(animated: false, completion: nil)
} else {
print("Error logging in: ", error!.localizedDescription)
let alert = UIAlertController(title: "Fehler beim Einloggen", message: error!.localizedDescription, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Verstanden", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
}
func readData() {
guard let uid = Auth.auth().currentUser?.uid else { return }
let databaseRef = Database.database().reference()
databaseRef.child("users").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let monthlyStartKmData = value?["monthlyStartKm"] as? Int ?? 0
let currentKmData = value?["currentKm"] as? Int ?? 0
...
startKm = monthlyStartKmData
currentKm = currentKmData
...
}) { (error) in
print(error.localizedDescription)
}
}
No you can't as the call to listen is asynchronous so it's run with a delay while the rootVC is setted , you need to show A loader like
let vc = UIViewController()
vc.view.backgroundColor = .red
self.window?.rootViewController = vc
self.window?.makeKeyAndVisible()
let authListener = Auth.auth().addStateDidChangeListener { auth, user in
.....
}
Inside AppDelegate
if FIRAuth.auth().currentUser != nil {
// go to home , if you went before you should have km month values cached
}
else {
// go to login
}
There's a possibility to delay the change of view until Firebase was able to load its data:
databaseRef.observeSingleEvent
Working code:
let authListener = Auth.auth().addStateDidChangeListener { auth, user in
let databaseRef = Database.database().reference()
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if user != nil {
databaseRef.observeSingleEvent(of: .value, with: { snap in
let controller = storyboard.instantiateViewController(withIdentifier: "RootPageViewController") as! RootPageViewController
self.window?.rootViewController = controller
self.window?.makeKeyAndVisible()
})
} else {
let vc = storyboard.instantiateViewController(withIdentifier: "SettingsTableViewController") as! SettingsTableViewController
vc.runResetData()
let controller = storyboard.instantiateViewController(withIdentifier: "ProfileViewController") as! ProfileViewController
self.window?.rootViewController = controller
self.window?.makeKeyAndVisible()
}
}

how to set swrevealController after my login viewController in swift

I am developing an app in which I have login page and dashboard page in login page user can login with facebook,after login I navigate the user to dashboard everything works fine till now
But in dashboard I am going to add side menu (by using SWRevealViewcontroller) If I give swrevealviewcontroller as intialView It works but I could not see the login page
First I have to bring login page then it should move to dashboard(which contains side menu item). Here I am using two navigationControllers one is to login and another one is for swrevealViewcontroller
I don't know how to move from login page to dashboard (which contains side menu item) now
This is my app delegate
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject:AnyObject]?) -> Bool {
// Override point for customization after application launch.
print("at delegate")
var configureError: NSError?
GGLContext.sharedInstance().configureWithError(&configureError)
assert(configureError == nil, "Error configuring Google services: \(configureError)")
GIDSignIn.sharedInstance().delegate = self
if NSUserDefaults.standardUserDefaults().boolForKey("kUserLogin")
{
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let root:UIViewController = mainStoryboardIpad.instantiateViewControllerWithIdentifier("RevealView")
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.rootViewController = root
self.window?.makeKeyAndVisible()
}
else
{
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewControlleripad : UIViewController = mainStoryboardIpad.instantiateViewControllerWithIdentifier("loginView") as UIViewController
let nav:UINavigationController = UINavigationController.init(rootViewController: initialViewControlleripad)
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.rootViewController = nav
self.window?.makeKeyAndVisible()
}
return FBSDKApplicationDelegate.sharedInstance().application(application,
didFinishLaunchingWithOptions:launchOptions) || true
}
this is my loginbutton function
func loginButton(loginButton: FBSDKLoginButton!,didCompleteWithResult result : FBSDKLoginManagerLoginResult!, error: NSError!)
{
print("come to ")
FBSDKGraphRequest.init(graphPath: "me", parameters: ["fields":"name,picture.type(large),gender,email"]).startWithCompletionHandler { (connection, result, error) -> Void in
print("come to print")
let strName: String = (result.objectForKey("name") as? String)!
print("before getting token")
var token = FBSDKAccessToken.currentAccessToken().tokenString
print(token)
print("after getting token")
print(result)
let Gender : String = (result.objectForKey("gender") as? String)!
let Id = (result.objectForKey("id") as? String)!
let deviceId = UIDevice.currentDevice().identifierForVendor?.UUIDString
let UUID = NSUUID().UUIDString
print(UUID)
var DeviceId = deviceId
print(DeviceId)
print(Id)
print(Gender)
print(strName)
var strEmail : String
if ((result.objectForKey("email") as? String) != nil){
strEmail = (result.objectForKey("email") as? String)!
}else {
strEmail = "not available"
}
//let strEmail: String = (result.objectForKey("email") as? String)!
var userID = result["id"] as! NSString
var facebookProfileUrl = "http://graph.facebook.com/\(userID)/picture?type=large"
// print(strEmail)
let request = NSMutableURLRequest(URL: NSURL(string: "http://projects.svirtzone.com/wified/api/authentication.php")!)
request.HTTPMethod = "POST"
let postString = "username=\(strName)&email=\(strEmail)&action=auth&pic=\(facebookProfileUrl)&dob=1989/05/05&logintype=Facebook&gender=\(Gender)&device_type=Mac&facebookid=\(Id)&agerange=15&deviceid=\(deviceId)&accesstoken=\(token)"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
guard error == nil && data != nil else { // check for fundamental networking error
print("error=\(error)")
return
}
if let httpStatus = response as? NSHTTPURLResponse where httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(response)")
}
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("responseString = \(responseString)")
}
task.resume()
}
if(error != nil){
print(error.localizedDescription)
return
}
print("before main view")
let defaults = NSUserDefaults.standardUserDefaults()
if !defaults.boolForKey("kUserLogin")
{
print("inside if***")
defaults.setBool(true, forKey: "kUserLogin")
}
NSUserDefaults.standardUserDefaults().setBool(false, forKey: "kUserLogin")
let app = UIApplication.sharedApplication().delegate as! AppDelegate
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewControlleripad : UIViewController = mainStoryboardIpad.instantiateViewControllerWithIdentifier("RevealView") as UIViewController
let nav:UINavigationController = UINavigationController.init(rootViewController: initialViewControlleripad)
app.window = UIWindow(frame: UIScreen.mainScreen().bounds)
app.window?.rootViewController = nav
app.window?.makeKeyAndVisible()
}
this is my main viewController(I set this with swrevealViewController) here I have side menu item "Menu" this Item is disabling now
#IBOutlet var Menu: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
Menu.target = self.revealViewController()
Menu.action = Selector("revealToggle:")
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
this is my google signIn code in appDelegate
func signIn(signIn: GIDSignIn!, didSignInForUser user: GIDGoogleUser!,
withError error: NSError!) {
if (error == nil) {
print("at delegate5")
// Perform any operations on signed in user here.
let userId = user.userID // For client-side use only!
let idToken = user.authentication.idToken // Safe to send to the server
let fullName = user.profile.name
let givenName = user.profile.givenName
let familyName = user.profile.familyName
let email = user.profile.email
var imageURL = ""
if user.profile.hasImage {
imageURL = user.profile.imageURLWithDimension(100).absoluteString
}
NSNotificationCenter.defaultCenter().postNotificationName(
"ToggleAuthUINotification",
object: nil,
userInfo: ["statusText": "Signed in user:\n\(fullName)","email" : email, "imageURL" : imageURL])
print( imageURL)
let postString = "username=\(fullName)&email=\(email)&action=auth&pic=\(imageURL)&dob=1989/05/05&logintype=Google&gender=female&device_type=Mac&facebookid=no&agerange=15&deviceid=not available&accesstoken=\(idToken)"
let request = NSMutableURLRequest(URL: NSURL(string: "http://projects.svirtzone.com/wified/api/authentication.php")!)
request.HTTPMethod = "POST"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
guard error == nil && data != nil else { // check for fundamental networking error
print("error=\(error)")
return
}
if let httpStatus = response as? NSHTTPURLResponse where httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(response)")
}
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("responseString = \(responseString)")
}
task.resume()
} else {
print("\(error.localizedDescription)")
NSNotificationCenter.defaultCenter().postNotificationName(
"ToggleAuthUINotification", object: nil, userInfo: nil)
let defaults = NSUserDefaults.standardUserDefaults()
if !defaults.boolForKey("kUserLogin")
{
print("inside if***")
defaults.setBool(true, forKey: "kUserLogin")
}
}
NSUserDefaults.standardUserDefaults().setBool(false, forKey: "kUserLogin")
let app = UIApplication.sharedApplication().delegate as! AppDelegate
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewControlleripad : UIViewController = mainStoryboardIpad.instantiateViewControllerWithIdentifier("RevealView") as UIViewController
let nav:UINavigationController = UINavigationController.init(rootViewController: initialViewControlleripad)
app.window = UIWindow(frame: UIScreen.mainScreen().bounds)
app.window?.rootViewController = nav
app.window?.makeKeyAndVisible()
}
Can any one help me please ?
Try this I hope it would be helpful!!!
After the login successfully set flag value in LoginViewController
let defaults = NSUserDefaults.standardUserDefaults()
if !defaults.boolForKey("kUserLogin")
{
defaults.setBool(true, forKey: "kUserLogin")
}
When You log out set the flag value and make Login Screen as root
NSUserDefaults.standardUserDefaults().setBool(false, forKey: "kUserLogin")
let app = UIApplication.sharedApplication().delegate as! AppDelegate
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewControlleripad : UIViewController = mainStoryboardIpad.instantiateViewControllerWithIdentifier("LoginViewController") as UIViewController
let nav:UINavigationController = UINavigationController.init(rootViewController: initialViewControlleripad)
app.window = UIWindow(frame: UIScreen.mainScreen().bounds)
app.window?.rootViewController = nav
app.window?.makeKeyAndVisible()
Step-2 Set Rootviewcontroller as SVrevealviewcontroller
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if NSUserDefaults.standardUserDefaults().boolForKey("kUserLogin")
{
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let root:UIViewController = mainStoryboardIpad.instantiateViewControllerWithIdentifier("RootViewController")
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.rootViewController = root
self.window?.makeKeyAndVisible()
}
else
{
let mainStoryboardIpad : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewControlleripad : UIViewController = mainStoryboardIpad.instantiateViewControllerWithIdentifier("LoginViewController") as UIViewController
let nav:UINavigationController = UINavigationController.init(rootViewController: initialViewControlleripad)
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.rootViewController = nav
self.window?.makeKeyAndVisible()
}
return true
}

Resources