3

This is the code I was using prior to UIAlertView being deprecated:

//OLD UIALERTVIEW
- (void)applicationDidBecomeActive:(UIApplication *)application {
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.

    // check to see if newer version exists to alert the user
    BOOL updateAvailable = NO;
    NSDictionary *updateDictionary = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:@"PLIST_URL_HERE"]];
    NSArray *items = [updateDictionary objectForKey:@"items"];
    NSDictionary *itemDict = [items lastObject];
    NSDictionary *metaData = [itemDict objectForKey:@"metadata"];
    NSString *newversion = [metaData  valueForKey:@"bundle-version"];
    NSString *currentversion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];

    //updateAvailable = [newversion compare:currentversion options:NSNumericSearch] == NSOrderedDescending; //default code
    updateAvailable = [@"1.3" compare:currentversion options:NSNumericSearch] == NSOrderedDescending; //force update
    //updateAvailable = [@"1.1" compare:currentversion options:NSNumericSearch] == NSOrderedDescending; //force no update

    if (updateAvailable) {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"UPDATE AVAILABLE" message:@"Click OK to update the application to the latest version" delegate:self cancelButtonTitle:@"Later" otherButtonTitles:@"OK", nil];
        [alert show];
    }
}


-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    if (buttonIndex != [alertView cancelButtonIndex]) {
        // point to the pList on dropbox to install the
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"PLIST_URL_HERE"]];
        exit(0);

I am trying to update it to use UIAlertController instead. heres the code I'm using for that

//NEW UIACTIONCONTROLLER
- (void)applicationDidBecomeActive:(UIApplication *)application {
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.

    // check to see if newer version exists to alert the user
    BOOL updateAvailable = NO;
    NSDictionary *updateDictionary = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:@"PLIST_URL_HERE"]];
    NSArray *items = [updateDictionary objectForKey:@"items"];
    NSDictionary *itemDict = [items lastObject];
    NSDictionary *metaData = [itemDict objectForKey:@"metadata"];
    NSString *newversion = [metaData  valueForKey:@"bundle-version"];
    NSString *currentversion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];

    //updateAvailable = [newversion compare:currentversion options:NSNumericSearch] == NSOrderedDescending; //default code
    updateAvailable = [@"1.3" compare:currentversion options:NSNumericSearch] == NSOrderedDescending; //force update
    //updateAvailable = [@"1.1" compare:currentversion options:NSNumericSearch] == NSOrderedDescending; //force no update

    if (updateAvailable) {
        UIAlertController * alert=   [UIAlertController alertControllerWithTitle:@"UPDATE AVAILABLE" message:@"Click OK to update the application to the latest version" preferredStyle:UIAlertControllerStyleAlert];

        UIAlertAction *ok = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"PLIST_URL_HERE"]];
        exit(0);
        }];

        UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil];

        [alert addAction:ok];
        [alert addAction:cancel];

        [self presentViewController:alert animated:YES completion:nil];
    }
}

problem is this code:

[self presentViewController:alert animated:YES completion:nil];

gives me this error:

/Users/liz/Desktop/CactusQueuesGit/CactusQueue/AppDelegate.m:154:15: No visible @interface for 'AppDelegate' declares the selector 'presentViewController:animated:completion:'

I can put that code in viewDidLoad but I don't want the alert popping up every time the users go to the TableView. I just want it to load ONLY when the app is opened.

how do I go about doing this?

Nicolas Miari
  • 16,006
  • 8
  • 81
  • 189
highboi
  • 673
  • 7
  • 28
  • Why not just add a check. If firstOpened { // show alert} to your viewdidload? – Beau Nouvelle Oct 15 '15 at 03:03
  • don't know why i didn't think of that. it works (doesn't nag) but i get this warning `Presenting view controllers on detached view controllers is discouraged` hope it doesn't cause issues later down the line – highboi Oct 15 '15 at 03:19
  • Try it in viewDidAppear and see if that changes anything. – Beau Nouvelle Oct 15 '15 at 03:22
  • viewDidAppear causes it to nag, I'm looking into abhinav's answer. if i can't get that to work ill just stick to viewDidLoad – highboi Oct 15 '15 at 03:29

2 Answers2

11

Per Apple Documentation, UIAlertController is just like any another view controller which needs a view controller as base to present on.

To present an UIAlertController from AppDelegate, you can create a temporary window with an empty view controller in it and present on top of it. Something like this:

self.alertWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.alertWindow.rootViewController = [[UIViewController alloc] init];
self.alertWindow.windowLevel = UIWindowLevelAlert + 1;
[self.alertWindow makeKeyAndVisible];
[self.alertWindow.rootViewController presentViewController:alertControl animated:YES completion:nil];

This window will automatically be taken out of memory once alert is dismissed.

You can also present your alert on rootViewController if it has been initialized and is added in view hierarchy.

I hope you got why you are getting this error now. Technically, AppDelegate is not a view controller and so does not respond to presentViewController:animated:completion: method.

Tim
  • 4,560
  • 2
  • 40
  • 64
Abhinav
  • 37,684
  • 43
  • 191
  • 309
  • 2
    The AppDelegate had a `UIWindow` property so creating a new window might not be necessary (use `self.window.rootViewController`). Haven't tested but would there be any problems with that? – Kevin Oct 15 '15 at 03:24
  • As I said in my post if `rootViewController` is set then this would definitely work. I had ended up in a situation where, somehow, I was not able to see my alert in app delegate due to launch image covering the root view controller. Creating a new window worked like a charm in my case :)! – Abhinav Oct 15 '15 at 03:27
  • yea i know why i got the error, i was looking for a way around it. gonna test this now otherwise ill settle with beau young suggestion – highboi Oct 15 '15 at 03:32
  • 1
    Sorry I should have been more clear. Wouldn't `rootViewController` always be set? – Kevin Oct 15 '15 at 03:33
  • Yes @Kevin. Please check my above edited comment where I ended up in a situation forcing me to use a new window :)! – Abhinav Oct 15 '15 at 03:35
  • @abhinav got the alert to show up (over a black screen which is fine) but hitting cancel doesn't dismiss the view. I've tried `[self.window popToRootViewControllerAnimated:YES];`, `[self.window.rootViewController dismissViewControllerAnimated:YES completion:nil]; and `[self.window dismissModalViewControllerAnimated:YES]` but none of them work. i also got this in the logger `Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior` – highboi Oct 15 '15 at 03:48
  • @Abhinav disregard that lost comment, it didn't work because i overlooked something, it works now. thanks a lot – highboi Oct 15 '15 at 03:52
  • Glad it worked... For a moment I was like what??> :). Happy to help. Could you please accept this answer for the benefit of others. – Abhinav Oct 15 '15 at 03:53
  • 1
    In storyboard based apps, by the time `didFinishLaunchingWithOptions:` is called, the root view controller is already set up. In other apps, you typically perform the setup yourself within that same method, so there is your handle to the view controller you are creating. – Nicolas Miari Jun 29 '16 at 02:02
2
  let navigationController = application.windows[0].rootViewController as UINavigationController

            let activeViewCont = navigationController.visibleViewController

            activeViewCont.presentViewController(alertController, animated: true, completion: nil)
  • Although this code might (or might not) solve the problem, a good answer always requires an explanation on how the problem is solved. – BDL Apr 06 '18 at 14:30