Home > Software engineering >  How to dismiss view controller from SceneDelegate?
How to dismiss view controller from SceneDelegate?

Time:11-06

I am working on an ios App (Swift 5) and I am trying to show a screen if the app is offline and then dismisses when the user re-connects.

I am expecting the OfflineViewController to appear when the user is offline, and the last screen the user on to appear if they are connected.

What is happening is the OfflineViewController is appearing when I disconnect from the network, however it does not go away when I connect back to the network. I tried adding a button to dismiss and this does not work either.

I've attached my code below, any idea what am I doing wrong?

SceneDelegate

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?
    let reachability = try! Reachability()


    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        
        guard let _ = (scene as? UIWindowScene) else { return }

        // Send to homepage if logged in, otherwise login screen.
        let accessToken: String? = KeychainWrapper.standard.string(forKey: "accessToken")
        
        // If access token exists, skip login page
        if accessToken != nil {            
            if let windowScene = scene as? UIWindowScene {
                self.window = UIWindow(windowScene: windowScene)
                let mainStoryboard:UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
                let vc = mainStoryboard.instantiateViewController(withIdentifier: "homeTabController") as! TabBarController
                self.window!.rootViewController = vc
            }
        }

        reachability.whenUnreachable = { [self] _ in
            print("Not reachable (Scene delegate)")

            let storyboard = UIStoryboard(name: "Main", bundle: nil)
            
            let vc = storyboard.instantiateViewController(withIdentifier: "OfflineViewController") as! OfflineViewController
            vc.modalPresentationStyle = .fullScreen

            guard let firstScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
                return
            }

            guard let firstWindow = firstScene.windows.first else {
                return
            }
            
            let rootVC = firstWindow.rootViewController
            rootVC?.dismiss(animated: true, completion: nil)
            rootVC!.present(vc, animated: true, completion: nil)

        }

        do {
            try reachability.startNotifier()
        } catch {
            print("Unable to start notifier")
        }
    }
}

OfflineViewController

import UIKit

class OfflineViewController: UIViewController {
    
    let reachability = try! Reachability()

    override func viewDidLoad() {
        super.viewDidLoad()

        do {
            try reachability.startNotifier()
        } catch {
            print("Unable to start notifier")
        }
    }
    
    @IBAction func hitRefresh(_ sender: Any) {
        reachability.whenReachable = { reachability in
                self.dismiss(animated: true, completion: nil)
        }
    }
}

CodePudding user response:

I would start be removing all of the reachability code from OfflineViewController. The logic to dismiss belongs with the logic to present.

Then update the reachability code in the scene delegate with a whenReachable block that dismisses the current OfflineViewController.

You should also avoid using UIApplication.shared.connectedScenes in the scene delegate code. You already know the scene. No need to go find it.

The updated whenUnreachable:

    reachability.whenUnreachable = { [self] _ in
        print("Not reachable (Scene delegate)")

        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        
        let vc = storyboard.instantiateViewController(withIdentifier: "OfflineViewController") as! OfflineViewController
        vc.modalPresentationStyle = .fullScreen

        guard let winScene = (scene as? UIWindowScene) else { return }

        guard let firstWindow = winScene.windows.first else {
            return
        }
        
        let rootVC = firstWindow.rootViewController
        rootVC?.dismiss(animated: true, completion: nil)
        rootVC?.present(vc, animated: true, completion: nil)
    }

The added whenReachable:

    reachability.whenReachable = { [self] _ in
        print("Reachable (Scene delegate)")

        guard let winScene = (scene as? UIWindowScene) else { return }

        guard let firstWindow = winScene.windows.first else {
            return
        }
        
        let rootVC = firstWindow.rootViewController
        rootVC?.dismiss(animated: true, completion: nil)
    }
  • Related