7

When in My iOS app which which is in the detail screen and I press home button which will result it in going to background mode. After inactivity of 7 around minutes, I relaunch it and it doesn't start from where I left it. It starts from the first screen.

I hit the internet and came to know about state preservation and restoration. I implemented in one screen but it doesn't seem to work. This is what I did in appDelegate.m

    //appDelegate.m

    -(BOOL)application:(UIApplication *)application shouldSaveApplicationState:(NSCoder *)coder
    {
        return YES;
    }

    -(BOOL)application:(UIApplication *)application shouldRestoreApplicationState:(NSCoder *)coder
    {
        return YES;
    }

Following code is in appDelegate.m in willFinishLaunchingWithOptions method. I am not using storyboard as this app is very old. It has XIBs. So this app always needs to go to login screen where it is checked if accessToken is stored, it will go to home screen from login screen. If not stored, it will remain in login screen. So this is mandatory to perform. Thus there is only one way to code this like below.

- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
   ...
   ...
   loginViewController = [[LoginViewController alloc] initWithNibName:@"LoginViewController" bundle:nil];

   self.navigationController = [[UINavigationController alloc]initWithRootViewController:loginViewController];
   self.navigationController.restorationIdentifier = @"NavigationController";
   [loginViewController.view setBackgroundColor:[UIColor whiteColor]];
   self.window.rootViewController = self.navigationController;
   ...
   ...
}

I have given restorationId to all view controller like below in viewDidLoad(). For example This is what I did in PetDetailViewController.m

    - (void)viewDidLoad
    {
        [super viewDidLoad];

        self.restorationIdentifier = @"MatchedPetIdentification";
        self.restorationClass = [self class];
    }

    -(void)encodeRestorableStateWithCoder:(NSCoder *)coder
    {
        [super encodeRestorableStateWithCoder:coder];
    }

    -(void)decodeRestorableStateWithCoder:(NSCoder *)coder
    {
        [super decodeRestorableStateWithCoder:coder];
    }

Now when I go to PetDetail screen and press home button, encodeRestorableStateWithCoder() gets called. Stopping app from xcode, relaunching it stays on same screen but immediately goes to login screen and transit to home screen(code in willFinishLaunchingWithOptions might be executing)

Am I doing anything wrong? How to prevent app from restarting from first screen unless user kills it manually?

