NSUserdefault and core data - ios

what is NsUserdefault ? and how it can be used to store peristent data of app .
Please clear my doubt on when to use them and how they can be useful performance wise ?

With the NSUserDefaults class, you can save settings and properties related to application or user data.
For example, you could save a profile image set by the user or a default color scheme for the application. The objects will be saved in what is known as the iOS “defaults system”. The iOS defaults system is available throughout all of the code in your app, and any data saved to the defaults system will persist through application sessions. This means that even if the user closes your application or reboots their phone, the saved data will still be available the next time they open the app.
Performance
The NSUserDefaults class caches the values internally so the lookup is extremely fast. The overhead of [NSUserDefaults standardUserDefaults] vs an instance variable is so small that you wouldn't even notice it if you did it 5 million times in your code.

The NSUserDefaults class acts very much like something called a Property List (aka plist). Plists are limited in what kind of objects they can store. The six types plists can store are:
NSData
NSString
NSNumber
NSDate
NSArray
NSDictionary
Usage:
NSUserDefaults is used to store values that belongs to users settings and needs to be remembered even when app is killed and re-launched. Having said that, as posted in another answer, we should put only light weighted objects in NSUserDefaults.
For other heavy wight save and reload user file system. For things that involve search on saved data use Core Data.
Writing To NSUserDefaults:
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject("Your Object", forKey: "yourKey")
Reading from NSUserDefaults:
let defaults = NSUserDefaults.standardUserDefaults()
if let value = defaults.stringForKey("yourKey")
{
print(value)
}
Other Useful Convenience Methods:
func setBool(value: Bool, forKey defaultName: String)
func setInteger(value: Int, forKey defaultName: String)
func setFloat(value: Float, forKey defaultName: String)
func setDouble(value: Double, forKey defaultName: String)
func setObject(value: AnyObject?, forKey defaultName: String)
func setURL(url: NSURL, forKey defaultName: String)
Storage Size:
As long as there's enough space on the iPhone/iPad, you can store NSUserDefault values. All those values is stored into a .plist file, and this file is very small, most of the time under 1 kb. But keep this as light as possible.

