Preventing NSUserDefaults from iCloud Backup - ios

There are three ways to write persistently to the device in iOS that I'm aware of:
NSUserDefaults
Custom Objects - Archived and Written to a PATH in NSDocuments
SQLite
Apple provide a mechanism to prevent iCloud backups with #2 i.e.
- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
I use NSUserDefaults to store three images but to adhere to iOS Data Storage Guidelines -
How do I prevent Backups with iCloud with NSUserDefaults?
This question has been asked several times on SO but there is no clear comprehensive answer yet.
Is there a such a function or do I have to change storing images using method #2. I was hoping something convenient like:
- (BOOL)addSkipBackAttributeForStandardUserDefaultsKey:(NSString *)
exists.

There's no mechanism for this. NSUserDefaults is not intended to be used for saving app data. It's there to hold user preferences and other settings the app needs, but isn't intended as a full data persistence system.
It's not accurate to say that this "...[Apple's] mechanism for storing data can't adhere to it's own guidelines" because that's not the purpose of this API. In this case specifically, if those images should not be backed up, then that's absolute evidence that user defaults is the wrong place to store them.
You should save the images to files. If you have UIImage, this is simple. UIImage conforms to NSCoding, so it's easy to convert to/from an NSData. And NSData includes convenience methods to read/write objects to files. If the file names might change, you could reasonably put the file names in user defaults. If the file names are known in advance and can't change, then there's no reason to store them.

Related

How to create sharable Core Data .sqlite backups locally and in iCloud using current NSPersistentStore methods

I have been having quite a time of figuring out the correct way to create a backup of a Core-Data backed .sqlite file and storing that backup, both locally and/or in iCloud (whatever the user prefers), for download, restore or sharing. Let me state up front that I am not talking about moving the persistent store to iCloud to be used by the app as its datasource. I am simply asking about creating backup files in this question.
In 2014, Apple changed their default journaling mode for Core Data SQLite stores to WAL.
https://developer.apple.com/library/content/qa/qa1809/_index.html
With that change, they recommended:
To safely back up and restore a Core Data SQLite store, you can do the
following:
Use the following method of NSPersistentStoreCoordinator class, rather
than file system APIs, to back up and restore the Core Data store:
- (NSPersistentStore *)migratePersistentStore:(NSPersistentStore *)store toURL:(NSURL *)URL options:(NSDictionary *)options withType:(NSString *)storeType error:(NSError **)error
Note that this is the option we recommend.
Prior to this, I had been using NSFileManager to create backups. With this recommendation, I believe that the correct way to create a backup locally is to add a new persistent store and then to migrate that persistent store to the desired backup location, using
the NSPersistentStoreCoordinator methods addPersistentStoreWithType:configuration:URL:options:error, and migratePersistentStore:toURL:options:withType:error, respectively.
My questions are two-fold:
I previously would zip my data to NSData before exporting it, and
would write the NSData directly to a file. My file extension would
be custom to my app, for sharing the zipped data via email or other
iOS sharing methods. With the
migratePersistentStore:toURL:options:withType:error method, I now
end up with a .sqlite file (and its corresponding WAL file, etc) in
the desired location, but I cannot figure out how to share this file
now. If I zip the file, won't I be in danger of losing the data I
worked hard to preserve by using
migratePersistentStore:toURL:options:withType:error in the first
place? I believe so, but I do want to enable my users to share/save their
backups vie email, etc, and I don't know how to best approach this now.
I am having a hard time understanding how
migratePersistentStore:toURL:options:withType:error can be used to
backup the file to iCloud. Much like the sharing example above, I
see that I can use
addPersistentStoreWithType:configuration:URL:options:error, and
migratePersistentStore:toURL:options:withType:error to get the
desired .sqlite copy locally, but if I then try to upload that local
file to iCloud, I fear I will lose the data I worked to preserve by
using migratePersistentStore:toURL:options:withType:error in the
first place. I have been trying to see if there is a way that I
could/should use
migratePersistentStore:toURL:options:withType:error to move the
newly created persistentStore directly to iCloud, but I haven't been
able to find any documentation on how to do that or if it should be
done at all. Is there a specific url I would need to use to indicate that the destination for the persistentStore is iCloud?
I would greatly appreciate any insights that can be shared regarding the answers to these questions.
ZIP should be safe way IMO since it has CRC data for data integrity validation.
You have to be careful though regarding shared CoreData store version. Suppose two users run different versions of the app and share the CoreData store between each other. CoreData doesn't support progressive migrations out of box. https://www.objc.io/issues/4-core-data/core-data-migration/
Maybe sharing a portion of data in JSON and re-creating CoreData entities from it would be safer and easier strategy for sharing data as opposed to sharing entire graph.
You can only copy file into iCloud container and share it across devices but you can't really use it directly from container or have incremental updates coming via iCloud. NSFileManager has setUbiquitous that allows to move file into iCloud container.

NSUserDefaults or a backend?

