51

I am trying to get my iPhone app to restart programmatically when the Logout button is pressed.

Has anyone got a code sample to share? I've read that it is possible by modifying the main.m file but I couldn't find any code related to this.

Any help would be appreciated.

Will
  • 449
  • 5
  • 23
manospro
  • 799
  • 1
  • 6
  • 16
  • possible duplicate of [Proper way to exit iPhone application?](http://stackoverflow.com/questions/355168/proper-way-to-exit-iphone-application) – Brad Larson Dec 09 '10 at 15:09
  • 12
    You can't actually force it to restart. You could force it to die, but you'll probably get rejected for it. Instead you should design your app so that you can programmatically clear out all the data state and reset it yourself. – Jason Coco Dec 09 '10 at 15:09
  • As long as the crash is initiated by the user, it shouldn't get rejected. For example, a banking app may alert the user that something is not in sync, or too long as passed since they were last active, and will have to log in again, and offer a "quit now" button (as the only choice). This is a non-issue because the user expects it to happen. Not saying that this is the best or only way to reset/log out, but it is acceptable, as far as Apple is concerned, last time I checked. – Daniel Springer May 25 '20 at 17:53

7 Answers7

66

Note:

Although this has been answered as 'not possible' I think it's a valid question for a new iOS developer to ask and there is something they can do which is probably what they want.

There is a way to 'restart' your app from the user's perspective which is not technically restarting or exiting the iOS App. As pointed out by other answers an iOS App should never explicitly exit because this is not allowed on iOS.

My Answer:

If you want your app to go back to the state it was in at launch this is not 100% possible but I will explain a way to get most of the way there that should be sufficient for all valid purposes.

The first thing to do is to re-create your root view controller. I recommend doing this from a method in the app delegate like this:

- (void)resetAppToFirstController
{
    self.window.rootViewController = [[MyMainViewController alloc] initWithNibName:nil bundle:nil];
}

In many cases this will be enough, but any app-state that you have should also be reset in this method. For example log out a user, reset any non-persistent state, and nullify (release) all objects that you can. This method can also be used to initially create your first view controller from application:didFinishLaunchingWithOptions.

Framework classes and Singletons:

You will not be able to completely reset the state of any framework singletons or per-app instances, such as these:

[UIApplication sharedApplication];
[NSNotificationCenter defaultCenter];
[NSUserDefaults standardUserDefaults];
[UIScreen screens];
// etc...

That is probably fine as you shouldn't be storing any non-persistent state in these anyway (except NSNotificationCenter, but all registered observers should have been removed when the objects were released). If you do want to initialise or reset any framework state you can do it in the same resetAppToFirstController method. Anyway, there should be no need to re-create these, or the window object.

If you have any of your own singletons, you can re-create these by storing them in a singleton-holder class (which is itself a real singleton). Conceptually, this is a simple singleton class with properties for each of your other singletons and a reset method to nullify and release them all. Your other singletons should use this class (instead of a static or global variable) to store singleton instances. Be careful if you use any third party libraries as they may also employ singletons and you will need to ensure these also use your singleton-holder so that you can reset them as needed. I think this technique is good practice anyway, because in some cases (for example unit testing) you do want objects which are usually singletons to go away and reinitialise to a pristine state. However, you don't want to couple the singleton implementations with your singleton-holder, so a good way to implement this is by using an NSMutableDictionary as an associated object on [UIApplication sharedApplication] with the singleton class names as keys. However, I'm going off topic a bit as this is a more advanced technique beyond the scope of this question.


The above should be sufficient to 'reset' your application as far as the user is concerned. You can even show the splash screen again if you want as your first view controller.

jhabbott
  • 16,761
  • 8
  • 56
  • 91
20

First of all, although it is possible to force kill your app, this is not allowed by Apple and will rejected. Even if it wasn't rejected, there is no way to restart your app once it's killed. You just need to find some way to reset your app through your code, as Jason Coco said. It might be more work, but it's worth it to not get rejected by Apple.

sudo rm -rf
  • 28,958
  • 19
  • 100
  • 160
  • The relevant guidelines are here: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/MobileHIG/MobileHIG.pdf – Julian Sep 11 '12 at 02:22
  • 26
    Further to the above comment, which was prematurely cut off: "Never quit an iOS application programmatically because people tend to interpret this as a crash. However, if external circumstances prevent your application from functioning as intended, you need to tell your users about the situation and explain what they can do about it." So don't quit on your user unexpectedly, but if this is a user-initiated action, with an explanation, then Apple has no problem with it. We use exit(0) in 4 approved apps. – Julian Sep 11 '12 at 02:34
15

try this, it works for me.

-(void)restart
{
    MyAppDelegate *appDelegate = (MyAppDelegate *)([UIApplication sharedApplication].delegate);
    [appDelegate.navigationController popToRootViewControllerAnimated:NO];
    UIViewController *topViewController = appDelegate.navigationController.topViewController;
    Class class = [topViewController class];
    NSString *nibName = topViewController.nibName;
    UIViewController *rootViewcontroller = (UIViewController *)([[class alloc] initWithNibName:nibName bundle:nil]);
    [appDelegate.navigationController.view removeFromSuperview];
    appDelegate.navigationController.viewControllers = [NSArray arrayWithObject:rootViewcontroller];
    [appDelegate.window addSubview:appDelegate.navigationController.view];
    [appDelegate.window makeKeyAndVisible];
}
roberto.buratti
  • 2,407
  • 1
  • 14
  • 10
  • 2
    Yes I know, the idea is to set the app a state that is as near as possible to the "initial" state... This trick works well ONLY if your app is based on a navigation controller but the idea is easily applicable to other situations. – roberto.buratti Feb 14 '13 at 20:45
9

Objective-C:

exit(0);

Swift:

exit(0)

I have this on 2 live apps and they haven't been rejected. One of my apps even has this line of code as a feature in the home page of my app. Its located at the top right corner, where tweet button is on Twitter. So don't worry about the Apple rejecting unless it looks like an unexpected crash.

enter image description here

Esqarrouth
  • 35,175
  • 17
  • 147
  • 154
5

Here is how you can do this on simulator using private API:

    NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@", scheme, endpointString]];

    Class pClass = NSClassFromString(@"BKSSystemService");
    id service = [[pClass alloc] init];

    SEL pSelector = NSSelectorFromString(@"openURL:application:options:clientPort:withResult:");
    NSMethodSignature *signature = [service methodSignatureForSelector:pSelector];
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
    invocation.target = service;
    NSString *app = @"com.apple.mobilesafari";
    [invocation setSelector:pSelector];
    [invocation setArgument:&URL atIndex:2];
    [invocation setArgument:&app atIndex:3];
    unsigned int i = [service performSelector:NSSelectorFromString(@"createClientPort")];
    [invocation setArgument:&i atIndex:5];
    [invocation invoke];
    exit(0);

This also should work on jailbroken apps with appropriate entitlements.

For other apps simple html page can be used:

    NSString *format = @"https://dl.dropboxusercontent.com/s/rawt1ov4nbqh4yd/launchApp.html?scheme=%@&URL=%@";
    NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:format, scheme, endpointString]];
    [[UIApplication sharedApplication] openURL:URL];
    exit(0);

