How to present an alert window in didFinishLaunchingWithOptions?

in AppDelegate.swift, how to present an alert window in didFinishLaunchingWithOptions?

I used window?.rootViewController?.present(alert, animated: true), but rootViewController was nil, so nothing was shown.


The app was starting with a storyboard.

Why do you want to put the alert here, and not when the first view shows ?


But you can addd this just before return:


        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()) {
            self.window?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()
            let alert = UIAlertController(title: "", message: "Alert just at launch", preferredStyle: .alert)
            self.window?.rootViewController?.present(alert, animated: true, completion: nil)
        }

The app wants to do iCloud account or connection check in didFinishLaunchingWithOptions,

if iCloud not available, present an alert to the user to enable it.


If adding the following line in didFinishLaunchingWithOptions, will the app initiate another one later? (double instances)

self.window?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()

Yes, that's effectively the problem.


And in fact, it would be much easier to do it in viewDidLoad of the initial controller or better in viewDidAppear.


Whjat would be your problem doing so ?


You could manage to hide all the content until the alert is passed ?

I am trying to do an iCloud app refering to

https://developer.apple.com/library/archive/samplecode/CloudKitShare

as you can see from

https://developer.apple.com/library/archive/samplecode/CloudKitShare/Listings/CloudShares_AppDelegate_swift.html#//apple_ref/doc/uid/TP40017580-CloudShares_AppDelegate_swift-DontLinkElementID_3


AppDelegate has a func handleAccountUnavailable to present the alert window.

Also, in didFinishLaunchingWithOptions, it has the following:

let storyboard = UIStoryboard(name: StoryboardID.main, bundle: nil)

let mainNCOrNil = storyboard.instantiateViewController(withIdentifier: StoryboardID.mainNC)

let zoneNCOrNil = storyboard.instantiateViewController(withIdentifier: StoryboardID.zoneNC)


window?.rootViewController = MenuViewController(mainViewController: mainNC, menuViewController: zoneNC)

window?.makeKeyAndVisible()


But my app is very simple, only have a UITabBarController as the start point of main.

How can I achieve the same?

If I understand the code, they do not call alert directly in AppDelegate (logical), but add observers for further notification.

The other solutions may well work - I don't know, I don't do Swift. But this is a classic problem and is solved for the general case in Objective C with the following:


// present the alert with the following: 
         [self presentThisViewController:alert];


// and implement this method:



-(void)presentThisViewController:(UIViewController *)alert{
    UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
    while (true){
        if (topController.presentedViewController) {
            topController = topController.presentedViewController;
        }else if([topController isKindOfClass:[UINavigationController class]]) {
            UINavigationController *nav = (UINavigationController *)topController;
            topController = nav.topViewController;
        }else if([topController isKindOfClass:[UITabBarController class]]) {
            UITabBarController *tab = (UITabBarController *)topController;
            topController = tab.selectedViewController;
        }else{
            break;
        }
    }
    [topController presentViewController:alert animated:YES completion:nil];
}

At the end of didFinishLaunchingWithOptions, if checkAccountStatus checks account not available,

handleAccountUnavailable will present the window inside AppDelegate?

if rootViewController is nil, the present will not happen?

rootViewController will never be nil. The problem is - it might not be the top viewController so you have to move to its presentedViewController if it is presenting a viewController.

This is great.


What if the topController turns out to be an UIAlertController (either system or app presented)? Will presenting an alert on an alert fail?

I’ll give you 2 answers. The better answer is - try it. The less good answer is an alert view controller can indeed present an alert view controller.

it is nil in my case, I didn't do anything indidFinishLaunchingWithOptions though.


var top = UIApplication.shared.keyWindow?.rootViewController

while (true){

if (top?.presentedViewController) != nil {

top = top?.presentedViewController

}else if let nav = top as? UINavigationController {

top = nav.topViewController;

}else if let tab = top as? UITabBarController {

top = tab.selectedViewController;

}else{

break;

}

}

top?.present(alert, animated: true)

should I set a flag, and show the alert window in didLoad of UITabBarController?

You want :

The app wants to do iCloud account or connection check in didFinishLaunchingWithOptions,

if iCloud not available, present an alert to the user to enable it.


So, why not make this test in the initial window viewDidLoad ? Il will be easy to alert at this stage.


In addition, it is not a good idea to delay didFinishLaunching by network requests that can last. As explained in WWDC15:


// func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?)

// WWDC15 CocoaTouch Optimization: Need to keep this very short to return as fast as possible, so

// that app launches quickly

// Otherwise, put long tasks in a backgroundQueue

// let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

// dispatch_async(globalQueue) { // defer work until later access to database... }

It is unlikely that at the end of didFinishLaunchingWithOptions: there will be no rootViewController. Usually (if not always?) the rootViewController has been assigned before the method runs. If not, you must assign it in the method otherwise the app won't start.


Check again to see if, in your case, top==nil. I suspect it doesn't.

How to present an alert window in didFinishLaunchingWithOptions?
 
 
Q