I have to ship a simple app with a very tight deadline so I have no time to learn anything othen than what I know (meaning learning Core Data for example). Would it fine to use NSUserDefaults to store data for my app? It's almost like a check list app where users have list of items with some data accompanying each item.
Other than that I would be using a service like Parse which I can handle thanks to having built my last app with it.
Would Apple reject an app if you use NSUserDefaults for something other than settings/preferences?
Thanks!
From Apple's documentation:
The NSUserDefaults class provides a programmatic interface for interacting with the defaults system. The defaults system allows an application to customize its behavior to match a user’s preferences. For example, you can allow users to determine what units of measurement your application displays or how often documents are automatically saved. Applications record such preferences by assigning values to a set of parameters in a user’s defaults database. The parameters are referred to as defaults since they’re commonly used to determine an application’s default state at startup or the way it acts by default.
[...]
Values returned from NSUserDefaults are immutable, even if you set a mutable object as the value. For example, if you set a mutable string as the value for "MyStringDefault", the string you later retrieve using stringForKey: will be immutable.
In other words, this is not the place to store mutable application data.
If the checklist data is going to be modified by end users (i.e. adding new items, or editing the text of the items on the checklist) it really would be worthwhile to learn how to use Core Data. Over the years it really has become very easy to learn and use.
You can find a great series of Core Data tutorials at http://code.tutsplus.com/series/core-data-from-scratch--cms-653
If you're looking for something that's been updated for iOS 8 and Swift (although they're mainly syntax changes for the new language), you can check out http://www.raywenderlich.com/85578/first-core-data-app-using-swift.
Apple wouldn't reject even if you unnecessarily burden NSUserDefault. But users of your App will definitely uninstall it or OS will kill it if app crashes or hangs their iPhone.
NSUserDefault : is a fast way to access the data. it stores in (key,value) pair and lives as long as your app is installed in phone. Usually session based small amount of data is supposed to be stored in it. Not ur entire database.
Database (CoreData or Sqlite) : Behind the seen Coredata is also a sqlite with objects talking to each other(Think in terms of model instead of tables if using Coredata).
I simple suggest you use Coredata if your data by any chance is big or expected to get big. CoreData wont take much time.
Yes, you can store data in NSUserDefaults, which will be the quick and dirty method to store data locally.
You could use Core Data, though I prefer to just use FMDB and write my own SQLite statements. You could also use NSFileManager and just read and write everything to a JSON file. Parse will probably be overkill if all you are doing is storing a list of check list objects.
Go for NSUserDefault fro now !
When the App is submitted fro approval, look at SQlLite. this will be useful for the next projects.
Parse... sure but make sure it's not overkilling especially if you have limited amount of data
Good Luck !

iOS NSUserDefaults standardUserDefaults

I've a conceptual question — do I always have to use the standardUserDefaults API when accessing NSUserDefaults?
I am saving a lot of settings/flags/NSCoding info in my app for a variety of things. But for some of these things, it doesn't feel natural for me to be bothering the standardUserDefaults. I am wondering if I could potentially create a new NSUserDefaults entity for managing certain type of information that I want to store.
Just for an example — I am using some background NSURLSessionDownloadTasks and I want to save some relationship data between the downloadTask and the actual user for which the download task was initiated. I am saving that data to NSUserDefaults. I just am not sure if accessing NSUserDefaults for this (and other things) is the way to go!
[Maybe I didn't explain this right. Sorry!]
NSUserDefaults is not the right solution for what you described.
NSUserDefaults is actually for a very narrow and particular purpose. It is mainly meant to interact with users, let them set their preferences and get those preferences persisted.
It is not meant to be used as any type of general purpose storage.
For what you want, you can always use a usual NSDictionary etc for that. If you need it to be accessible from multiple places, leave it on the application delegate or create a singleton class. If you need it to be persisted across runs, use something like SQLite.
One thing you could do is use [[NSUserDefaults alloc] initWithSuiteName:] to create separate NSUserDefaults storage.
Just doing [[NSUserDefaults alloc] init] will get you the same storage as standardUserDefaults.
As I understand, there's no need to specifically create a new user defaults file for the specific purpose of classification of data.
The NSUserDefaults can be as large as any system file and the data dump can be adjusted accordingly. In case one needs a new defaults, please see Catfish_Man's answer. Also, this is specifically for the case when you deal with a certain group of apps.

NSUserDefaults Lose Newly Saved Data if App Killed Within 10 Seconds

I'm looking for a faster way to save user preferences than the NSUserDefaults. I've found that if the app is killed within around 10 seconds of writing to NSUserDefaults, it will not be saved permanently. I use the defaults to save paths to custom ring tones, paths to custom images, map coordinates, and basically just user defined preferences. Is using core data the better option? SQLite? What's accepted as the fastest and most lightweight?
You need to be sure to call synchronize to save the data immediately.
[[NSUserDefaults standardUserDefaults] synchronize];
From Apple's class reference:
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.
And to answer your second question, it really depends on how much data you want to store. NSUserDefaults is designed to store very small amounts of data (for preferences) like the state of a toggle switch, etc. You can get away with storing the paths to images and ring tones here but it isn't exactly advisable. By that I mean you can, but probably shouldn't.
Core Data is a much better approach if you plan on storing many of these paths as it is very scalable and performs very well. So overall, if you need to store a lot of data, user Core Data, or as another alternative store the paths in a plist in the documents directory.

Why is NSUserDefaults used in iOS?

I am new to iOS development and Mobile app development in general, I have decent knowledge about Objective C and MVC patterns to be used and I have never done any Mac development.
I am struggling to understand what NSUserDefaults is for?
We already have something like PList which stores data as XML and we have SQLite which is lightweight DB for such devices.
Then why do we have it?
Is it an alternative simple key-value storage for our app in the same way we have different types of storage on the cloud like an RDBMS and a key-value based NoSQL store etc ?
For NSUserDefaults, Apple docs say that :-
"Applications record such preferences by assigning values to a set of parameters in a user’s defaults database"
What do they mean by user's default database?
My guess is that, like in any multi-user operating system we have various user accounts and in the same way in Mac as well we might be having multiple users each having a database from where applications would load saved preferences for that user.
So like Mac OS X, does iOS also have multiple users and depending upon whichever is logged in NSUserDefaults picks his/her preferences?
Please tell me if I am wrong.
One thing that hasn't been mentioned yet: NSUserDefaults supports all basic scalar (float, int, BOOL) types, as well as the plist-compatible types: NSData, NSString, NSNumber, NSDate, NSArray, and NSDictionary. (You mentioned plists as an alternative--NSUserDefaults is just a front-end to a standard plist file in the application's Preferences folder.) This makes it easy to create persistent storage for your application state in just a few lines of code. Even better, if you implement NSCoding on your model objects, you can archive them into an NSData and put them in NSUserDefaults:
NSData* data = [NSKeyedArchiver archivedDataWithRootObject:arrayOfModelObjects];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:kDefaultsKeyModelObjects];
and restoring your app data is as simple as
- (void)viewDidLoad
{
NSData* data = [[NSUserDefaults standardUserDefaults] objectForKey:kDefaultsKeyModelObjects];
arrayOfModelObjects = [[NSKeyedUnarchiver unarchiveRootObject:data] retain];
// error checking, tell UI to load the new values, etc...
}
(kDefaultsKeyModelObjects is a string constant you've defined somewhere. Always put your NSUserDefaults keys in one place! Having a typo in a key name can take hours to debug. :)
If you used, e.g., SQLite to store your application data, you have to write a lot of code to move your model data in and out of the database. That makes sense if you're dealing with a lot of data, need efficient searching, etc.; but if it's, say, a list of servers in a networking app, it's easier and cleaner to throw them in NSUserDefaults.
One last thing: NSUserDefaults is great for prototyping. Even if you know you're going to need a SQLite store eventually, you can start out using NSUserDefaults as a quick-and-easy persistent store, and get your UI working and your data model fleshed out before writing a bunch of database code.
So like Mac OS X, does iOS also have multiple users and depending upon
whichever is logged in NSUserDefaults picks his/her preferences ?
Nope, on iOS the defaults plist is in the application's Library/Preferences folder, not in a user-specific folder like it is on OS X. I can't imagine iOS will ever have multiple logins, but you never know. If it did, they'd have to make separate defaults plists for different users.
NSUserDefaults are used to save small portions of data the App is dependent on to run correctly, while other approaches are good to store bigger amounts of data, that the app work with.
NSUserDefaults makes is incredible easy, as you don't have to worry about files, entities, fetches,…. It is all done in one line.
It's originally for storing settings, but does seem to get (ab)used for a lot of other things if the questions on SO are anything to go by. The "point" of it is that it provides a single-line data persistence and retrieval method for a wide range of data types.
There is no multi-user configuration (yet?) on iOS (have you ever had to sign in to your iPhone?) that is built in to NSUserDefaults, but because OS X and iOS are built on the same foundation, the same class is used for both operating systems.
iOS is a single-user OS. Hence, NSUserDefaults is used differently in OSX and iOS.
In iOS, you normally use NSUserDefaults to save application preferences and/or user data on the "iOS defaults system". Data saved in the defaults system will be persistent at all time even when you reboot the iOS device. As a matter of fact, even when the user updates the application.
You typically want to use NSUserDefaults to save application preferences/settings. Any other massive data such as game data or realtime calculations should be save in CoreData or sqllite in the documents directory.
NSUserDefaults stored values for only your application.
Note - It is not meant to serve as a replacement for a traditional store for data (Core Data, SQLite, Property Lists, etc are meant for storing application data). What NSUserDefaults is meant for is for specific key value pairs.
For example, I use it to store an authenticated user's networkid and full name once the login process is done. Upon session timeout or logout, I clear those values. For this NSUserDefaults shines.

Resources