When to save Singleton data in Swift? - ios

So I'm using a global singleton to manage my app's data, that I need to be persistent throughout app launches. Thus, I'm writing the singleton's data to a file, and reading them in the init method.
My questions is when to save the singleton back to the file. Right now whenever I make any change to my singleton, I also call .saveData() to save them, but this means I overwrite a file a lot more than I need to.
I'm trying to prevent any data loss due to my app's termination and I was thinking of saving my data inside applicationWillResignActive or applicationDidEnterBackground, instead of saving every time I make a change. Another idea that came to me while reading the swift iBook is to actually call saveData in my singleton's deinit {}, but I'm not sure if a destructor is suited for the job of writing everything to a file before destroying the object.
Is any of the above methods guaranteed to save my data the day I want it? (Let's assume that my app does not crash, since if it does it's understandable that some data will be lost)
What is the best time to call my saveData() method?
P.S.: In the future I'll use CoreData for saving my data instead of a file, but for now I need a my data up and running to put the rest of the app (UI, animations, networking etc) together.

I'd use a combination of applicationDidEnterBackground and applicationWillTerminate.
applicationWillTerminate - This is your 'goto' method for doing clean up/saving before the app is terminated. However, it won't trigger if the app was suspended before termination.
applicationDidEnterBackground - Because the previous method doesn't trigger if the application process is suspended first, you'll want to trigger on background as well because the application will enter background before suspension (the case where applicationWillTerminate alone would be insufficient)

Related

When and why should you use NSUserDefaults's synchronize() method?

So I've had a look at the apple documentation on the NSUserDefaults's synchronize() method. See below for reference:
https://developer.apple.com/reference/foundation/userdefaults/1414005-synchronize
The page currently reads:
Because this method is automatically invoked at periodic intervals, use this method only if you cannot wait for the automatic synchronization (for example, if your application is about to exit) or if you want to update the user defaults to what is on disk even though you have not made any changes.
However, what I still don't understand is when should this method be called? For example, should it be called every time the user changes the app's settings? Or should I just trust that the background api is going to handle that? And does the leaving of the view immediately after a settings change in memory result in that change being lost?
Also, when might a failure to call synchronize() result in user settings not getting changed correctly?
Furthermore, what is the cost (performance, memory or otherwise) of calling this method? I know it involves reading and writing from/to the disk but does that really take that much effort on phones?
There seems to be so much confusion about user defaults. Think of it this way. It's essentially the same as you having a global dictionary available throughout your app. If you add/edit/remove a key/value to the global dictionary, that change is immediately visible anywhere in your code. Since this dictionary is in memory, all would be lost when your app terminates if it wasn't persisted to a file. NSUserDefaults automatically persists the dictionary to a file every once in a while.
The only reason there is a synchronize method is so your app can tell NSUserDefaults to persist the dictionary "now" instead of waiting for the automatic saving that will eventually happen.
And the only reason you ever need to do that is because your app might be terminated (or crash) before the next automatic save.
In my own apps, the only place I call synchronize is in the applicationDidEnterBackground delegate method. This is to ensure the latest unsaved changes are persisted in case the app is terminated while in the background.
I think much of the confusion comes from debugging an app during development. It's not uncommon during development that you kill the app with the "stop" button in the debugger. And many times this happens before the most recent NSUserDefaults changes have been persisted. So I've developed the habit of putting my app in the background by pressing the Home button before killing the app in the debugger whenever I want to make sure the latest updates are persisted.
Given the above summary, let's review your questions:
should it be called every time the user changes the app's settings?
No. As described above, any change is automatically available immediately.
Or should I just trust that the background api is going to handle that?
Yes, trust the automatic persistence with the exception of calling synchronize when your app enters the background.
And does the leaving of the view immediately after a settings change in memory result in that change being lost?
This has no effect. Once you add/edit/delete a key/value in NSUserDefaults, the change is made.
Also, when might a failure to call synchronize() result in user settings not getting changed correctly?
The only time a change can be lost is if your app is terminated before the latest changes have been persisted. Calling synchronize when your app enters the background solves most of these issues. The only remaining possible problem is if your app crashes. Any unsaved changes that have not yet been persisted will be lost. Fix your app so it doesn't crash.
Furthermore, what is the cost (performance, memory or otherwise) of calling this method? I know it involves reading and writing from/to the disk but does that really take that much effort on phones?
The automatic persistence is done in the background and it simply writes a dictionary to a plist file. It's very fast unless you are not following recommendations. It will be slower if you are misusing NSUserDefaults to store large amounts of data.
Apple's documentation for synchronize() has been updated and now reads:
Waits for any pending asynchronous updates to the defaults database and returns; this method is unnecessary and shouldn't be used.
UPDATE
As anticipated, it has been deprecated as mentioned in Apple Doc
synchronize()
Waits for any pending asynchronous updates to the defaults database and returns; this method is unnecessary and shouldn't be used.
Original Answer
-synchronize is deprecated and will be marked with the NS_DEPRECATED macro in a future release.
-synchronize blocks the calling thread until all in-progress set operations have completed. This is no longer necessary. Replacements for previous uses of -synchronize depend on what the intent of calling synchronize was. If you synchronized…
— …before reading in order to fetch updated values: remove the synchronize call
— …after writing in order to notify another program to read: the other program can use KVO to observe the default without needing to notify
— …before exiting in a non-app (command line tool, agent, or daemon) process: call CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication)
— …for any other reason: remove the synchronize call
As far i know, synchronize is used to sync the data immediately but iOS can handle that in smart way. So you dont need to call it everytime. If you call it everytime then it will turn to performance issue.
Check apple documentation:
Official Link

