6

So I have a view controller which is dismissed when a user logs in. In the dismiss function completion block I have fired a function which should reload the next view controller. Nothing happens, blank view controller. But if I close the application when I am already logged in, then reopen it, everything loads like it should.

So here is my login function:

func loginFunc() {

        if emailField.text != "" && passwordField.text != "" {

            Auth.auth().signIn(withEmail: emailField.text!, password: passwordField.text!, completion: { (user, error) in

                if user != nil {
                    // Sign In Successful
                    print("Sign In Sucessful")
                    self.dismiss(animated: true, completion: {
                        self.mainVC.starterMethod()
                    })
                } else {

                    if let myError = error?.localizedDescription {
                        print(myError)
                    } else {
                        print("Error")
                    }

                }

            })

        }

    }

And here is my starterMethod():

func starterMethod() {

        ref = Database.database().reference()

        let userId = Auth.auth().currentUser?.uid

        if userId != nil {
            print("You are logged in...")
        } else {
            present(LoginController(), animated: true, completion: nil)
        }

        setupPicks()
        setupViewsMed()
        fetchGames()
        setNavigationBar()
        setupCircles()

    }

starterMethod() is called in the viewDidLoad

I would really like to avoid viewDidAppear() if possible

  • Is your `completionBlock` @escaping? if it is, you have memory leak (it is bad, but it should work because self is persisted in memory and has access to `mainVC`. if it's not @escaping, you definitely will freeze the UI, and there won't be any memory leaks, BUT after dismiss completion handler, self no longer exists hence mainVC should not exist – farzadshbfn Aug 12 '17 at 11:29
  • You can even use Notification. delegate sounds fine as others said, but authenticating is more abstract than a hook between two VCs, after user successfully logged in, send a Notification, and add an observer in MainVC – farzadshbfn Aug 12 '17 at 11:31
  • @farzadshbfn How would implementing Notification and observer look like. Could you please help me a little more? :) –  Aug 12 '17 at 20:04
  • I don't have access to my computer at the moment, but this thread will give you the basic idea... https://stackoverflow.com/questions/2191594/send-and-receive-messages-through-nsnotificationcenter-in-objective-c – farzadshbfn Aug 12 '17 at 20:10
  • @farzadshbfn Great! Thanks –  Aug 12 '17 at 20:22

4 Answers4

15

Prior to iOS12 dismiss (and it's completion) is not called if there is no presentingViewController. On iOS13+ dismiss doesn't work, but it's completion is called

Vadim Zhuk
  • 334
  • 2
  • 10
  • Hi Vadim Zhuk, how did you find out this? Is it documented somewhere by Apple? – Hieu Dinh Nov 04 '20 at 07:34
  • No, it's not documented by Apple - mb this is a bug, but this method(and its completion) should work only in case of modal presentation, at the same time the completion is called (on ios13+) even if the dismiss method doesn't work – Vadim Zhuk Nov 21 '20 at 18:52
  • I noticed that the completion also is not called when isBeingDismissed already was true on iOS 12 and older. On iOS 13 and newer isBeingDismissed is true during interactive dismissal (that can be aborted), so there it's better to rely on the completion. – Sebastian Kirsche Jun 15 '21 at 19:41
3

For that i think Delegate method is best suited for you. Because dismissed view controller want to notify to presenting view controller.

From Apple's documentation.

When it comes time to dismiss a presented view controller, the preferred approach is to let the presenting view controller dismiss it. In other words, whenever possible, the same view controller that presented the view controller should also take responsibility for dismissing it. Although there are several techniques for notifying the presenting view controller that its presented view controller should be dismissed, the preferred technique is delegation.

Jaydeep Vora
  • 6,085
  • 1
  • 22
  • 40
2

From your code below

self.dismiss(animated: true, completion: {
     self.mainVC.starterMethod()
})

from the code i can assume that you have instance of mainVC (self.mainVC) created in current class which is being displayed.

After current view is dissmissed you might probably loosing reference of you mainVC and causing the issue.

Solution :

You can use delegate method or Notification to listen to the event in main vc.

You need to create this below protocol in your Current view controller @protocol LoginDoneProtocol { func loginAndDismissed() } confirms to the protocol in your mainVC, like

Class mainVC : UIViewCOntroller , LoginDoneProtocol {
}

You can implement the delegate method in your mainVC, inside the delegate method call your method to reset the values

func loginAndDismissed(){
    starterMethod()
}

On the comppletionBlock of Dismissing your viewcontroller you can simply call

self.dismiss(animated: true, completion: {

delegate.loginAndDismissed()

})

Note : You must assign delegate to current View Controller from your mainVC when your current View Controller is presented.

You will many tutorial on how to create delegate.

Devang Tandel
  • 2,988
  • 1
  • 21
  • 43
0

I managed to figure out the solution. I imagine it is the easiest approach and very reliable.

First of all I declared a constant:

let LOGGED_IN_NOTIFICATION = NSNotification.Name("LoggedInNotification")

Then in the dismiss method completion block I added:

NotificationCenter.default.post(name: LOGGED_IN_NOTIFICATION, object: nil)

And finally added an observer in the mainVC's viewDidLoad method

override func viewDidLoad() {
    super.viewDidLoad()

        NotificationCenter.default.addObserver(forName: LOGGED_IN_NOTIFICATION, object: nil, queue: nil) {
            notification in

            print("Logged in")

            self.reset()

            self.starterMethod()

        }

    self.starterMethod()

}