Hiren Prajapati
  • 717
  • 3
  • 10
  • 25
  • Look at whether you have written anything to reset in viewWillEnterForeground or didBecomeActive methods. Also, you may want to check memory footprint of your app. Your app may be getting killed by System for some reason. – Puneet Sharma Oct 11 '17 at 08:40
  • @PuneetSharma thanks. It actually was resetting things when app becomes active. Is there any way I can track down why system is killing my app in background after few minutes? – Hiren Prajapati Oct 11 '17 at 09:10
  • @HirenPrajapati every app is killed if it's in background mode for more then 3 min and OS needs more memory for other apps. – Varun Naharia Oct 11 '17 at 09:22
  • @HirenPrajapati: Because you were resetting things in didBecomeActive, that gets called everytime ypu move app to foregorund, how do you say that the app is getting killed in suspended state? – Puneet Sharma Oct 11 '17 at 09:35
  • @PuneetSharma everyone is saying that iOS suspends the app few minutes after the app goes in background. – Hiren Prajapati Oct 11 '17 at 09:40
  • @HirenPrajapati: Yes it does, but the app is suspended, i.e, not running any code but it is not yet terminated. If there is no requirement(that arises because of memory crunch), the app will no be terminated and can be resumed from suspended state. – Puneet Sharma Oct 11 '17 at 09:42
  • @HirenPrajapati will you please post the code that is written in didBecomeActive method ? – Varun Naharia Oct 11 '17 at 09:59
  • @VarunNaharia Is 'didFinishLaunchingWithOptions' method called when restoration takes place? I have initialised a UIViewController as a root view controller in didFinishLaunchingWithOptions method. Do you think this is what forces the app to restart from first screen? – Hiren Prajapati Oct 12 '17 at 06:34
  • @PuneetSharma Is 'didFinishLaunchingWithOptions' method called when restoration takes place? I have initialised a UIViewController as a root view controller in didFinishLaunchingWithOptions method. Do you think this is what forces the app to restart from first screen? – Hiren Prajapati Oct 12 '17 at 06:35
  • @HirenPrajapati: No it does not get called, when app resumes from suspended state. willEnterForeground and didBecomeActive gets called. Are you still facing issue with your code? You mentioned you were resetting thing in didBecomeActive – Puneet Sharma Oct 12 '17 at 06:36
  • @PuneetSharma I actually checked and the code in didBecomeActive was just to call API and use response to update database. I didn't seen any screen initialisation in this method. And even I tried to comment this code and still not able to resolve this issue. – Hiren Prajapati Oct 12 '17 at 06:50
  • It is difficult to say what is getting wrong without seeing your code. Can you upload the code at github or somewhere. I can take a look at it. Also, are you performing any background task using backgroundTaskWithExpirationHandler? – Puneet Sharma Oct 12 '17 at 06:52
  • @HirenPrajapati if you want fix save UINavigationController.viewControllers list saved in user default and read back and On UINavigationController.viewController = {viewController from UserDefaults}, If it not works or you have any confusion let know about it – Varun Naharia Oct 12 '17 at 07:05
  • @PuneetSharma Okay, I came to know that detail in ViewController I'm testing on, encodeRestorableStateWithCoder and decodeRestorableStateWithCoder never get called. This is old project so it is using xib but everything is done programatically. Navigation controller is created in appDelegate programatically and all screens are navigated using initWithNibName method. I even wrote following in ViewDidLoad of testing class: self.navigationController.restorationIdentifier = @"MatchedPetIdentificationNavigation"; self.restorationIdentifier = @"MatchedPetIdentification"; – Hiren Prajapati Oct 12 '17 at 08:07
  • @VarunNahariaOkay, I came to know that detail in ViewController I'm testing on, encodeRestorableStateWithCoder and decodeRestorableStateWithCoder never get called. This is old project so it is using xib but everything is done programatically. I even wrote following in ViewDidLoad of this class: self.navigationController.restorationIdentifier = @"MatchedPetIdentificationNavigation"; self.restorationIdentifier = @"MatchedPetIdentification"; self.restorationClass = [self class] – Hiren Prajapati Oct 12 '17 at 08:07
  • @HirenPrajapati I have created a working example for please check my answer – Varun Naharia Oct 12 '17 at 08:50
  • @VarunNaharia thanks for giving example. It won't solve my problem. Do I need to call willEncodeRestorableStateWithCoder and didDecodeRestorableStateWithCoder methods? I have used XIBs, maybe thats why I'm stuck here. – Hiren Prajapati Oct 12 '17 at 11:46
  • @VarunNaharia this is in willFinishLaunchingWithOptions(): homeViewController = [[HomeViewController alloc] initWithNibName:@"HomeViewController" bundle:nil]; self.navigationController = [[UINavigationController alloc]initWithRootViewController:homeViewController]; self.navigationController.restorationIdentifier = @"NavigationController"; self.window.rootViewController = self.navigationController; This is causing app to restart. Please can you solve this? – Hiren Prajapati Oct 12 '17 at 11:48
  • @HirenPrajapati when I tried the example app it is giving me the last opened UIViewController even after terminating app from background, How it is not working for you, there is something that you are unable to explain here that's why it's taking this much time to solve your problem, please add more information images(gif) – Varun Naharia Oct 12 '17 at 11:54
  • @HirenPrajapati please add code in question it's hard to read in comment, yes your problem can be solved it's just taking time in understating your problem correctly – Varun Naharia Oct 12 '17 at 11:55
  • @VarunNaharia Hey, I've just edited my question. Please go through it. I guess this will make you understand what is happening. Replay ASAP – Hiren Prajapati Oct 12 '17 at 12:13
  • @VarunNaharia I mandatory initialise loginviewcontroller as root view controller. In loginviewcontroller class, I check if user already has logged in or not. This is explained in edited question. – Hiren Prajapati Oct 12 '17 at 12:15
  • @HirenPrajapati so your basic need is Open Login Screen if user not logged in and open home screen if user is logged in ? Correct me if I am understanding wrong – Varun Naharia Oct 12 '17 at 12:20
  • @VarunNaharia wether user logged in or not, it is compulsory for the app to go to loginscreen. In loginscreen's viewWillAppear(), I check if login token is already stored, app will go to home screen itself. For further info check edited question in case you haven't – Hiren Prajapati Oct 12 '17 at 12:23
  • @HirenPrajapati you are doing it wrong way all your code is unnecessary if you user is already logged in then why show him login screen you can just check user login status behind the scene and decide with controller to show – Varun Naharia Oct 12 '17 at 12:27
  • @VarunNaharia This is an old app. I do not have rights to change its old functionality rather than adding new ones. And I guess this isn't an issue which is causing my current problem. In short, LoginViewController is a homescreen. After go out of app when in detail screen, relaunch the app, it should remain in detailscreen(in this case: PetDetailViewController). But it always goes to first screen. I hope you now completely understand my problem. – Hiren Prajapati Oct 12 '17 at 12:37
  • @HirenPrajapati please check updated answer – Varun Naharia Oct 12 '17 at 12:47
  • @VarunNaharia you completely got it wrong. This is not about login.Take loginviewcontroller as homescreen. In willFinishLaunchingWithOptions(), I initialise homescreen. I guess this is the only way in Xib project you can initialise any viewcontroller as an initial viewcontroller. My problem is when app goes in background while in detailscreen, few minutes after relaunching it starts from homescreen. I want app to relaunch with detail screen. So this is about state preservation and restoration. So I implemented this. But it still calling homescreen. Check edited question please. – Hiren Prajapati Oct 12 '17 at 13:00

1 Answers1

1

You cannot control when your app is put into a suspended state from the background state, the OS will automatically do this to allow more memory for foreground apps. For more information on state transitions and app termination you can refer to:

https://developer.apple.com/library/content/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/TheAppLifeCycle/TheAppLifeCycle.html

  • the link you provided just shows UIKit documentation https://developer.apple.com/documentation/uikit/app_and_environment/scenes/preparing_your_ui_to_run_in_the_background – Pavel Bogart Oct 19 '20 at 17:05