Restore state every single time? - ios

My app has a view controller that has to be restored even if the user kills the app himself, and shouldRestoreApplicationState doesn't do that; if the user killed the app I get back to the first controller.
So is there a way to do what I want, get the state to be restored every single time?
If that's impossible with state restoration, I thought maybe I could save the view controller to my persistent store and present it from my appDelegate, but I couldn't figure that out yet. I'd have to rebuild the whole navigation stack, including a tab bar controller and multiple navigation controllers up to that view, from the appDelegate. Is that a good idea? How should I go about that?
I tried variations of this:
let bookInfoStoryboard = UIStoryboard(name: "BookInfo", bundle: nil)
let controller = bookInfoStoryboard
.instantiateViewControllerWithIdentifier("bookInfoTableViewController")
self.window?.makeKeyAndVisible()
self.window?.rootViewController!.presentViewController(controller, animated: true, completion: nil)
But I keep getting various errors.
Ideally I wanted to restore the state of every view controller, but it must happen every time. Is there a better way to do that?
Thanks,
Daniel

Persisting view controllers is not possible, as they carry a lot of transient information and very little persistent one.
What you can do is to save the configuration that represents the hierarchy of view controllers to user defaults, and rebuild from there. For example assuming you have a tab view with file system like navigation, you can persist to user defaults a string like this: bookmarks.folder1.subfolder2.file18.playing.position=19.28. This might correspond to a video file being played and reaching 19.28 seconds before the application crashed, or was terminated by OS.
By using schemas like the one above you can restore the controllers to a state very close to the one before the last session ended. And the best part is that the user doesn't even have to know that your application started all over, he'll get the same experience as the last time he navigated through the application.
Ofcourse the restoral depends on the complexity level of the application, and might not be applicable for all flows within the application, however for such cases you can at least bring the user back to an application state closer to the one before exit.

Fighting against standard behaviour like this is probably a bad idea. That behaviour is there in case there is a bug in your app and it keeps restoring bad state and getting stuck.
If you really want to do this, you can’t use UIKit’s state restoration system so you’ll have to build your own. This used to be very common before the UIKit feature was added in iOS 6. Cristik’s answer is a good starting point for implementing such as system.

Related

iOS - Constant listening to database update implementation

