###### tags: `第14屆IT邦鐵人賽文章` # 【在 iOS 開發路上的大小事2-Day04】App 內的簡易儲存方式,UserDefaults 在 iOS App 裡,Apple 提供了兩種原生持久化儲存資料的方式 一種是今天要講的 UserDefaults、一種是 CoreData CoreData 是屬於資料庫方面的資料儲存解決方案,不是今天的主角,所以就先不討論 UserDefaults 是一種 Key-Value 的簡易、持久性儲存資料的方式 提供了 String、Array、Dictionary、Data、String Array、Int、Float、Double、Bool、URL 等儲存格式 ```swift // 取自 Xcode 內的 Foundation/NSUserDefaults/UserDefaults open func string(forKey defaultName: String) -> String? open func array(forKey defaultName: String) -> [Any]? open func dictionary(forKey defaultName: String) -> [String : Any]? open func data(forKey defaultName: String) -> Data? open func stringArray(forKey defaultName: String) -> [String]? open func integer(forKey defaultName: String) -> Int open func float(forKey defaultName: String) -> Float open func double(forKey defaultName: String) -> Double open func bool(forKey defaultName: String) -> Bool @available(iOS 4.0, *) open func url(forKey defaultName: String) -> URL? ``` 所以就來將 UserDefaults 寫成一個 class 來管理吧 ```swift class UserPreference { static let shared = UserPreference() private let userPreference: UserDefaults private init() { userPreference = UserDefaults.standard } } ``` UserDefaults 的給值、讀值方式如下~ ```swift // 給值 // 你要給這個值的 Key,要用 String 來表示 userPreference.set(你要給的值, forKey: 你要給這個值的 Key) // 讀值 // 根據你要給的值的型態來決定 // Int userPreference.integer(forKey: 你要讀的那個值的 Key) // String userPreference.string(forKey: 你要讀的那個值的 Key) // Float userPreference.float(forKey: 你要讀的那個值的 Key) // Double userPreference.double(forKey: 你要讀的那個值的 Key) // Data userPreference.data(forKey: 你要讀的那個值的 Key) // String Array userPreference.stringArray(forKey: 你要讀的那個值的 Key) // Bool userPreference.bool(forKey: 你要讀的那個值的 Key) // Dictionary userPreference.dictionary(forKey: 你要讀的那個值的 Key) // Array userPreference.array(forKey: 你要讀的那個值的 Key) // URL userPreference.url(forKey: 你要讀的那個值的 Key) ``` 然後 Key 的部分,我們可以寫一個 enum 來管理,像是下面這樣 ```swift enum UserPreference: String { case email // 使用者的 Email case username // 使用者的名字 case age // 使用者的年齡 case height // 使用者的身高 case weight // 使用者的體重 case isActivate // 使用者的帳號啟用狀態 } ``` 然後在讀值、給值方面,可以像下面這樣寫,透過 get/set 的方式來完成 ```swift var email: String { get { return userPreference.string(forKey: UserPreference.email.rawValue) ?? "" } set { return userPreference.set(newValue, forKey: UserPreference.email.rawValue) } } var username: String { get { return userPreference.string(forKey: UserPreference.username.rawValue) ?? "" } set { return userPreference.set(newValue, forKey: UserPreference.username.rawValue) } } var age: Int { get { return userPreference.integer(forKey: UserPreference.age.rawValue) } set { return userPreference.set(newValue, forKey: UserPreference.age.rawValue) } } var height: Double { get { return userPreference.double(forKey: UserPreference.height.rawValue) } set { return userPreference.set(newValue, forKey: UserPreference.height.rawValue) } } var weight: Double { get { return userPreference.double(forKey: UserPreference.weight.rawValue) } set { return userPreference.set(newValue, forKey: UserPreference.weight.rawValue) } } var isActivate: Bool { get { return userPreference.bool(forKey: UserPreference.isActivate.rawValue) } set { return userPreference.set(newValue, forKey: UserPreference.isActivate.rawValue) } } ``` 所以整個 class 就會長得像是下面這樣~ ```swift class UserPreference { static let shared = UserPreference() private let userPreference: UserDefaults private init() { userPreference = UserDefaults.standard } enum UserPreference: String { case email // 使用者的 Email case username // 使用者的名字 case age // 使用者的年齡 case height // 使用者的身高 case weight // 使用者的體重 case isActivate // 使用者的帳號啟用狀態 } var email: String { get { return userPreference.string(forKey: UserPreference.email.rawValue) ?? "" } set { return userPreference.set(newValue, forKey: UserPreference.email.rawValue) } } var username: String { get { return userPreference.string(forKey: UserPreference.username.rawValue) ?? "" } set { return userPreference.set(newValue, forKey: UserPreference.username.rawValue) } } var age: Int { get { return userPreference.integer(forKey: UserPreference.age.rawValue) } set { return userPreference.set(newValue, forKey: UserPreference.age.rawValue) } } var height: Double { get { return userPreference.double(forKey: UserPreference.height.rawValue) } set { return userPreference.set(newValue, forKey: UserPreference.height.rawValue) } } var weight: Double { get { return userPreference.double(forKey: UserPreference.weight.rawValue) } set { return userPreference.set(newValue, forKey: UserPreference.weight.rawValue) } } var isActivate: Bool { get { return userPreference.bool(forKey: UserPreference.isActivate.rawValue) } set { return userPreference.set(newValue, forKey: UserPreference.isActivate.rawValue) } } } ``` 在其他檔案呼叫的時候就可以像下面這樣來呼叫~ ```swift var email: String = UserPreference.shared.email var username: String = UserPreference.shared.username var age: Int = UserPreference.shared.age var height: Double = UserPreference.shared.height var weight: Double = UserPreference.shared.weight var isActivate: Bool = UserPreference.shared.isActivate ``` 這樣 Code 看起來就會清晰很多了~ 這樣也同時解決去年實作 Firebase Auth 登入,重開 App 沒辦法紀錄登入狀態的問題~ ## 參考資料 > 1. https://developer.apple.com/documentation/foundation/userdefaults > 2. https://codertw.com/%E7%A8%8B%E5%BC%8F%E8%AA%9E%E8%A8%80/741066/#outline__1_1 > 3. https://www.jianshu.com/p/3796886b4953