MagicalRecord UIApplicationDidEnterBackground

I use MagicalRecord as a good library for working with Core Data. My question is how can I save data before my app will enter background. Because I try to write data via block and of course it will not work because the app will be suspended.
So how can I update or put my object in context and save it. Usually I use method that allow import all data from dictionary and save it and it works perfect while app is running.
I'm assuming you want to do this in someplace other than your AppDelegate since you have references to actual data objects. The easiest way I can think of is to listen for the UIApplicationDidEnterBackgroundNotification and perform your save operation there. What you may also need to do is use the iOS backgrounding API so that the task can complete while it's running in the background. You may also want to listen to one of the notifications fired prior to actually going into the background as well. UIApplicationWillResignActiveNotification seems as appropriate, and you may not have to deal with the backgrounding API.

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...

Delete data when app is killed in the background.

I have an app where I am saving data but when the app is killed in the background I have to delete all the saved data.
I tried using this method:
- (void)applicationWillTerminate:(UIApplication *)application
But its not working. Can someone please suggest me how to do that?
Not sure why you need to wipe the data on terminate specifically, but here is something to consider:
Another way to handle this type of situation when apps write data that may be incomplete when they are forced to quit, is that they write out a flag when the data is known to be good.
That way, if the app exits normally, the data will be written and the flag will be written.
If the app is forced to quit, the flag will not get written by nature of the forced quit.
THEN, when the app starts it can look for the flag. If the flag isn't there, the app knows that any data is incomplete and can discard it (delete it) and start over.
Hope this helps.
applicationWillTerminate: is basically never called and you should no longer rely on it.
The app is not given any callback when it is killed so you can't do anything. You need to decide on a different approach based on the actual user requirement. This may involve encryption / not holding certain data on disk (only ever in memory) / etc...
I know, I'm super late to the party but, if you can live with data "sticking around" until the next app launch you can delete/clear data in application(_:didFinishLaunchingWithOptions).

Best Core Data save strategy (when to save data to disk)

Based on your experience, for an iOS app which uses only one main-thread only NSManagedObjectContext, what is the best way to persist user settings and cached data from server to disk with respect to reliability and performance?
I see next options:
save context at the end of every change
save context only on app's exit (like in Apple's samples)
save context on app's exit, going to background or becoming not active (incoming phone call for example)
add timer to save context from time to time if it has any changes
call specially prepared delayed save routine which will gather together calls to save context to make sure they don't fire too often
Currently we use the 1st option, so I can say that the reliability of it is very good, the data is saved even after Xcode terminate the app during debug session, but performance may suffer when app become more and more complex, esp. when changes to DB can happen at any moment of the app's flow due to async loading of data from server.
On the other side saving at certain events of the app (exit, going to background etc) will give the best performance, but can you say from your experience that it is enough to make sure that user will not loose the data?
I think you should save often, because it's more reliable (you won't lost data if the app crashes) and you can save free up memory occupied by modified but unused objects.
At the same moment you don't want to overwhelm db with your save requests.
My suggestion is to expose two methods in interface file and choose which one you want to call depending on the situation you have.
- (void)save {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(save) object:nil];
[_storage save:nil];
}
- (void)setNeedsSave {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(save) object:nil];
[self performSelector:#selector(save) withObject:nil afterDelay:1.0];
}
Also, have you considered using second managed object context with private queue? You can set it up as a parent context and save / fetch data in background: http://www.cocoanetics.com/2012/07/multi-context-coredata/
Saving the context in the UIApplicationDelegate methods applicationDidEnterBackground: and applicationWillTerminate: has always been fine for me. I also save in special circumstances like a big data import or something similar.
According to Apple documentation
"It is recommended that you save user data at appropriate points throughout the execution of your app, usually in response to specific actions. For example, save data when the user dismisses a data entry screen. Do not rely on specific app state transitions to save all of your app’s critical data".
I used to save context on every changes but the performance is really not that good. Currently I'm saving the context on the end of the fetching data function. The performance is like 4 times better. Just make sure you have [context lock] and [context unlock] at the beginning and the end of the function if you plan to call the function async in background threads.

Resources