I encountered this situation where I had added app state restoration APIs for the first time to a new app supporting iOS 9.3 and higher. The state restoration is working fine functionally, but I noticed that it was showing a screen snapshot instead of the LaunchScreen.xib content because of the delay during state restoration. By snapshot I'm referring to the automatic screenshot iOS takes of your apps UI as it goes into the background.
If you don't know what app state restoration is, it came out with iOS 6, here's the link from Apple on it:
Apple Documentation on App State Saving & Restoring
Showing the screen snapshot in this apps case is a significant problem as this particular app has about 4 seconds of delay, on an iPhone 4s running iOS 9.3.5, to do during app state restoration. Users would perceive the app to be hung since the launch / splash screen was not being shown during this time. This situation reproduces on all iOS versions currently available, on both simulator and device.
How can I prevent the snapshot from being shown, and force the LaunchScreen.xib to be used always, all while still preserving the app state save/restore functionality benefits?
After researching this I found that Apple has long ago provided a method off of UIApplication to deal with this situation. But its usage, even today, is poorly documented.
The solution is to use the ignoreSnapshotOnNextAppliationLaunch method from UIApplication.
Apple ignoreSnapshotOnNextApplicationLaunch method
You will have to access it via the UIApplication singleton pattern as suggested by Apple here as I will explain:
Apple UIApplication sharedApplication method
The where to use this is what is not clearly documented and that I am sharing here. The ignoreSnapshotOnNextApplicationLaunch method will have absolutely no effect unless specifically called when iOS is saving the app state from the view controller(s). Such as when you tap the home button to background the app.
You cannot call this method directly from the AppDelegate methods dealing with background / foreground transitions, as it needs to be called from the view controllers while their states are being saved for later restoration.
For this saving task Apple provides the encodeRestorableStateWithCoder method from UIViewController
Apple encodeRestorableStateWithCoder method
And this is where we need to make the change. If doing state restoration you should already have it; but by adding this method call to each view controller class where you have setup restoration IDs in storyboard, or are saving state manually, you can avoid any snapshots being used by including the ignoreSnapshotOnNextApplicationLaunch from the UIApplication singleton. This will not prevent iOS from taking the snapshot, just not showing it during app state restoration on re-launch.
// save any app state information that is not already saved automatically
- (void)encodeRestorableStateWithCoder:(NSCoder *)coder {
// prevent taking a screen shapshot and force launchScreen xib to be used always
[[UIApplication sharedApplication] ignoreSnapshotOnNextApplicationLaunch];
[super encodeRestorableStateWithCoder:coder];
return;
}
Be sure you re-background the app after adding this during your testing, to have iOS delete the previously saved snapshot file.
Related
Should my app be updated to Scene Delegate from App Delegate. My app supports ios 13.0 and up
first you have to understand what is difference
You could think of them as the global and private versions. One is shared and the other is limited to the individual owner. In a way, they are exactly what you would expect by the names.
Multi-window support is happening
Next time you create a new Xcode project you’ll see your AppDelegate has split in two: AppDelegate.swift and SceneDelegate.swift. This is a result of the new multi-window support that landed with iPadOS, and effectively splits the work of the app delegate in two.
From iOS 13 onwards, your app delegate should:
Set up any data that you need for the duration of the app.
Respond to any events that focus on the app, such as a file being shared with you.
Register for external services, such as push notifications.
Configure your initial scenes.
In contrast, scene delegates are there to handle one instance of your app’s user interface. So, if the user has created two windows showing your app, you have two scenes, both backed by the same app delegate.
Keep in mind that these scenes are designed to work independently from each other. So, your application no longer moves to the background, but instead individual scenes do – the user might move one to the background while keeping another open.
at last I will say that you can go with Scene Delegate
Courtesy of https://www.hackingwithswift.com/articles/193/whats-new-in-ios-13
I have a simple "timer" watchOS app that uses hierarchical navigation. I can press the digital crown to return to the watch Springboard, then tap the app icon and be returned to the same interface controller I was using.
If I return to the watch face and tap my app's complication, the app is launched, but appears to have restarted: I lose my current state. Is there any way to prevent this?
It sounds like you're asking how to stop your app being swapped out of memory?
If so, just like on iOS this isn't possible - and obviously the watch has less memory than a phone so is more likely to be swapped out.
What you need to do is store your state in some persistence layer - e.g. NSUserDefaults - so when the app restarts it can reload its state
If you open your watch app by tap complication, the app will automatically back to root interface controller(s). This is a system forced behavior.
If you want to preserve previous state, you need to change your app's hierarchic to page-based interfaces.
User interaction in page-based interfaces can be just like navigation-based interfaces. You can replace pushController(withName:context:), pop() and popToRootController() methods to becomeCurrentPage(). I also found switch from one page interface to another is also faster than navigation (push/pop) to another.
I wonder how to launch the App from background with specific scene instead of always starts with launch screen or main.storyboard's initial ViewController.
For Example, if the user was viewing the profile scene and then make the app go to background from there, I would like to launch the same profile scene(without reloading or the profile pictures, bio, etc) the next time the user bring the app to the foreground.
Now the case is that the App always start with the launch screen and or the information that was loaded before went away.
How can I remember the specific scene when App enters background? By the way, I noticed this issue when I refractored the storyboard.
That's exactly what state restoration is for!
State restoration is a feature in iOS that lets a user return to their app in the exact state in which they left it – regardless of what’s happened behind the scenes.
You can enable it from your appDelegate by overriding application:shouldSaveApplicationState: and application:shouldRestoreApplicationState:
and having them returning true.
Then you will have to apply restorationIdentifier to your viewControllers (and yes, you can do it from the storyboard :) ). Doing so will allow your user to come back to the very screen / screen hierarchy they were when they last left...
You will however have to handle the logic the the data that need be displayed. In your viewController subclass, you can override encodeRestorableStateWithCoder: and decodeRestorableStateWithCoder: to store and then retrieve your data to display from the coder.
find the Apple doc here : https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/PreservingandRestoringState.html
And a Ray Wenderlich (love the man!) here : https://www.raywenderlich.com/117471/state-restoration-tutorial
G'day iOS Guru's,
I have searched extensively for an answer, but can't find one (I bet the first response to my question will be to another similar question, but I cant find it).
Anyway, my problem is that I am running a simple map app that the user can drop pins on the map with a customised circle overlay around the dropped pin.
When the app goes into the background (iphone locked or home button pressed), if I re-enter the app within ~ 5 mins, the pins are still there and the app reopens to the last screen.
All good.
However, if I leave the app in the background for longer than 5 mins, the app restarts and all the pins are lost.
I have "Application does not run in background = NO" in the plist, and also enabled "App registers for location updates" under Required background modes.
How can I prevent the app from restarting after it enters the background and load the last opened view?
iOS can, and will, terminate your app while it's in the background if it needs additional resources to carry out whatever's going on in the foreground.
You need to ensure that your data is saved/archived when your app is terminated, and unpacked when re-launched so as to go back to where the user last was. The traditional way to do this is to use the applicationDidEnterBackground method, which is called when your app is suspended. You can then save all the data you need in order to resume cleanly in case your app is later terminated.
However, if you're targeting iOS 6 and upwards you can take advantage of a new feature - State Presumption & Restoration (link to documentation). State restoration off-loads some (but not all) of the heavy lifting onto iOS, and it can automatically snapshot your UI and provides easier ways to preserve and restore data.
in your AppDelegate.m
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
// Save your data
}
I am facing the above issue and unsure why it is happening or how to fix it. When the app goes to the background and is later reopened, it always starts from the initial view.
I would like it to show the view that was shown when the app was dismissed - which is usually the default for iOS apps.
Can someone please explain why this might be happening and how I can resolve it.
Your app is probably killed while in the background to free it's resources for other tasks.
If applicable you can opt to use a background mode for your app so it keeps running in the background. This is possible if you require location updates, play audio or interact with bluetooth le devices.
By your description it's more likely you want to implement State preservation and Restoration (Programmig Guide)
Even if your app supports background execution, it cannot run forever. At some point, the system might need to terminate your app to free up memory for the current foreground app. However, the user should never have to care if an app is already running or was terminated. [...]
The state preservation system in UIKit provides a simple but flexible infrastructure for preserving and restoring the state of your app’s view controllers and views.
If you are on an iPhone older that the 3GS, this is normal. Apps are closed instead of backgrounded on these older models.
The other possible reason is that you've set
UIApplicationExitsOnSuspend=YES
in your Info.plist. In that case, your app will also always terminate when going to the background.