I have a project where I have 4 tab bars, and when i switched tabs, my api to get the API request to update my view is in the method viewDidAppear().
However this will make a very bad UX experience for user as whenever they switch tab, a loading icon and some times it load for 0.5 seconds but that instant appearance and disappearance of the loading icon is really bad for UX perspective. How can i implement a view where whenever the database value change, my view is automatically updating itself?
I have thought of using Timer() to implement where it calls the api every second but this is not practical as it will be constantly calling the API every second.
Does anyone have any suggestion? Thank you
There's a lot to unpack here, but I'll try to provide a generalized answer and point to several possible solutions to the UX problem.
There's several aspects that can play a role here:
Do you control the backend the data comes from, i.e. is it possible to follow paiv's comment and implement push notification? Or perhaps use some other means than pure HTTP(S) requests, like a websocket?
How frequently does data change? Is it realistic that it does so in between switching from one tab to another?
On that note, what do you do when data changes while your user is on one tab? Does that get refreshed in any way? Is it a problem if they don't get informed about this immediately? Is there a refresh button?
Do you persist the data beyond a view's on-screen time at all? Is it scrapped when a user re-visits a tab, i.e. does a tab always start "empty" until the API response is handled or do users see the values from the last time they visited that tab?
What best to do depends a lot on the exact answers to these questions. If you cannot enable the backend to do anything besides serving HTTP requests AND have to keep all data up-to-date constantly then obviously you have to do something like the polling you described. The update-interval depends on how important this is for your users. Even if it is 1 second, be aware that this means they could visit a tab before the next update has been fetched.
If you can adapt the backend and up-to-date data is a must, then push notifications or perhaps websockets are a way to go (keep in mind that websockets mean one open-connection per active user then!).
Push notifications would probably break down when the update interval is too high (i.e. if data gets changed in the backend very quickly and you keep spamming them), but would work nicely otherwise, plus they would update views that are already on screen if used correctly.
In 90 % of the cases I've seen, however, going with regular HTTP requests and a properly designed UI is the best choice. Properly decoupling the UI code from the data loading code and the code that displays the data is also very important. Here's how I would handle this:
Properly persist the data in some way in the app that does not require on views owning the data alone. If you want to persist beyond app runtime that means CoreData (or an equivalent), otherwise an in memory storage may be fine (that is kind of a global state then).
In your UI, show any data you currently have, even if it is "outdated" (in your case: "left over from the last time the user was on this tab"). Design the UI so that this is clearly apparent (grey it out, put tiny activity indicators next to the data while it is being loaded anew/updated, or something similar).
As said in 2. do show if the app is loading data. Initiating the loading when the users go to a tab is okay, but don't make a UI that is completely dominated by it (like one giant, empty view with a spinner on it).
Functionally, the loading should not directly update any views, instead it writes new data to your storage and just informs views and view controllers that they need to update themselves (if you're using CoreData and/or Combine you get a lot of this logic basically for free, especially for a SwiftUI app).
Such a design also lends itself to later being adapted for push notifications: While at first the re-loading is triggered by a tab switch (or a "refresh" button, for example), once (silent) PNs are available, they trigger the reloading. Even websockets can be integrated into this more or less, though there's probably a bit more to do that.
In any way, as said it all depends on your use-case and also available resources, but the gist of it is probably "If the current loading icon you have destroys the UX, redesign it properly". My gut feeling here would be that anything else might be over-engineering for your needs...

State preservation and restoration strategies with Core Data objects in a UIManagedDocument

I'm starting to try and add support for state preservation and restoration to my iOS app, which has a Core Data component to it that I access via a UIManagedDocument.
I'm starting to add the restoration identifiers to my view controllers, and have hooked up the required functions (currently empty) within my AppDelegate, and controllers.
I have an object that could potentially be referenced by multiple view controllers so I plan to try and preserve and restore this within my AppDelegate and just have the relevant view controllers retrieve the object from the AppDelegate. Timing of this could be tricky as the app delegate method didRecodeRestorableState occurs after all the views have already called their own decodeRestorableStateWithCoder methods.
My main problem though is that this shared class as well as multiple ViewControllers all want to have NSManagedObject properties preserved and restored. I hope to be able to use the object's URIRepresentation to facilitate this but the problem I have is my AppDelegate will open my UIManagedDocument within my AppDelegate's willFinishLaunchingWithOptions method. It does this via the UIManagedDocument openWithCompletionHandler method. Due to the threading of this opening the document is successfully opened after all my views and app delegate have already tried to restore their saved state. The AppDelegate does send a notification out once the document is ready for use, so all my view controllers can listen to this notification.
I guess I just wonder is this the best, or even only strategy for dealing with this. My objects will need to hold onto the URIRepresentations that they restore and only once the document (and it's NSManagedObjectContext) is ready try to actually find and set the corresponding NSManagedObjects up that they saved out. As such the restoring is happening a lot later than the calls to perform the restoring would I assume usually perform all their restoring work. I worry whether a controller may potentially appear empty for a brief period of time whilst it waits for the document to open and then get properly initialised.
Is there any purpose in blocking and delaying the opening of my document in this case so yes the app takes longer to open but can at least restore more correctly with all the data required before any views appear. Are there any timers being ran to make sure certain methods don't take too long? Would it be more correct to show a different view whilst we're in this limbo state, not quite sure how to go about this but its the sort of thing you may see with other apps like say the Facebook app which is dependant on a network connection.
I can't seem to find any real explanation of this sort of issue within the documentation so far.
Any help is as always very much appreciated! Cheers
In the end I just implemented notifications from when my UIManagedDocument had finished loading. These were picked up by all controllers that had coredata managed objects it wanted to restore. During restoration I keep hold of the encoded URIs, and later when receiving this UIManagedDocument ready notification I just decoded the URIs to their respective managed objects.
The problem with the shared object that I described I solved by encoded and restoring in one place from my appDelegate and then using another notification out to systems to tell them that this shared object was now fully decoded and available for use.
Not ideal and involved creating quite a lot of hierarchies of methods to ensure all objects were decoded correctly but it works ok.
Sadly since then I've hit a stumbling block where UIDataSourceModelAssociation protocol methods are being called by the OS before my UIManagedDocument has finished opening. Sadly this means that I'm unable to do anything useful. So what i really need to do somehow is defer my app restoration until everything is loaded from a CoreData UIManagedDocument POV. That problem continues...

Re-Call appdelegate applicationdidfinishload

Inside my code, there's a place that I want to recall the applicationdidfinishload function, it's like restarting the app, a workaround since apple wouldn't like it if the app restarts.
But I fail to do it no matter what I try… Is there any way to do so? just reload the app by calling that function... but how to call it? I tried several methods from the internet but no luck...
I just want from within the app, in another view controller, if a certain condition I have is fulfilled, to reload the applicationDidFinishLoading from the app delegate.
That method is not responsible for loading the app. The system loads your app (using UIApplicationMain) and tells you when the app has been loaded using that delegate method, so that you can prepare for the app's normal execution (such as setting shared resources, UI elements, appearance, etc.).
UIApplicationMain will never return, under normal circumstances, thus you will not be allowed to restart the application.
If you want to restart your interface orientation, one easy solution is to throw away the UIWindow object and create a new one. Note, that this is a terrible user experience!
If you have come to an unspeakable situation where you are no longer able to continue executing the app (you should not get to such situations but the worst of cases), you should notify the user that he should kill the app from task manager and restart it themselves.

Use of Global Model in ios APp

I am designing an app which manages a tank of hot water. The app makes RESTful API calls to a service to;
Obtain a profile of temperature at various layers in the tank.
Allow a user to work with a timer (like an immersion timer) custom control to set times to turn on/off heating elements.
So, as a first cut, I have two tabs in a nav controller;
1. A graphic, showing a picture of the tank, graduated to show temperature.
2. A custom control, like commercial domestic timers, with two concentric rings, allowing the user to point, and drag 30 minute slots to set times for heating elements.
I have both custom views working well. I'm afraid to say I'm stuck on a very simple point - even after reading all of Apple's ios docs, and would love some help.
The data server (A BeagleBone running embedded Linux) implements 2 sets of RESTful APIs, one dealing with tank temps, and the other set to read and to update timers.
Is it best to start the App with a view controller which instantiates a model, who issues the APIs, and displays "Loading...", then populate a single application wide data model.
Have each view controller (The image of the tank, and the timer controller) to populate their own (separate) model?
And the big question for me (despite years of working in Smalltalk, C++, Java...) what is the recommended way to ;
instantiate a view controller
load / display a view with a "busy"spinner if the model hasn't loaded
My app isn't complicated enough for GCD, or indeed KVO. This is basically, a "show a view, call a web service to read the data", "modify the data", "call a web service to replace the data"
Basically, I believe my question is, when a view comes on screen, what is best practice to determine that the controller has a model, or has a model which is still loading data?
Sorry for the long winded question.
You asked a very good question, about a very common task in an mobile app. My suggestions are
Since your two tabs really have no common data, the problem simply boils down to "how to initialize a view controller with remote data".
If user can't use your app without remote data,
simply show a loading view in [vc viewDidLoad], and start fetching data from server asynchronously; your vc should implement NSURLConnectionDelegate to listen to call back
when data is successfully fetched from server, dismiss the loading view, and render the data to user
if data fetch failed (due to bad network or server downtime), show alert view to user, and retry the data fetching; if fetching fails for several times, tell user to try again later
you can make the loading view more beautiful, to provide a better user experience; note the loading view should cover all the buttons/controls, so user can't mess up your app state during data fetching
If user can use your app without remote data, it's another story. You shouldn't use a loading view in that case, and should silently fetch data in background. Since this does not seem to be your case, I will not complicate the answer by this case.

IOS Saving State For Complex Apps

I'm building a fairly complex business application on the iPad IOS 4.2: 4 tabs, with potentially deep navigational paths on each tab.
In the opinion of some of your more experienced IOS developers, what would the general expectation of a user be with respect to saving application state between launches (i.e. after an app's been fully terminated and subsequently restarted)? I'm using Core Data and have all the data issues covered, but I'm concerned about the application's navigational tree. If the user had left the 1st tab on screen 3, the 2nd tab on screen 4, the third on screen 2, where he left the entry of a new record half-complete, and was, at the time of the app entering the background, working on the 4th tab on screen 3...do you think the average user would expect the application to remember all that the next time it launched? (My gut says yes, though I'm unsure for how long.)
If the answer is yes, can you suggest a general strategy for handling this (and, again, I'm talking about the navigational tree here, not Core Data stuff)? For example, if navigational controllers were used as the root view controller for each tab, it would be simple enough to record enough info about their navigational stacks to be able to restore them later. But what about things like popovers, alert/action sheets, or modal VCs created on the fly? Should each view controller record the state of its UI objects and, if so, what is the recommended way to do this?
I know a lot of this is up to the user, but I'm asking for the general perspective on these issues, i.e. the voice of experience.
It's pretty simple in principle, but it can get quite complex in practice, to go through your navigation hierarchy and storing stuff that can't be derived from the data model.
There's an open source implementation of this called DTResurectionKit. I also documented how I do it in my apps on my website. It's similar to (but simpler than) DTResurectionKit.
In the opinion of some of your more experienced IOS developers, what would the general expectation of a user be with respect to saving application state between launches?
The best way to think about this is to make sure the user never has to figure out why or how they got to where they are when the app first opens.
This depends completely on the type of app you have and the length of time since the last open. It sounds like you have a fairly complex drill-down app so I think it is definitely best to remember the navigation stack, within a pre-determined time frame. I use the three20 framework which does this automatically for me, but if you were to implement it, it would be something like this:
If the user opens up in the past 24 hours, open to the exact spot the left off
If the user opens within a week, open to the main "section" or area of the app they were in
If the user opens after a week has past, open to the root screen.
Now of course, these values will differ based on your apps function and use cases, but you get the idea. By you making some broad assumptions about how people are using your app and when, you can increase the user experience by not shoving them so deep in your app when they won't remember how they got there.
As for implementation, it is all just data.. You dont need to serialize live objects to store the stack, just implement the data needed to recreate the state on the next launch. What you need to store is highly dependent on your own setup... mileage will vary. I use NSUserDefaults to store all instance vars and the navigational stack through Three20. Check out TTNavigator for a great implementation.
I would suggest keeping the state of each tab view. Only at the "page" level. Don't worry about popovers or incomplete data entry (hopefully there's not too much interim state before you're saving it to your core data store.)
Like you said, it's easy enough to remember what tab you're on, and what controller you're navigated to in each tab. More isn't necessary.
It sounds like you've got it under control, but for the benefit of others: 1) when you change tabs, save "active tab", 2) when you navigate within a tab, save "active controller in tab", 3) when you launch the app, set the "active tab", 4) when you change tabs, set/confirm the "active controller in tab".
The reason for 4) is that the view/controllers for the tabs will be delayed in their loading, or perhaps never loaded. You don't want to set the "active controller in tab" for a tab that is not visible and may never be loaded into the app, it would just cause unnecessary loading. It will often happen (after the app has been loaded) that you don't need to change it because it's already in the correct state.
I think your time is better spent elsewhere. Of course, your app might be perfectly suited for this, but in our case data was partly online, could have gone stale, influenced view state in different navigation views in different tabs simultaneously, etc. etc. It's not an insurmountable challenge, but definitely hard and a huge time-sink.
We decided to spend our time on fixing the bugs and improving functionality. If you have to make the same kind of choice, I'm pretty sure which option your users would prefer. Particularly now that your app will survive a phone call in the background.

Resources