2

What am i doing wrong? I have a database structure like the one shown in this image. my data structure
In appleDelegate.swift i just want to check if a certain user token actually exists under the "users" node. that is, if "users" has the child currentUserID (a string token). I understand observeSingleEvent is executed asynchronously.I get this error in swift: 'Application windows are expected to have a root view controller at the end of application launch'. in "func application(_ application: UIApplication" i have this code. I also have my completion handler function below.

if let user = Auth.auth().currentUser{
        let currentUserID = user.uid
        ifUserIsMember(userId:currentUserID){(exist)->() in
            if exist == true{
                print("user is member")
                self.window?.rootViewController = CustomTabBarController()
            } else {
                self.window?.rootViewController = UINavigationController(rootViewController: LoginController())
            }
        }

        return true
    } else {
        self.window?.rootViewController = UINavigationController(rootViewController: LoginController())
        return true
    }
}

func ifUserIsMember(userId:String,completionHandler:@escaping((_ exists : Bool)->Void)){
    print("ifUserIsMember")
    let ref = Database.database().reference()
    ref.child("users").observeSingleEvent(of: .value, with: { (snapshot) in
        if snapshot.hasChild(userId) {
            print("user exists")
            completionHandler(true)
        } else {
            print("user doesn't exist")
            completionHandler(false)
        }
    })
}
Jay
  • 34,438
  • 18
  • 52
  • 81
caa5042
  • 166
  • 3
  • 16
  • For your error, check this [question.](https://stackoverflow.com/questions/7520971/applications-are-expected-to-have-a-root-view-controller-at-the-end-of-applicati) I'm not sure reading the database in the app delegate before loading the root view controller is the best practice. I could be wrong but your app is trying to wait for Firebase to return a value before your app has a root view controller. If you give the app a root view controller then check for the data, does it give you the same error? If not, then you need to restructure your app. – temp_ Jul 10 '18 at 21:47
  • hmm...the idea was to automatically send the user to the customtabbarcontroller if the user exists under the "users" node...its supposed to be like an automatic login...and if the currentUserId is not found in the 'users' node then send the user to the login/register controller. i'm doing everything programmatically but maybe i can take the code out of applegate, link applegate to some kind of "launch screen" viewcontroller and do the check over there? – caa5042 Jul 10 '18 at 22:08
  • I am going with @temp_ on this. AppDelegate is not the best place to perform this function and moreover you are referring to a rootViewController and a self.window that doesn't exist yet. Moreover, all of that code could be replaced with 6 lines. Lastly, why don't you just read the *users/uid* node directly instead of loading in 10,000 users and checking to see if there's a uid child in the list? that would be a lot faster and again, less code. – Jay Jul 15 '18 at 14:02
  • . i wasnt aware that observesingleevent loads ALL the users just to check if that uid child node exists....please let me know the right approach to just check if the specified uid exists under "users".please elaborate on 'that code can be replaced with 6 lines' – caa5042 Jul 15 '18 at 16:37

1 Answers1

5

I would suggest moving the code out of the app delegate and into an initial viewController. From there establish if this is an existing user and send the user to the appropriate UI.

.observeSingleEvent loads all of the nodes at a given location - one use would be to iterate over them to populate a datasource. If there were 10,000 users they would all be loaded in if you observe the /users node.

In this case it's really not necessary. It would be better to just observe the single node you are interested in and if it exists, send the user to a UI for existing users.

here's the code to do that

    if let user = Auth.auth().currentUser {
        let ref = self.ref.child("users").child(user.uid)
        ref.observeSingleEvent(of: .value, with: { snapshot in
            self.presentUserViewController(existing: snapshot.exists() )
        })
    }

snapshot.exists will be either true if the user node exists or false if not so the function presentUserViewController would accept a bool to then set up the UI depending on the user type.

Jay
  • 34,438
  • 18
  • 52
  • 81