Is it mandatory to call NSUserDefaults synchronize method? - ios

Please, anyone, help me: Is calling NSUserDefaults's synchronize() method mandatory?. If I don't call it, what will happen? My application is working fine without it.

No.
Since iOS12, it isn't mandatory anymore.
Apple says:
This method is unnecessary and shouldn't be used.
You can find more information on iOS12 release note:
UserDefaults
NSUserDefaults has several bug fixes and improvements:
Removed synchronization requirements. It's no longer necessary to use synchronize, CFPreferencesAppSynchronize, or CFPreferencesSynchronize. These methods will be deprecated in a future version of the OS.
Now that you don't need to call these synchronization methods, the performance characteristics of NSUserDefaults and Preferences Utilities are slightly different: The time taken for enqueueing write operations is now paid by the writing thread, rather than by the next thread to call synchronize or do a read operation.

From the docs:
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.
Meaning that if you kill the app right after something is written to the defaults without the periodic interval catching it, it will get lost. You probably did not kill the app right after a write event yet which is why your app seems to work fine so far.

Normally it works perfectly fine and you only have to use it in special cases, for example when the app will close directly after writing to NSUserDefaults. So you can simply add the synchronize method to the corresponding AppDelegate-method.

As others have said, you don't normally have to call synchronize at all.
Normally the system calls it for you so your defaults changes get written.
However, when working with Xcode it's pretty common to terminate your app by pressing command period or clicking the stop button. In that case it terminates the app without warning and your user defaults changes will quite likely not be written out, and will be lost.
This can be a good thing or a bad thing, depending on what you want. It's certainly confusing.
You can "fix" it by calling synchronize each time you make a change, or on some time interval. However that does slow your app down and increase it's power requirements (both by very small amounts.) If you are in a loop, writing changes to 10,000 server or Core Data records, and you change user defaults after each pass, then calling synchronize after each one might have a measurable effect on app speed and on battery life. In most cases you're not likely to notice the difference however.

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

Where to put code that gets checked frequently?

I have some code that needs to get called frequently, such as check what day it is, if it's the next day then move the day strings in the tableView.
Now I thought that the viewDidLoad would get called all the time and so it would be 'fine' to put it in there. However, I've left the simulator overnight, and I've pressed the home button and clicked again, changed VCs etc. and viewDidLoad hasn't been hit.
What are my options for doing sporadic checks such as, is it a new day? As x happened etc.
In this specific case, you can subscribe to NSCalendarDayChangedNotification to be notified when the date changes and respond accordingly in your view controller. In general, didBecomeActive or viewDidAppear would likely work.
What are my options for doing sporadic checks such as, is it a new day
It depends what the meaning of "is" is! In particular, "is" when? You say "sporadic", but that's just fluff. When do you need to know this? To what stimulus do you want to respond? When the user opens your app? Then put it in applicationDidBecomeActive. Every day at noon? Then run an NSTimer. Really, the problem here is that you don't seem to know, yourself, just when you need to perform these checks.
Whilst in your app, its quite easy to continually check for something. You simply create a background thread. However, what you describe is a thread that persists from outside the app's lifecycle.
Have a read on this documentation provided by Apple itself. You have to have good excuse to put a background thread. The scope of such thread is limited to only certain scenarios such as downloading background stuff, playing sounds etc.
For your scenario, I'd look at applicationDidBecomeActive(_:) found in your Application Delegate. There you can mimic such continual check. Beware however, don't put heavy word load on start up or your app might be killed automatically if it fails to become active in reasonable amount of time.

Where Is Authoritative Source of Do Not Call NSUserDefaults synchronize for iOS 8 and Above?

I recall that one should not bother calling NSUserDefaults' synchronize method on iOS 8 and above. I also read from a one non-Apple source and another a hint of the same. However, I am having trouble finding an authoritative source from Apple. Was it a WWDC video from a couple years back? What was the Apple authoritative source?
Read this: NSUserDefaults
and skimmed through some of this: WWDC 2014 Videos
Well, after reading you comment (please add that link to your post) I came to this conclusion:
The article states you shouldn't call -[NSUserDefaults synchronize] because it is a performance drawback.
I only use this when I really need the defaults to be written, for example when needing it up to date in a different class. I never felt like needing to.
To sum it up; better don't call it, for performance reasons. If you need the data to be up to date immediately, call it.
The most accurate source I've seen is the following tweet and blog post from David Smith, a developer at Apple who works on NSUserDefaults: https://twitter.com/catfish_man/status/674727133017587712 and http://dscoder.com/defaults.html
Reminder that the only thing -[NSUserDefaults synchronize] does is wait. That’s not useless, but the situations it’s useful in are uncommon.
The blog post elaborates:
If you find yourself to do anything else to set a preference, again, you probably don't need to. It is almost never necessary to call -synchronize after setting a preference (see Sharing Defaults Between Programs below), and users are generally not capable of changing settings fast enough for any sort of "batching" to be useful for performance. The actual write to disk is asynchronous and coalesced automatically by NSUserDefaults.
If one process sets a shared default, then notifies another process to read it, then you may be in one of the very few remaining situations that it's useful to call the -synchronize method in: -synchronize acts as a "barrier", in that it provides a guarantee that once it has returned, any other process that reads that default will see the new value rather than the old value. For applications running on iOS 9.3 and later / macOS Sierra and later, -synchronize is not needed (or recommended) even in this situation, since Key-Value Observation of defaults works between processes now, so the reading process can just watch directly for the value to change. As a result of that, applications running on those operating systems should generally never call synchronize.

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

What actions are required when your app get an applicationWillResignActive call?

This question io3->ios4 upgrade said to support applicationWillResignActive. While implementing this call, I also implemented applicationDidEnterBackground and applicationWillEnterForeground. However, I found that my app would crash. After some debugging on the simulator I determined that I needed to reinitialize a key data structure in applicationWillEnterForeground. So my question is how would I have known that from reading the documentation? (In fact, I may be doing the wrong thing and just so happened to get it working again.) Is there an exact description of what to do when these methods are called?
Thanks.
The only things you should do when supporting multitasking, is saving the state of your app when it enters the background, and reload it when it becomes active. (If you generate a new template in Xcode, you'll see this.)
Saving state means writing any user preferences or data to disk. Reloading the state involves reading saved preferences and data, recreating any in memory data structures that might need it (like in your example you gave).
In most circumstances, there's little else you need to do. The only thing that would crash your app that is unique to multitasking would be trying to run code in the background for longer than the allotted amount of time, (which is 10 minutes.) Otherwise, it sounds like you have got other problems with your code.

Resources