Local Data Persistence - UserDefaults (Basic)
Tags: swift, userdefaults, database, ios
Published: 2021-10-04 03:19:07

We need to consider data storing to persist user data in local device when we develop App. There are many solutions but today we are looking most simple solution by using UserDefaults Class.

__UserDefaults class is good and easy solution to store small data as user's setting (ex, audio volume setting, on-off of Face ID and so on)__

Let's see Apple documentation for definition of UserDefaults first.
> An interface to the user’s defaults database, where you store key-value pairs persistently across launches of your app. 

And another key sentence of Apple Document what kinds of data type UserDefaults supports.

>The UserDefaults class provides convenience methods for accessing common types such as *floats, doubles, integers, Boolean values, and URLs*. These methods are described in Setting Default Values.

and

>A default object must be a property list—that is, an instance of (or for collections, a combination of instances of) NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. If you want to store any other type of object, you should typically archive it to create an instance of NSData. (I will review this advanced topic next article)

__Usually we are using UserDefaults for small data with common data type as *floats, doubles, integers, Boolean values, and URLs.*__




##Let's Coding and Testing by Playground...

```swift
import UIKit

let defaults = UserDefaults.standard

let intValue = 10
let strValue = "Daniel Jang"
let arrValue = [1, 2, 3]
let dictValue = ["ZipCode": 98765]

// Save data TO Local device
defaults.set(intValue, forKey: "Volume")
defaults.set(strValue, forKey: "Name")
defaults.set(arrValue, forKey: "numArray")
defaults.set(dictValue, forKey: "Zipcode")

// Load data FROM Local device
let AudioVolume = defaults.integer(forKey: "Volume")
let personName = defaults.string(forKey: "Name")!
let myArray = defaults.array(forKey: "numArray")!
let townZipcode = defaults.dictionary(forKey: "Zipcode")!

print(AudioVolume)
print(personName)
print(myArray)
print(townZipcode)
```
>Result
10
Daniel Jang
[1, 2, 3]
["ZipCode": 98765]

You could see very simple logic to save and load data.
###Let's check some key code
1) let defaults = UserDefaults.standard


>The UserDefaults class provides a programmatic interface for interacting with the defaults system. The defaults system allows an app to customize its behavior to match a user’s preferences. For example, you can allow users to specify their preferred units of measurement or media playback speed. Apps store these preferences by assigning values to a set of parameters in a user’s defaults database. The parameters are referred to as defaults because they’re commonly used to determine an app’s default state at startup or the way it acts by default

2) defaults.set(value: forKey:)
  * defaults.set(value: Int, forKey: String)
  * defaults.set(value: Bool, forKey: String)
  * defaults.set(value: Double, forKey: String)
  * defaults.set(value: Any?, forKey: String)
  * And other common data types

> "set(value: forKey)" is a method to save user's data into local device. 
  * "value" parameter should be common data types and 
  * "forKey" parameter should be String as _Key Value_. _Key Value_ means kinds of name tag. we will use this _Key Value_ when we load user's data from local device.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a0t0i8cgo56fhqrgv853.png)

3) defaults.object(forKey: )
  * defaults.integer(forKey: )
  * defaults.bool(forKey: )
  * defaults.double(forKey: )
  * defaults.string(forKey: )
  * And other methods to load common data types

Methods for loading are also simple enough! There are 2 ways to load user's data from local device usually.
First...
>
  * We can use "defaults.integer(forKey:)" method to load Integer data or "defaults.string(forKey: )" method to load String data from local device and so on.
  * "forKey" parameter should be same String value as above "forKey" when you save user's data. (IMPORTANT!!!)


Second...
>
  * We can use "defaults.object(forKey:)" method for all stored user's data even Integer, String, Array, Dictionary etc with as typecasting. Then we could change above code for data loading as below. (Sorry please find other article about "as" typecasting. I focus on UserDefault now)
  * "forKey" parameter is same as first.

```swift
let AudioVolume = defaults.object(forKey: "Volume") as! Int
let personName = defaults.object(forKey: "Name") as! String
let myArray = defaults.object(forKey: "numArray") as! [Int]
let townZipcode = defaults.object(forKey: "Zipcode") as! [String: Int]
```

###By the way, where we can see stored user's data?
Please put below code end of your code editor then you will get a location path what your app is running in your device as below image.
```swift 
print(FileManager.default.urls(for: .documentDirectory, in: .userDomainMask))
```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d61b6o4jqjvurxb3tb8n.png)

After running your playground. I hope you could find ".plist" file then click the ".plist"file then you can see your data is stored as key-value data in ".plist" file as below image. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fcqug3btru4jtvnqaop9.png)


##What happen? if we try to save "custom class" instead common data types as below. You will get error message from Xcode immediately.
```swift 
let defaults = UserDefaults.standard

class Product {
    let name: String
    let modelNum: String
    
    init(name: String, modelNum: String){
        self.name = name
        self.modelNum = modelNum
    }
}

let appleWatch = Product(name: "Apple Watch 7", modelNum: "W214312")
defaults.set(appleWatch, forKey: "Apple")
```

##Then? Cannot use UserDefaults class to store custom class?
Yes we CAN use UserDefaults class to store custom class! Hint is above definition part of Apple Document.(by NSData Codable. We will review it next article) 

Thank you for your reading...
Daniel JANG.