Should I use NSUserDefaults or a plist to store data?
Asked Answered
F

9

52

I will be storing a few strings (maybe 10-20). I am not sure if I should use NSUserDefaults to save them, or write them out to a plist. What is considered best practice? NSUserDefaults seems like it is less lines of code, therefore quicker to implement.

I'd like to add that these string values will be added/removed by the user.

Fatten answered 14/8, 2011 at 18:37 Comment(0)
T
86

I am assuming an array, but it will work with dictionaries too.

Userdefaults, Core Data and Plists can all be read/write but if you use a plist you need to pay attention in what dir you put it. See the plist part down below.

Core Data I think it's way too much overkill, it's just strings. It's supposed to be used when you want to persist more complex objects.

NSUserDefaults:

It's pretty fast and easy to do, though it's supposed to store only user settings. To write them to the userdefaults:

NSArray *stringsArray = [[NSArray alloc] arrayWithObjects: string1, string2, string3, nil];
[[NSUserDefaults standardUserDefaults] setObject:stringsArray forKey:@"MyStrings"];
[[NSUserDefaults standardUserDefaults] synchronize];

To read the from the userdefaults:

NSArray *stringsArray = [[NSUserDefaults standardUserDefaults] objectForKey:@"MyStrings"];

Plist:

If your strings are going to be modified you will need to write and read a plist but you cant't write into your app's resources.

  1. To have a read/write plist first find the documents directory

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *stringsPlistPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"Strings.plist"];
    
  2. Create the array (I am assuming the strings are string1, ...)

    NSArray *stringsArray = [[NSArray alloc] arrayWithObjects: string1, string2, string3, nil];
    
  3. Write it to file

    [stringsArray writeToFile:stringsPlistPath atomically:YES];
    

To read the plist:

  1. Find the documents directory

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *stringsPlistPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"Strings.plist"];
    
  2. Read it in:

    NSArray *stringsArray = [NSArray arrayWithContentsOfFile:stringsPlistPath];
    
Tao answered 14/8, 2011 at 20:13 Comment(4)
I implemented the PList solution for a list of recently used URLs. It seems that the PList was the correct approach for my problem. But I have to say that after having done the same on the Android, this was surprisingly far more involved as far as a learning curve and the overall effort. So I have to comment and say thanks so much for the example code above...Holism
Question... when you say "you can't write into your apps resources" do you mean that we can't write to a plist that we create and populate from within xcode?Lepley
You can only write in the Documents directory of your app. So you could copy that plist from the Resources directory to the Documents directory at runtime and then edit it.Tao
I want to add list of URL's to be used in my app. Is plist a safe option to do that? I don't want my UR's to get snuffedMortimer
S
8

If you are storing 10-20 strings and are looking for not too many lines of code, core data is certainly much too much overhead. I recommend going with the plist. Not a lot of code:

NSURL *plistURL = [[NSBundle mainBundle] URLForResource:@"MyStrings" withExtension:@"plist"];
NSArray *stringArray = [NSArray arrayWithContentsOfURL:plistURL];
Sideshow answered 14/8, 2011 at 18:57 Comment(0)
B
8

iOS ultimately stores all NSUserDefaults data to a plist file. So it will not affect the performance if that is your concern. I personally prefer using NSUserDefaults for small data and plist for a relatively large set of data.

Note: Never store any sensitive information in NSUserDefaults as anyone can see that data.

Benison answered 9/3, 2015 at 11:13 Comment(0)
D
5

NSUserDefaults will store the user preferences into a file into the Library/Preferences folder. In theory it serves only to store some application/user properties.

Plist file are usefull to manage a single file. If you need to manage more you should use the Coredata. There is no restriction about the size of the plist file. Otherwise you have to be careful with plist file because when you need to save or read it the entire contents of the file will be load into memory.

Draughtboard answered 14/8, 2011 at 19:9 Comment(0)
B
3

Using .plist

  1. Create a plist using Xcode

Write a value to plist

NSURL *plistURL = [[NSBundle mainBundle] URLForResource:@"settings" withExtension:@"plist"];

 NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithContentsOfURL:plistURL];
 [dict setValue:@"value" forKey:@"key"];
 [dict writeToURL:plistURL atomically:YES];

Read a value from plist

NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithContentsOfURL:plistURL];
NSString *myValue = [dict valueForKey:@"key"];
Brogdon answered 6/7, 2015 at 6:30 Comment(0)
U
2

It depends on what you want to store and why. NSUserDefaults is meant for storing user preferences. You can try to use it for other things, but you probably shouldn't.

Otherwise, if your needs are simple a plist file is pretty straightforward. You can also use core data or come up with your own file format. In general, I use plist for simple tasks and then move to core data for anything more complex.

Unthread answered 14/8, 2011 at 18:56 Comment(0)
C
2

Using a plist is a good choice for storing your strings if the strings are not just user settings that can go in NSUserDefaults. As was mentioned, when using a plist you must store your plist in the Documents directory in order to write to it, because you can't write into your own app's resources. When I first learned this, I wasn't clear on where your own app's Bundle directory was vs. where the Documents directory was, so I thought I'd post example code here that first copies a plist called "Strings.plist" (that you already have in your own app's Bundle directory) to the Documents directory, and then writes to it and reads from it.

