Сложный пользовательский объект хранится в NSUserDefaults. Кодируемое решение Swift 4 - PullRequest
0 голосов
/ 02 июля 2018

Я некоторое время читал и искал, как сохранить пользовательский объект в "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)

Эта реализация работает только для String var, но вылетает, когда я пытаюсь сделать это с моим secondCustomClass ...

Я могу представить, что это потому, что «MySecondCustomClass» не реализует «NSCoding». это верно? Есть ли другой способ достичь того, что я пытаюсь сделать? Мой класс Custom имеет структуру, большую, чем та, которую я здесь показываю, поэтому, прежде чем я приступлю к программированию или продумыванию другой альтернативы, мне нужно знать.

Большое спасибо.

Ответы [ 2 ]

0 голосов
/ 17 октября 2018

Я собираюсь привести рабочий пример с пользовательским помощником, который я реализовал для управления Path для различных объектов 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)")
    }

}

И My StorageHelper:

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!")
        }

    }

}
0 голосов
/ 17 июля 2018

Вместо этого используйте Codable, здесь действительно хороший пост о том, как его использовать

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...