Search code examples
swiftnavigationuinavigationcontrolleruinavigationbarviewdidload

Swift Disable Navigation Controller buttons Until viewDidLoad is completed


VC1 is my homepage in the project and I can go to 5 different view controllers from VC1. In VC1 viewDidLoad I am downloading data from my Firestore database and I need them all to be loaded to the phone before allowing the user to interact with the buttons in the navigation bar. Here is how my VC1 look like (3 buttons bottom, 2 buttons top):

enter image description here

But currently user can click a button in VC1 while the data from viewDidLoad is not fully loaded to the phone and gets them to go from VC1 to VC2 (if they are fast enough, they can accomplish this).

How can I prevent a user to interact with the buttons in the nav bar unless all the functions in the viewDidLoad is completed?

override func viewDidLoad() {
    super.viewDidLoad()
func1()
func2()
func3()
}

Not sure if this is related but in my AppDelegate this is how I define my rootViewController:

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {

let storyboard = UIStoryboard(name: "Main", bundle: .main)
        window?.rootViewController = storyboard.instantiateViewController(withIdentifier: "TabBarController")
}

And this is what I have in main Storyboard:

enter image description here

and this is my home storyboard:

enter image description here


Solution

  • The user cannot touch any button while viewDidLoad is running. Or to be more precise: The buttons will not react upon events while those viewDid..., viewWill... functions are running. This is because all those methods are executed in the main thread and will block any user interaction.

    I assume your "problem" is caused because func1(), 2 or 3 are calling network services, which typically run in a arbitrary worker thread. Therefore, func1 returns before all data has been fetched, therefore also viewDidLoad returns too early for you.

    Since you should never run the network calls in the main thread (and Firestore calls won't do so either), you need to do some extra work, for example:

    • in viewDidLoad, disable all buttons you don't want the user to interact with
    • in the Firestore handlers, check if all data has been received, and then enable the buttons again (in the main thread, you might need to call DispatchQueue.main.async for this)
    • if you have multiple background jobs running and you need to wait for all of them, you could use DispatchGroup