存储在 NSUserDefaults 中的复杂自定义对象.可编码解决方案斯威夫特 4.



我一直在阅读和搜索一段时间,了解如何将自定义对象存储到"NSUserDefaults"中。到目前为止,我已经有了解决方案,允许我通过在自定义对象中实现"NSCoding"来实现这一点。我得到的示例基于非常简单的对象,但就我而言,我面临着对具有复杂结构的现有自定义类以及其中包含其他自定义类的挑战。

class MyCustomClass:NSObject, NSCoding{
let codingTagSecondClass = "codingTagSecondClass"
var mySecondClass:MySecondCustomClass?
...
let codeingTagaString = "codeingTagaString"
var aString = "aString"
}

我已经实现了NSCoding方法,例如:

required init?(coder aDecoder: NSCoder) {
aString = aDecoder.decodeObject(forKey: codeingTagaString) as! String
mySecondClass = aDecoder.decodeObject(forKey: codingTagSecondClass) as? mySecondClass:MySecondCustomClass
}
func encode(with aCoder: NSCoder) {
aCoder.encode(aString, forKey: codeingTagaString)
aCoder.encode(mySecondClass, forKey: codingTagSecondClass)
}

这就是我存储到 NSUserDefaults 中的方式

let archivedObject = NSKeyedArchiver.archivedData(withRootObject: myCustomObject!)
let defaults = UserDefaults.standard
defaults.set(archivedObject, forKey: defaultUserCurrentServerProxy)

此实现仅适用于字符串变量,但是当我尝试使用我的第二个自定义类执行此操作时它会崩溃...

我可以想象这是因为"MySecondCustomClass"没有实现"NSCoding"。 对吗?有没有其他方法来实现我想要做的事情?我的自定义类的结构比我在这里显示的结构大,所以在我进入编码或思考我需要知道的不同替代方案之前。

谢谢。

改用Codable,这里有一篇关于如何使用它的非常好的帖子

我将给出一个工作示例,其中包含我为管理不同可编码对象的路径而实现的自定义帮助程序

My Class:private class StoredMediaItem:Codable{
var metadata: String?
var url: URL?
var trackID:UInt32 = 0
init(metadata:String?, url:URL?, trackID:UInt32){
self.metadata = metadata
self.url = url
self.trackID = trackID
}
}

阅读:

func readStoredItemArray(fileName:String)->[StoredMediaItem]?{
do {
let storedMediaItemArray = try StorageHelper.retrieve(fileName, from: .caches, as: [StoredMediaItem].self)
print("(logClassNameOH) Read Stored Item Array from (fileName) SUCCESS")
return storedMediaItemArray
} catch {
print("(logClassNameOH) Read Stored Item Array from(fileName) ERROR -> (error)")
return nil
}
}

对于写作:

private func saveMediaItemArray(_ mediaItemArrayTemp:[storedMediaItemArray], as fileName:String){
do {
try StorageHelper.store(storedMediaItemArray, to: .caches, as: fileName)
print("(logClassNameOH) Read Stored Item Array from (fileName) SUCCESS")
} catch {
print("(logClassNameOH) save MediaItem Array ERROR -> (error)")
}
}

和我的存储助手:

class StorageHelper{
//MARK: - Variables
enum StorageHelperError:Error{
case error(_ message:String)
}
enum Directory {
// Only documents and other data that is user-generated, or that cannot otherwise be recreated by your application, should be stored in the <Application_Home>/Documents directory and will be automatically backed up by iCloud.
case documents
// Data that can be downloaded again or regenerated should be stored in the <Application_Home>/Library/Caches directory. Examples of files you should put in the Caches directory include database cache files and downloadable content, such as that used by magazine, newspaper, and map applications.
case caches
}

//MARK: - Functions
/** Store an encodable struct to the specified directory on disk
*  @param object      The encodable struct to store
*  @param directory   Where to store the struct
*  @param fileName    What to name the file where the struct data will be stored
**/
static func store<T: Encodable>(_ object: T, to directory: Directory, as fileName: String) throws {
let url = getURL(for: directory).appendingPathComponent(fileName, isDirectory: false)
let encoder = JSONEncoder()
do {
let data = try encoder.encode(object)
if FileManager.default.fileExists(atPath: url.path) {
try FileManager.default.removeItem(at: url)
}
FileManager.default.createFile(atPath: url.path, contents: data, attributes: nil)
}
catch {
throw(error)
}
}
/** Retrieve and convert an Object from a file on disk
*  @param fileName    Name of the file where struct data is stored
*  @param directory   Directory where Object data is stored
*  @param type        Object type (i.e. Message.self)
*  @return decoded    Object model(s) of data
**/
static func retrieve<T: Decodable>(_ fileName: String, from directory: Directory, as type: T.Type) throws -> T{
let url = getURL(for: directory).appendingPathComponent(fileName, isDirectory: false)
if !FileManager.default.fileExists(atPath: url.path) {
throw StorageHelperError.error("No data at location: (url.path)")
}
if let data = FileManager.default.contents(atPath: url.path) {
let decoder = JSONDecoder()
do {
let model = try decoder.decode(type, from: data)
return model
} catch {
throw(error)
}
}
else {
throw StorageHelperError.error("No data at location: (url.path)")
}
}
/** Remove all files at specified directory **/
static func clear(_ directory: Directory) throws {
let url = getURL(for: directory)
do {
let contents = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil, options: [])
for fileUrl in contents {
try FileManager.default.removeItem(at: fileUrl)
}
}
catch {
throw(error)
}
}
/** Remove specified file from specified directory **/
static func remove(_ fileName: String, from directory: Directory) throws {
let url = getURL(for: directory).appendingPathComponent(fileName, isDirectory: false)
if FileManager.default.fileExists(atPath: url.path) {
do {
try FileManager.default.removeItem(at: url)
} catch {
throw(error)
}
}
}

//MARK: Helpers
/** Returns BOOL indicating whether file exists at specified directory with specified file name **/
static fileprivate func fileExists(_ fileName: String, in directory: Directory) -> Bool {
let url = getURL(for: directory).appendingPathComponent(fileName, isDirectory: false)
return FileManager.default.fileExists(atPath: url.path)
}
/** Returns URL constructed from specified directory **/
static fileprivate func getURL(for directory: Directory) -> URL {
var searchPathDirectory: FileManager.SearchPathDirectory
switch directory {
case .documents:
searchPathDirectory = .documentDirectory
case .caches:
searchPathDirectory = .cachesDirectory
}
if let url = FileManager.default.urls(for: searchPathDirectory, in: .userDomainMask).first {
return url
} else {
fatalError("Could not create URL for specified directory!")
}
}
}

最新更新