This is the piece of code i made as a demo use of NSDefaultUser .
to save data
NSString *name = _tf_name.text;
NSString *email = _tf_email.text;
int age = [[_tf_age text] integerValue];
// Create Instance of NSUserDefaults
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// Set the object and YOU name the key, we are defining the key here
[defaults setObject:name forKey:#"firstName"];
[defaults setObject:email forKey:#"email"];
[defaults setInteger:age forKey:#"age"];
// Call Sync to Save Objects
[defaults synchronize];
// Load Data
// Get Saved Objects
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *name = [defaults objectForKey:#"firstName"];
NSString *email = [defaults objectForKey:#"email"];
int age = [defaults integerForKey:#"age"];
NSString *ageString = [NSString stringWithFormat:#"%i", age];
// Load UI
_tf_name.text = name;
_tf_email.text = email;
_tf_age.text = ageString;

NSUserDefaults are used to save very light data, where as 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.

Related

iOS - Incrementing ID number for creating filenames

The application I'm working on creates pdf files and saves them in the device's documents directory. What I'm trying to do is create filenames with unique ID number, for example "invoice76.pdf".
How do I ensure each time the pdf file is created the number is incremented from the previous file name.
I tried using a global int but that obviously resets it each time the application is started.
Probably the simplest solution would be to store it in the NSUserDefaults. Alternatively, you could save the value in your own plist file.
Use this code which works betweeb app restarts:
- (void)storeIndex:(NSInteger)index{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
//change value to new value
[defaults setInteger:index forKey:#"pdfIndex"];
//make save
[defaults synchronize];
}
- (NSInteger)lastIndex{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSInteger value = [defaults integerForKey:#"pdsIndex"];
return value;
}
//stores new index and returns its value
- (NSInteger)incrementIndex{
NSInteger lastIndex = [self lastIndex];
lastIndex++;
[self storeIndex:lastIndex];
return lastIndex;
}
Pull an nsstring timestamp from nsdate, and if saving multiple files at the same time (and you don't want to use a massive timestamp) increment through n in a for loop. This will guarantee a unique ID. Then when loading, sort your array ascending and you have your list.

Where to store Dictionary data in iOS

I am developing iPhone app,i have one doubt
I have an NSMutableDictionary which contains data in this format
dict at 0th index:
"ProductId":"77386",
"ProductImage":"http://static.abcd.com/images/product/large/design_gallery_slide_green - Copy (2).jpg",
"Productshortname":"Apple iPhone 5c 16GB",
"categorycode":null,
"categoryid":8,
"categoryimage":"",
"categoryshortname":"",
"favorite":"0",
"price":"31500",
"productnameinUrl":"apple-iphone-5c-16gb",
"storecount":"10"
dict at 1st index:
"ProductId":"11386",
"ProductImage":"http://static.abcd.com/images/product/large/design_gallery_slide_green - Copy (2).jpg",
"Productshortname":"Apple iPhone 5s 16GB",
"categorycode":null,
"categoryid":8,
"categoryimage":"",
"categoryshortname":"",
"favorite":"1",
"price":"31500",
"productnameinUrl":"apple-iphone-5s-16gb",
"storecount":"18"
dict at 2nd index:
"ProductId":"31386",
"ProductImage":"http://static.abcd.com/images/product/large/design_gallery_slide_green - Copy (2).jpg",
"Productshortname":"Apple iPhone 4s 16GB",
"categorycode":null,
"categoryid":8,
"categoryimage":"",
"categoryshortname":"",
"favorite":"1",
"price":"31500",
"productnameinUrl":"apple-iphone-4s-16gb",
"storecount":"38"
and so on...
What i want to do is, i want to store this dictionary indexes some where in my directory and i want to fetch it after some time or even after closing and opening the app after few times.
where should i store this kind of data ? is there any storage for strong this kind of data?
Please help and thanks in advance !!
You can store the data in NSUserdefaults and can access any time and anywhere as you want
yourdict;//Your NSDictionary Object That contains the data to store
[[NSUserDefaults standardUserDefaults] setObject:yourdict forKey:#"dict"];
[[NSUserDefaults standardUserDefaults] synchronize];
At the time of retrieval of data,
dict = [[NSUserDefaults standardUserDefaults] objectForKey:#"dict"];
You've already chosen an approved answer but I'll throw my thoughts in anyway.
This information looks like it could get large.
The user defaults isn't designed for large chunks of data. It's really meant for small bits of information, such as boolean preferences etc etc, not to be treated as an easy-to-use database.
Some problems with the user defaults:
The defaults file is read and parsed when you launch your app, regardless of whether you need your information from it at that time or not. This is because other parts of your app also use it for storing their bits of info too.
The entire defaults file needs to be parsed in order for you to retrieve anything, even if you just want a single entry.
You don't choose when the defaults file is parsed. You can't do any smart threading if it becomes huge (say you put 1000 products in there)
I'd recommend either writing the dictionary to it's own plist using NSDictionary's writeToFile: and reading using initWithContentsOfFile: (this still suffers from point #2 above)
OR
Using CoreData/sqlite to write the information to a real database.
NSDictionary methods: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSDictionary_Class/Reference/Reference.html#//apple_ref/occ/instm/NSDictionary/writeToFile:atomically:
An other option (And better in my experience) is to use - NSCoder, this option is great as you can use an Object with normal properties to access your data, which make your code more readable.
You can read about it here - NSCoding / NSKeyed​Archiver by NSHipster
An here is the reference - NSCoder docs
NSDictionary has a writeToFile: method which will do it for you.
NSDicationary writeToFile:Atomically:
use NSUserDefault and save your data like this in array
Here you can use this in anyway in your application for store value of NSUserDefaults.
// --- Saving
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
// saving an NSString
[prefs setObject:#"TextToSave" forKey:#"keyToLookupString"];
// saving an NSInteger
[prefs setInteger:42 forKey:#"integerKey"];
// saving a Double
[prefs setDouble:3.1415 forKey:#"doubleKey"];
// saving a Float
[prefs setFloat:1.2345678 forKey:#"floatKey"];
// This is suggested to synch prefs, but is not needed (I didn't put it in my tut)
[prefs synchronize];
Here you can use this in anyway in your application for get value of NSUserDefaults.
// --- Retrieving
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
// getting an NSString
NSString *myString = [prefs stringForKey:#"keyToLookupString"];
// getting an NSInteger
NSInteger myInt = [prefs integerForKey:#"integerKey"];
// getting an Float
float myFloat = [prefs floatForKey:#"floatKey"];
Thanks & Cheers ..
Looks like there can be more amount of data, so the best approach is to use core data to handle this scenario.
You can check few tutorials on how to use core data - Link1 , Link2
There are advantage of using core data over NSUserDefault and file system, as those load all the data at once and you might face some issue in performance.
You can also check following links which will illustrate you performance of different mechanism used to store data - PerformanceLinks1 PerformanceLinks2
try this
NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:
#"audio.caf",#"pictureAudioKey",
#"audio.m4a",#"englishAudioKey",
#"audio2.m4a",#"spanishAudioKey",
#"audio3.m4a",#"frenchAudioKey",
#"audio4.m4a",#"germanAudioKey",
#"audio5.m4a",#"italianAudioKey",
#"audio6.m4a",#"chineseAudioKey",
#"image.jpg",#"photoimagekey",
#"name.txt", #"identity",
#"imagename.txt",#"numberkey",nil];
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [documentPaths objectAtIndex:0];
NSString *dictionaryPath = [documentsDirectory stringByAppendingPathComponent:[[self imageNameTextField]text]];
dictionaryPath =[dictionaryPath stringByAppendingFormat:#"dicitonary" ] ;
NSDictionary *savedDictionary = dictionary;
NSLog(#"The Save file is:%#", savedDictionary);
[savedDictionary writeToFile:dictionaryPath atomically:YES];

Saving Successfully, but not Visibly Editing plist

So this is my first time trying to save data in an iOS app. I've pieced together this code from various answers on this site in order to save a high score for a game I'm making. I created a plist named saves.plist (in my Supporting Files folder) and added a row of key #"bestScore" and type Number. The test log returns that the save is successful, and everything works; however, when I go to look at the plist after, nothing seems to have changed (the value of bestScore is 0). Am I saving to a different plist that is automatically created in my code? If this is the case, what is the point of being able to create plists in Xcode, and what is the best practice to use here as far as where/how to create/store/access plists?
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.fm = [NSFileManager defaultManager];
self.destPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];//Documents directory
self.destPath = [self.destPath stringByAppendingPathComponent:#"saves.plist"];
// If the file doesn't exist in the Documents Folder, copy it.
if (![self.fm fileExistsAtPath:self.destPath]) {
NSString *sourcePath = [[NSBundle mainBundle] pathForResource:#"saves" ofType:#"plist"];
[self.fm copyItemAtPath:sourcePath toPath:self.destPath error:nil];
}
}
- (void)saveBestScore{
NSNumber *bestScoreBox = [NSNumber numberWithUnsignedLong:self.bestScore];
NSDictionary *data = #{bestScoreBox: #"bestScore"};
BOOL successful = [data writeToFile:self.destPath atomically:YES];
successful ? NSLog(#"YES") : NSLog(#"NO");
}
When you say
when I go to look at the plist after, nothing seems to have changed
(the value of bestScore is 0)
Do you mean looking at the plist in xcode project files ? You have copied the plist into a device directory and therefore you wont be able to see the change in xcode.
If you are using simulator, you can access the changed plist at:
~/Library/Application Support/iPhone Simulator/<Simulator Version>/Applications/<application>/Documents/
One easy way of storing score is to use NSUserDefault, which is a dictionary like persistence store for each application.
Set Score:
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:#(score)
forKey:#"score"];
[userDefaults synchronize];
Get Score:
int score = [[[NSUserDefaults standardUserDefaults] objectForKey:#"score"] intValue];
UPDATE:
rmaddy mentioned NSUserDefaults supports setInteger:forKey and integerForKey: Therefore you dont need to wrap the score into a NSNumber
When you write an NSDictionary to a plist using writeToFile:, the keys and values in the dictionary must follow strict rules. All keys must be NSString objects and all values must be property values (NSString, NSNumber, NSDate, NSData, etc.).
The problem you have is your dictionary has a key that is an NSNumber, not an NSString.
It appears you actually create the dictionary incorrectly. The syntax is:
#{ key : value, key : value, ... }
Change your code to:
NSDictionary *data = #{ #"bestScore" : bestScoreBox }; // key : value
Side note - your last line should be:
NSLog(#"%#", successful ? #"YES" : #"NO");
It's not good practice to use the ternary operator to run two different commands. It's meant to return one of two values.

Delete all keys from a NSUserDefaults dictionary iOS

I use the NSUserDefaults dictionary to store basic information such as high scores etc so that when the user closes the app data is not lost. Anyways I use:
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
to store data. If I wish to store a new high score for example then I would do:
[prefs setInteger:1023 forKey:#"highScore"];
[prefs synchronize]; //this is needed in case the app is closed.
and later if I wish to retrieve the high score I would do:
[prefs integerForKey:#"highScore"];
anyways the point is that I store a lot of other things because the NSUserDefaults enable you to store booleans, integers, objects etc. what method would I have to execute to delete all keys so that NSUserDefaults becomes like the fist time I launch the app?
I am looking for something like:
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[prefs deleteAllKeysAndObjectsInTheDictionary];
or maybe there is a way of getting all keys and I have to loop through each object but I don't know how to remove them.
EDIT:
I have tried :
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[NSUserDefaults resetStandardUserDefaults];
[prefs synchronize];
and I still am able to retrieve a high score....
If you have a look at the NSUserDefaults documentation you will see a method - (NSDictionary *) dictionaryRepresentation. Using this method on the standard user defaults, you can get a list of all keys in the user defaults. You can then use this to clear the user defaults:
- (void)resetDefaults {
NSUserDefaults * defs = [NSUserDefaults standardUserDefaults];
NSDictionary * dict = [defs dictionaryRepresentation];
for (id key in dict) {
[defs removeObjectForKey:key];
}
[defs synchronize];
}
Shortest way to do this with the same results like in Alex Nichol's top answer:
NSString *appDomain = NSBundle.mainBundle.bundleIdentifier;
[[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain];
[[NSUserDefaults standardUserDefaults] synchronize];
One-liner:
[[NSUserDefaults standardUserDefaults] removePersistentDomainForName:NSBundle.mainBundle.bundleIdentifier];
Simple Solution
Objective C:
NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier];
[[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain];
Swift 3.0 to Swift 5.0 :
if let appDomain = Bundle.main.bundleIdentifier {
UserDefaults.standard.removePersistentDomain(forName: appDomain)
}
Swift version:
if let bid = NSBundle.mainBundle().bundleIdentifier {
NSUserDefaults.standardUserDefaults().removePersistentDomainForName(bid)
}
+ (void) resetStandardUserDefaults doesn't persist the changes, it simply resets the in-memory user defaults object so that the next synchronize call will read from the on-disk copy, instead of overwriting existing in-memory values with the on-disk versions.
Iterating over the keys is better, but there's actually a function that does this for you: removePersistentDomainForName:.
// you can usually get the domain via [[NSBundle mainBundle] bundleIdentifier]
[[NSUserDefaults standardUserDefaults]
removePersistentDomainForName:[[NSBundle mainBundle] bundleIdentifier]];
// or use a string for any other settings domains you use
//[[NSUserDefaults standardUserDefaults]
// removePersistentDomainForName:#"com.mycompany.myappname"];
[[NSUserDefaults standardUserDefaults] synchronize];
At the end of the synchronize operation, both the disk and memory copies of user defaults will contain none of the values set by your application.
Oneliner in Swift:
Swift 3
NSUserDefaults.standardUserDefaults().removePersistentDomainForName(
NSBundle.mainBundle().bundleIdentifier!)
Swift 4
UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!)
For those of you that want to do this in the test target, use this (as the removePersistentDomain does not work for that case)
Swift 3:
for key in Array(UserDefaults.standard.dictionaryRepresentation().keys) {
UserDefaults.standard.removeObject(forKey: key)
}
For Swift 3:
let appDomain = Bundle.main.bundleIdentifier!
UserDefaults.standard.removePersistentDomain(forName: appDomain)
For Swift 3:
if let bundle = Bundle.main.bundleIdentifier {
UserDefaults.standard.removePersistentDomain(forName: bundle)
}
I found it the most handy to place the code in an extension on UserDefaults.
Swift 5
extension UserDefaults {
static func clear() {
guard let domain = Bundle.main.bundleIdentifier else { return }
UserDefaults.standard.removePersistentDomain(forName: domain)
UserDefaults.standard.synchronize()
}
}
Usage
UserDefaults.clear()
Swift
place in your logic
if let appDomain = Bundle.main.bundleIdentifier {
UserDefaults.standard.removePersistentDomain(forName: appDomain)
}
Swift
func resetUserDefaults(){
let userDefaults = NSUserDefaults.standardUserDefaults()
let dict = userDefaults.dictionaryRepresentation() as NSDictionary
for key in dict.allKeys {
userDefaults.removeObjectForKey(key as! String)
}
userDefaults.synchronize()
}
Does this method not do that:
+ (void)resetStandardUserDefaults
From the documentation for NSUserDefaults:
resetStandardUserDefaults
Synchronizes any changes made to the shared user defaults object and
releases it from memory.
+ (void)resetStandardUserDefaults
Discussion
A subsequent invocation of standardUserDefaults creates a new shared
user defaults object with the standard search list.
Based on this, you can do:
[NSUserDefaults resetStandardUserDefaults];
[NSUserDefaults standardUserDefaults];
and now the defaults should be reset.
Swift 3 or 4
We can even simplify described snippet into this modern expression:
func clearAll() {
let settingsDictionary = userDefaults.dictionaryRepresentation()
settingsDictionary.forEach { key, _ in userDefaults.removeObject(forKey: key) }
userDefaults.synchronize()
}
To remove all UserDefault value in swift (Latest syntax)
//remove UserDefaults
if let identifier = Bundle.main.bundleIdentifier {
UserDefaults.standard.removePersistentDomain(forName: identifier)
UserDefaults.standard.synchronize()
}
In Swift 5.0 below single line of code is enough.
UserDefaults.standard.dictionaryRepresentation().keys.forEach(defaults.removeObject(forKey:))
I use this:
UserDefaults.standard.removeAll()

iPhone first time application ran and config files

I'm developing an iPhone application and I want to know when it is the first time the application is executed. I want to check some extended permissions from facebook the first time.
How can I know that?
Another way to solved this problem is to store the extended permissions granted in some configuration file. I don't want to make visible this file through app settings icon.
How can I add some configuration files to store these permissions granted?
The std way would be to check if there is a value in NSUserDefaults and if that value doesn't exist (such as on first run) then create the value so on the rest of the starts you will know its not first run.
This is also a good candidate to use the NSUserDefaults and set key/value pares for each setting/permission you want.
Here's a quick tutorial on using NSUserDefaults.
Summary from that site for Saving:
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
// saving an NSString
[prefs setObject:#"TextToSave" forKey:#"keyToLookupString"];
// saving an NSInteger
[prefs setInteger:42 forKey:#"integerKey"];
// saving a Double
[prefs setDouble:3.1415 forKey:#"doubleKey"];
// saving a Float
[prefs setFloat:1.2345678 forKey:#"floatKey"];
// This is suggested to synch prefs, but is not needed (I didn't put it in my tut)
[prefs synchronize];
Loading:
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
// getting an NSString
NSString *myString = [prefs stringForKey:#"keyToLookupString"];
// getting an NSInteger
NSInteger myInt = [prefs integerForKey:#"integerKey"];
// getting an Float
float myFloat = [prefs floatForKey:#"floatKey"];
NSUserDefaults is perfect for saving app settings, and purchase history but if you are wanting to store much data then you should use another way.
Just write to a local file (xml or whatever).
Check, first, for the existence of the file. If not there this is the first run - otherwise just read the file.

Resources