189

I'm currently working on a iOS app developed in Swift and I need to store some user-created content on the device but I can't seem to find a simple and quick way to store/receive the users content on the device.

Could someone explain how to store and access local storage?

The idea is to store the data when the user executes an action and receive it when the app starts.

Wez
  • 10,555
  • 5
  • 49
  • 63
Nicklas Ridewing
  • 2,410
  • 2
  • 14
  • 26
  • Hey and welcome, what sort of data are you storing? Can you provide any code to give an example of what you are looking for? Thanks. – Wez Feb 20 '15 at 11:48
  • 1
    The data that I need to store is basically just string data. So to keep it really simple, I need to save two String values that I can receive if the user restarts the app. – Nicklas Ridewing Feb 20 '15 at 11:50
  • 2
    You can use NSUserDefaults. Here is the information you need http://www.codingexplorer.com/nsuserdefaults-a-swift-introduction/ – bpolat Feb 20 '15 at 11:51
  • I found this answer and it enabled me to save data, but since Swift 4.1 there has been a much easier way to do this using appstorage. I've made a video tutorial to help you do this: http://youtube.com/watch?v=nLsJD6yL9Ps – James Abela Dec 30 '22 at 02:22

12 Answers12

228

The simplest solution for storing a few strings or common types is UserDefaults.

The UserDefaults class provides convenience methods for accessing common types such as floats, doubles, integers, Boolean values, and URLs.

UserDefaults lets us store objects against a key of our choice, It's a good idea to store these keys somewhere accessible so we can reuse them.

Keys

struct DefaultsKeys {
    static let keyOne = "firstStringKey"
    static let keyTwo = "secondStringKey"
}

Setting

let defaults = UserDefaults.standard
defaults.set(
    "Some String Value", 
    forKey: DefaultsKeys.keyOne
)
defaults.set(
    "Another String Value", 
    forKey: DefaultsKeys.keyTwo
)

Getting

let defaults = UserDefaults.standard
if let stringOne = defaults.string(
    forKey: DefaultsKeys.keyOne
) {
    print(stringOne) // Some String Value
}
if let stringTwo = defaults.string(
    forKey: DefaultsKeys.keyTwo
) {
    print(stringTwo) // Another String Value
}

Swift 2.0

In Swift 2.0 UserDefaults was called NSUserDefaults and the setters and getters were named slightly differently:

Setting

let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(
    "Some String Value", 
    forKey: DefaultsKeys.keyOne
)
defaults.setObject(
    "Another String Value", 
    forKey: DefaultsKeys.keyTwo
)

Getting

let defaults = NSUserDefaults.standardUserDefaults()
if let stringOne = defaults.stringForKey(
    DefaultsKeys.keyOne
) {
    print(stringOne) // Some String Value
}
if let stringTwo = defaults.stringForKey(
    DefaultsKeys.keyTwo
) {
    print(stringTwo) // Another String Value
}

For anything more serious than minor config you should consider using a more robust persistent store:

Wez
  • 10,555
  • 5
  • 49
  • 63
  • 8
    Nice example. This should probably be an enum instead of a struct though. – pan-and-scan Jul 11 '15 at 15:36
  • 3
    Thats a strange implementation of enum, if you just have static constants, it may as well be a struct. – Craig Grummitt Mar 14 '16 at 23:39
  • 1
    This code is wrong. Do not use the KVC methods `setValue(_:forKey:)` to save data to UserDefaults. Use the provided `UserDefaults` methods of `set(_:forKey:)` (in Swift 3). – rmaddy Mar 28 '17 at 18:47
  • I updated the Swift 3 code. I don't know what the Swift 2 syntax should be. – rmaddy Mar 28 '17 at 21:32
  • @rmaddy it's fine as is. – Wez Mar 28 '17 at 21:34
  • Not likely. `setValue` is from key-value coding, not part of the `NSUserDefaults` API. Given the Objective-C API, it should probably be `setObject`. – rmaddy Mar 28 '17 at 21:35
  • You can set more then just strings, the documentation for example says you can store double's as well (func set(Double, forKey: String)), also includes , Float, Int, Bool & URL as the defaults allowed to store out of the box – Joseph Astrahan Mar 06 '20 at 23:16
  • What if it's an object that we try to store and not a string? – JAgüero Oct 29 '22 at 17:24
  • @parkorian The [documentation](https://developer.apple.com/documentation/foundation/userdefaults#2926904) says: "If you want to store any other type of object, you should typically archive it to create an instance of NSData." - it would also be possible to encode an object to a json string or similar and store it as a string in defaults. – Wez Dec 14 '22 at 09:33
67

They Say Use NSUserDefaults

When I was implementing long term (after app close) data storage for the first time, everything I read online pointed me towards NSUserDefaults. However, I wanted to store a dictionary and, although possible, it was proving to be a pain. I spent hours trying to get type-errors to go away.

NSUserDefaults is Also Limited in Function

Further reading revealed how the read/write of NSUserDefaults really forces the app to read/write everything or nothing, all at once, so it isn't efficient. Then I learned that retrieving an array isn't straight forward. I realized that if you're storing more than a few strings or booleans, NSUserDefaults really isn't ideal.

It's also not scalable. If you're learning how to code, learn the scalable way. Only use NSUserDefaults for storing simple strings or booleans related to preferences. Store arrays and other data using Core Data, it's not as hard as they say. Just start small.

Update: Also, if you add Apple Watch support, there's another potential consideration. Your app's NSUserDefaults is now automatically sent to the Watch Extension.

Using Core Data

So I ignored the warnings about Core Data being a more difficult solution and started reading. Within three hours I had it working. I had my table array being saved in Core Data and reloading the data upon opening the app back up! The tutorial code was easy enough to adapt and I was able to have it store both title and detail arrays with only a little extra experimenting.

So for anyone reading this post who's struggling with NSUserDefault type issues or whose need is more than storing strings, consider spending an hour or two playing with core data.

Here's the tutorial I read:

http://www.raywenderlich.com/85578/first-core-data-app-using-swift

If you didn't check "Core Data"

If you didn't check "Core Data"when you created your app, you can add it after and it only takes five minutes:

http://craig24.com/2014/12/how-to-add-core-data-to-an-existing-swift-project-in-xcode/

http://blog.zeityer.com/post/119012600864/adding-core-data-to-an-existing-swift-project

How to Delete from Core Data Lists

Delete Data from Coredata Swift

Dave G
  • 12,042
  • 7
  • 57
  • 83
  • 6
    There is the middle ground of saving your data to a .plist file somewhere in the Documents directory or elsewhere in the app's sandboxed directory. – Nicolas Miari Aug 06 '15 at 02:52
  • I'm a novice, so take this question with a grain of salt, but is that middle ground? NSUserDefaults is practically a p.list file anyways, so (based on what I've read) saving to a p.list file in the documents directory is kind of the same as using NSUserDefaults. The reason I didn't use that method was because saving an array to a p.list sounded like it involved extra steps, like converting it to another type and then later, when restoring values, it required re-assigning the values after reading them from the p.list. Again, solely based on my reading over the past few days. – Dave G Aug 06 '15 at 03:53
  • Well, saving an array to a .plist file is pretty straightforward (all objects in the hierarchy are Arrays, Strings, Dictionaries, or Numbers (no custom classes). – Nicolas Miari Aug 06 '15 at 03:56
  • Agreed, reading the file is all-or-nothing, but you can split your data into separate files depending on its structure. And CoreData had a significant learning curve, at least the last time I checked. – Nicolas Miari Aug 06 '15 at 03:57
  • 3
    I would say: use NSUserDefaults to store preferences (that's what the name means after all), The keychain to store persistent and/or sensitive data, your custom scheme of .plist files for _very basic_ app generated data, and CoreData for more heavy stuff. – Nicolas Miari Aug 06 '15 at 04:02
  • Yeah, everyone seems to agree that Core Data is trickier. I just couldn't get the NSUserDefaults to accept my dictionary. Maybe it was considered a custom class since I used a strut to give names/format to the dictionary. – Dave G Aug 06 '15 at 04:04
  • If you've got CoreData figured out, go for it! Your app will definitely scale well down the road. – Nicolas Miari Aug 06 '15 at 04:05
  • I liked the references here, but I was working in SwiftUI and the raywenderlich.com link was more confusing than helpful. I found this one about Core Data and SwiftUI: https://blckbirds.com/post/core-data-and-swiftui/ – pjmorse Jun 28 '20 at 19:22
11

Okey so thanks to @bploat and the link to http://www.codingexplorer.com/nsuserdefaults-a-swift-introduction/

I've found that the answer is quite simple for some basic string storage.

let defaults = NSUserDefaults.standardUserDefaults()

// Store
defaults.setObject("theGreatestName", forKey: "username")

// Receive
if let name = defaults.stringForKey("username")
{
    print(name)
    // Will output "theGreatestName"
}

I've summarized it here http://ridewing.se/blog/save-local-data-in-swift/

Community
  • 1
  • 1
Nicklas Ridewing
  • 2,410
  • 2
  • 14
  • 26
9

Using NSCoding and NSKeyedArchiver is another great option for data that's too complex for NSUserDefaults, but for which CoreData would be overkill. It also gives you the opportunity to manage the file structure more explicitly, which is great if you want to use encryption.

Patrick Lynch
  • 2,742
  • 1
  • 16
  • 18
9

For Swift 4.0, this got easier:

let defaults = UserDefaults.standard
//Set
defaults.set(passwordTextField.text, forKey: "Password")
//Get
let myPassword = defaults.string(forKey: "Password")
7

Swift 5+

None of the answers really cover in detail the default built in local storage capabilities. It can do far more than just strings.

You have the following options straight from the apple documentation for 'getting' data from the defaults.

func object(forKey: String) -> Any?
//Returns the object associated with the specified key.

func url(forKey: String) -> URL?
//Returns the URL associated with the specified key.

func array(forKey: String) -> [Any]?
//Returns the array associated with the specified key.

func dictionary(forKey: String) -> [String : Any]?
//Returns the dictionary object associated with the specified key.

func string(forKey: String) -> String?
//Returns the string associated with the specified key.

func stringArray(forKey: String) -> [String]?
//Returns the array of strings associated with the specified key.

func data(forKey: String) -> Data?
//Returns the data object associated with the specified key.

func bool(forKey: String) -> Bool
//Returns the Boolean value associated with the specified key.

func integer(forKey: String) -> Int
//Returns the integer value associated with the specified key.

func float(forKey: String) -> Float
//Returns the float value associated with the specified key.

func double(forKey: String) -> Double
//Returns the double value associated with the specified key.

func dictionaryRepresentation() -> [String : Any]
//Returns a dictionary that contains a union of all key-value pairs in the domains in the search list.

Here are the options for 'setting'

func set(Any?, forKey: String)
//Sets the value of the specified default key.

func set(Float, forKey: String)
//Sets the value of the specified default key to the specified float value.

func set(Double, forKey: String)
//Sets the value of the specified default key to the double value.

func set(Int, forKey: String)
//Sets the value of the specified default key to the specified integer value.

func set(Bool, forKey: String)
//Sets the value of the specified default key to the specified Boolean value.

func set(URL?, forKey: String)
//Sets the value of the specified default key to the specified URL.

If are storing things like preferences and not a large data set these are perfectly fine options.

Double Example:

Setting:

let defaults = UserDefaults.standard
var someDouble:Double = 0.5
defaults.set(someDouble, forKey: "someDouble")

Getting:

let defaults = UserDefaults.standard
var someDouble:Double = 0.0
someDouble = defaults.double(forKey: "someDouble")

What is interesting about one of the getters is dictionaryRepresentation, this handy getter will take all your data types regardless what they are and put them into a nice dictionary that you can access by it's string name and give the correct corresponding data type when you ask for it back since it's of type 'any'.

You can store your own classes and objects also using the func set(Any?, forKey: String) and func object(forKey: String) -> Any? setter and getter accordingly.

Hope this clarifies more the power of the UserDefaults class for storing local data.

On the note of how much you should store and how often, Hardy_Germany gave a good answer on that on this post, here is a quote from it

As many already mentioned: I'm not aware of any SIZE limitation (except physical memory) to store data in a .plist (e.g. UserDefaults). So it's not a question of HOW MUCH.

The real question should be HOW OFTEN you write new / changed values... And this is related to the battery drain this writes will cause.

IOS has no chance to avoid a physical write to "disk" if a single value changed, just to keep data integrity. Regarding UserDefaults this cause the whole file rewritten to disk.

This powers up the "disk" and keep it powered up for a longer time and prevent IOS to go to low power state.

Something else to note as mentioned by user Mohammad Reza Farahani from this post is the asynchronous and synchronous nature of userDefaults.

When you set a default value, it’s changed synchronously within your process, and asynchronously to persistent storage and other processes.

For example if you save and quickly close the program you may notice it does not save the results, this is because it's persisting asynchronously. You might not notice this all the time so if you plan on saving before quitting the program you may want to account for this by giving it some time to finish.

Maybe someone has some nice solutions for this they can share in the comments?

Joseph Astrahan
  • 8,659
  • 12
  • 83
  • 154
6

Swift 3.0

Setter :Local Storage

let authtoken = "12345"
    // Userdefaults helps to store session data locally 
 let defaults = UserDefaults.standard                                           
defaults.set(authtoken, forKey: "authtoken")

 defaults.synchronize()

Getter:Local Storage

 if UserDefaults.standard.string(forKey: "authtoken") != nil {

//perform your task on success }
Uma Madhavi
  • 4,851
  • 5
  • 38
  • 73
Ravindra Shekhawat
  • 4,275
  • 1
  • 19
  • 26
5

For Swift 3

UserDefaults.standard.setValue(token, forKey: "user_auth_token")
print("\(UserDefaults.standard.value(forKey: "user_auth_token")!)")
Arun Yokesh
  • 1,346
  • 17
  • 19
4

For someone who'd not prefer to handle UserDefaults for some reasons, there's another option - NSKeyedArchiver & NSKeyedUnarchiver. It helps save objects into a file using archiver, and load archived file to original objects.

// To archive object,
let mutableData: NSMutableData = NSMutableData()
let archiver: NSKeyedArchiver = NSKeyedArchiver(forWritingWith: mutableData)
archiver.encode(object, forKey: key)
archiver.finishEncoding()
return mutableData.write(toFile: path, atomically: true)

// To unarchive objects,
if let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
    let unarchiver = NSKeyedUnarchiver(forReadingWith: data)
    let object = unarchiver.decodeObject(forKey: key)
}

I've write an simple utility to save/load objects in local storage, used sample codes above. You might want to see this. https://github.com/DragonCherry/LocalStorage

DragonCherry
  • 742
  • 7
  • 10
2

This gives a great explanation for how to do this in Swift 5: https://www.hackingwithswift.com/example-code/system/how-to-save-user-settings-using-userdefaults

Summary:

To set a value:

let defaults = UserDefaults.standard
defaults.set("value", forKey: "key")

To get a String value:

let key = defaults.object(forKey: "StringKey") as? [String] ?? [String]()

To get integer value:

let key = defaults.integer(forKey: "IntegerKey")
jmoerdyk
  • 5,544
  • 7
  • 38
  • 49
Jeffrey Kozik
  • 201
  • 4
  • 8
0

NsUserDefaults saves only small variable sizes. If you want to save many objects you can use CoreData as a native solution, or I created a library that helps you save objects as easy as .save() function. It’s based on SQLite.

SundeedQLite

Check it out and tell me your comments

0

I found this answer and it enabled me to save data, but since Swift 4.1 there has been a much easier way to do this using appstorage.

@AppStorage("studentNames") var studentName: String = "Put name here"

Each item must be unique, but using String you can store a large variety of data in here.

I've made a video tutorial to help you do this: http://youtube.com/watch?v=nLsJD6yL9Ps

James Abela
  • 97
  • 1
  • 3