Unfortunately, internet connection is needed in this case.

Bohdan Orlov
  • 264
  • 3
  • 3
  • This is an interesting view into internals. Of course it's using private Apple API which would be rejected from being put into AppStore. I upvoted it because I needed a solution only for testing purposes. Unfortunately your first code does no longer work on iOS 9 (maybe the method signature has changed…) - it works with iOS 8.4 simulator. The 2nd one works on any simulator, but it's a bit tricky to find out what the HTML code should do… – konran Feb 06 '16 at 21:11
3

I exit during -applicationWillResignActive: if the app is on the start screen and Apple has accepted it for years. To the user it will not look like a crash. Next time the user starts the app from the icon. An additional check that does not quit the app if it was launched today could be useful for the user-experience in some cases.

- (void)applicationWillResignActive:(UIApplication *)application
{
    // called if phone-call comes in!
    if([gameController isGameFinished])
        exit(0);
}
cat
  • 2,721
  • 1
  • 21
  • 28
  • 1
    it's exit, not restart – user924 Apr 17 '18 at 12:49
  • Its up to you to save the sate of your app at this point and restore it at the next launch, which makes this effectively a restart. In old versions of iOS you had to save state anyway as switching between apps involved an exit. – cat Aug 31 '18 at 10:44
3

Put this in a UIAlertAction asking the user "Save and Quit". It will animate the exit(0) so at least it looks planned.

- (void)saveAndQuit
{
    [UIView animateWithDuration:0.8 animations:^{
        self.window.alpha = 0.0; // fade out...
        // ... while pinching to a point
        self.window.transform = CGAffineTransformScale(
                CGAffineTransformMakeTranslation( 0, 0 ), 0.1, 0.1 );
    } completion:^(BOOL finished) {
        exit(0);
    }];
}
Jeff
  • 2,395
  • 20
  • 39