// Make a path to the plist in the Documents directory
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *stringsPlistPathIndDoc = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"Strings.plist"];

// Make a path to the plist in your app's Bundle directory
NSString *stringsPlistPathInBundle = [[NSBundle mainBundle] pathForResource:@"Strings" ofType:@".plist"];

NSFileManager *fileManager = [NSFileManager defaultManager];

// Check first that you haven't already copied this plist to the Documents directory
if (![fileManager fileExistsAtPath:stringsPlistPathIndDoc])
{
    NSError *error;

    // Copy the plist from the Bundle directory to the Documents directory 
    [fileManager copyItemAtPath:stringsPlistPathInBundle toPath:stringsPlistPathIndDoc error:&error];
}

// Write your array out to the plist in the Documents directory
NSMutableArray *stringsArray = [[NSMutableArray alloc] initWithObjects:@"string1", @"string2", @"string3", nil]; 
[stringsArray writeToFile:stringsPlistPathIndDoc atomically:YES];

// Later if you want to read it:
stringsArray = [[NSMutableArray alloc] initWithContentsOfFile:stringsPlistPathIndDoc];
Coronograph answered 6/12, 2014 at 14:44 Comment(0)
C
2

NSUSerDefaults is indeed quick to implement, but mostly as your application grows, you want to store more and more, I went directly for plist files.

Mostly, people want to store a list of something, so here is my share on how to do this with NSDictionary. This does not require you to create a plist file first, it will be created at the first time saving something

xcode 7 beta, Swift 2.0

saving

func SaveItemFavorites(items : Array<ItemFavorite>) -> Bool
{
    let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as NSArray
    let docuDir = paths.firstObject as! String
    let path = docuDir.stringByAppendingPathComponent(ItemFavoritesFilePath)
    let filemanager = NSFileManager.defaultManager()

    let array = NSMutableArray()        
    
    for var i = 0 ; i < items.count ; i++
    {
        let dict = NSMutableDictionary()
        let ItemCode = items[i].ItemCode as NSString
        
        dict.setObject(ItemCode, forKey: "ItemCode")
        //add any aditional..
        array[i] = dict
    }
    
    let favoritesDictionary = NSDictionary(object: array, forKey: "favorites")
    //check if file exists
    if(!filemanager.fileExistsAtPath(path))
    {
        let created = filemanager.createFileAtPath(path, contents: nil, attributes: nil)
         if(created)
         {
             let succeeded = favoritesDictionary.writeToFile(path, atomically: true)
             return succeeded
         }
         return false
    }
    else
    {
        let succeeded = notificationDictionary.writeToFile(path, atomically: true)
        return succeeded
    }
}

Little note from the docs:

NSDictionary.writeToFile(path:atomically:)

This method recursively validates that all the contained objects are property list objects (instances of NSData, NSDate, NSNumber, NSString, NSArray, or NSDictionary) before writing out the file, and returns NO if all the objects are not property list objects, since the resultant file would not be a valid property list.

So whatever you set at dict.SetObject() should be one of the above mentioned types.

loading

private let ItemFavoritesFilePath = "ItemFavorites.plist"

func LoadItemFavorites() -> Array<ItemFavorite>
{
    let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as NSArray
    let docuDir = paths.firstObject as! String
    let path = docuDir.stringByAppendingPathComponent(ItemFavoritesFilePath)
    let dict = NSDictionary(contentsOfFile: path)
    let dictitems : AnyObject? = dict?.objectForKey("favorites")
    
    var favoriteItemsList = Array<ItemFavorite>()
    
    if let arrayitems = dictitems as? NSArray
    {
        for var i = 0;i<arrayitems.count;i++
        {
            if let itemDict = arrayitems[i] as? NSDictionary
            {
                let ItemCode = itemDict.objectForKey("ItemCode") as? String
                //get any additional
                
                let ItemFavorite = ItemFavorite(item: ItemCode)
                favoriteItemsList.append(ItemFavorite)
            }
        }
    }
    
    return favoriteItemsList
}
Capsize answered 13/7, 2015 at 18:37 Comment(0)
M
1

The recommended way to persist data like this is to use Core Data. While NSUserDefaults can be used to store more or less anything it's only supposed to be used to store preferences.

Monocycle answered 14/8, 2011 at 18:40 Comment(4)
Why would Core Data be preferable to a plist for a handful of strings?Seraphine
Because then the framework will take care of all the details like file handling, synchronization and error handling.Monocycle
@MarcusKarlsson NSUserDefaults will also provide this handling for the OP. The small number of strings the OP lists seems to me to be more equivalent to user preference, although more information would be helpful to guide here.Cimabue
What if i want to store cookies and there are around 50 of them. Should I, in this case, use core data, nsuserdefaults or plist?Pecan

© 2022 - 2024 — McMap. All rights reserved.