簡體   English   中英

從 UserDefaults 快速保存和檢索自定義對象

[英]Swift saving and retrieving custom object from UserDefaults

我在 Playground 中使用 Swift 3,Xcode 8.0:

import Foundation
class Person: NSObject, NSCoding {
    var name: String
    var age: Int
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    required convenience init(coder aDecoder: NSCoder) {
        let name = aDecoder.decodeObject(forKey: "name") as! String
        let age = aDecoder.decodeObject(forKey: "age") as! Int
        self.init(
            name: name,
            age: age
        )
    }
    func encode(with aCoder: NSCoder) {
        aCoder.encode(name, forKey: "name")
        aCoder.encode(age, forKey: "age")
    }
}

斯威夫特 4 或更高版本

您可以再次在 Playground 中保存/測試您的值


UserDefaults 需要在實際項目中進行測試。 注意:無需強制同步。 如果你想在 Playground 中測試編碼/解碼,你可以使用 keyed archiver 將數據保存到文檔目錄中的 plist 文件中。 您還需要解決課堂上的一些問題:


class Person: NSObject, NSCoding {
    let name: String
    let age: Int
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    required init(coder decoder: NSCoder) {
        self.name = decoder.decodeObject(forKey: "name") as? String ?? ""
        self.age = decoder.decodeInteger(forKey: "age")
    }
    func encode(with coder: NSCoder) {
        coder.encode(name, forKey: "name")
        coder.encode(age, forKey: "age")
    }
}

測試:

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        do {
            // setting a value for a key
            let newPerson = Person(name: "Joe", age: 10)
            var people = [Person]()
            people.append(newPerson)
            let encodedData = try NSKeyedArchiver.archivedData(withRootObject: people, requiringSecureCoding: false)
            UserDefaults.standard.set(encodedData, forKey: "people")
            // retrieving a value for a key
            if let data = UserDefaults.standard.data(forKey: "people"),
                let myPeopleList = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? [Person] {
                myPeopleList.forEach({print($0.name, $0.age)})  // Joe 10
            }                    
        } catch {
            print(error)
        }
        
    }
}
let age = aDecoder.decodeObject(forKey: "age") as! Int

Swift 3 已更改; 這不再適用於值類型。 現在正確的語法是:

let age = aDecoder.decodeInteger(forKey: "age")

有各種不同類型的相關 decode...() 函數:

let myBool = aDecoder.decodeBoolean(forKey: "myStoredBool")
let myFloat = aDecoder.decodeFloat(forKey: "myStoredFloat")

編輯: Swift 3 中所有可能的 decodeXXX 函數的完整列表

編輯:

另一個重要注意事項:如果您之前保存了使用舊版本 Swift 編碼的數據,則必須使用 decodeObject() 解碼這些值,但是一旦使用 encode(...) 重新編碼數據,它就不能再被如果它是值類型,則使用 decodeObject() 解碼。 因此,Markus Wyss 的回答將允許您處理使用任一 Swift 版本對數據進行編碼的情況:

self.age = aDecoder.decodeObject(forKey: "age") as? Int ?? aDecoder.decodeInteger(forKey: "age")

在 Swift 4 中:

您可以使用 Codable 從 Userdefaults 中保存和檢索自定義對象。 如果您經常這樣做,那么您可以添加為擴展名並像下面一樣使用它。

extension UserDefaults {

   func save<T:Encodable>(customObject object: T, inKey key: String) {
       let encoder = JSONEncoder()
       if let encoded = try? encoder.encode(object) {
           self.set(encoded, forKey: key)
       }
   }

   func retrieve<T:Decodable>(object type:T.Type, fromKey key: String) -> T? {
       if let data = self.data(forKey: key) {
           let decoder = JSONDecoder()
           if let object = try? decoder.decode(type, from: data) {
               return object
           }else {
               print("Couldnt decode object")
               return nil
           }
       }else {
           print("Couldnt find key")
           return nil
       }
   }

}

你的班級必須遵循 Codable。 它只是可編碼和可解碼協議的類型別名。

class UpdateProfile: Codable {
  //Your stuffs
}

用法:

let updateProfile = UpdateProfile()

//To save the object
UserDefaults.standard.save(customObject: updateProfile, inKey: "YourKey")

//To retrieve the saved object
let obj = UserDefaults.standard.retrieve(object: UpdateProfile.self, fromKey: "YourKey")

有關更多編碼和解碼自定義類型,請查看Apple 的文檔

試試這個:

self.age = aDecoder.decodeObject(forKey: "age") as? Int ?? aDecoder.decodeInteger(forKey: "age")

在 Swift 5 中,我會使用屬性包裝器來簡化代碼:

/// A type that adds an interface to use the user’s defaults with codable types
///
/// Example:
/// ```
/// @UserDefaultCodable(key: "nameKey", defaultValue: "Root") var name: String
/// ```
/// Adding the attribute @UserDefaultCodable the property works reading and writing from user's defaults
/// with any codable type
///
@propertyWrapper public struct UserDefaultCodable<T: Codable> {
    private let key: String
    private let defaultValue: T

    /// Initialize the key and the default value.
    public init(key: String, defaultValue: T) {
        self.key = key
        self.defaultValue = defaultValue
    }

    public var wrappedValue: T {
        get {
            // Read value from UserDefaults
            guard let data = UserDefaults.standard.object(forKey: key) as? Data else {
                // Return defaultValue when no data in UserDefaults
                return defaultValue
            }

            // Convert data to the desire data type
            let value = try? JSONDecoder().decode(T.self, from: data)
            return value ?? defaultValue
        }
        set {
            // Convert newValue to data
            let data = try? JSONEncoder().encode(newValue)

            // Set value to UserDefaults
            UserDefaults.standard.set(data, forKey: key)
        }
    }
}

在UserDefaults中保存“自定義對象”的簡單示例如下:

您無需借助THE GREAT'CODABLE'編寫用於保存/檢索對象的樣板代碼,這就是您擺脫煩人的手動編碼/解碼的目的

因此,如果您已經在使用NSCoding並切換到Codable(Encodable + Decodable的組合)協議,請從代碼中刪除以下兩種方法

required init(coder decoder: NSCoder) // NOT REQUIRED ANY MORE, DECODABLE TAKES CARE OF IT

func encode(with coder: NSCoder) // NOT REQUIRED ANY MORE, ENCODABLE TAKES CARE OF IT

讓我們從Codable的簡單性入手...

創建要存儲在UserDefaults中的自定義結構

struct Person : Codable {
    var name:String
}

OR

class Person : Codable {

    var name:String

    init(name:String) {
        self.name = name
    }
}

將對象保存在UserDefaults中,如下所示

 if let encoded = try? JSONEncoder().encode(Person(name: "Dhaval")) {
     UserDefaults.standard.set(encoded, forKey: "kSavedPerson")
 }

從UserDefaults加載對象,如下所示

guard let savedPersonData = UserDefaults.standard.object(forKey: "kSavedPerson") as? Data else { return }
guard let savedPerson = try? JSONDecoder().decode(Person.self, from: savedPersonData) else { return }

print("\n Saved person name : \(savedPerson.name)")

而已 ...

保存/加載愉快.. :)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM