Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 将结构保存为用户默认值_Ios_Swift_Struct_Nsuserdefaults - Fatal编程技术网

Ios 将结构保存为用户默认值

Ios 将结构保存为用户默认值,ios,swift,struct,nsuserdefaults,Ios,Swift,Struct,Nsuserdefaults,我有一个要保存为UserDefaults的结构。这是我的结构 struct Song { var title: String var artist: String } var songs: [Song] = [ Song(title: "Title 1", artist "Artist 1"), Song(title: "Title 2", artist "Artist 2"), Song(title: "Title 3", artist "Artist

我有一个要保存为UserDefaults的结构。这是我的结构

struct Song {
    var title: String
    var artist: String
}

var songs: [Song] = [
    Song(title: "Title 1", artist "Artist 1"),
    Song(title: "Title 2", artist "Artist 2"),
    Song(title: "Title 3", artist "Artist 3"),
]
在另一个ViewController中,我有一个UIButton,它像

@IBAction func likeButtonPressed(_ sender: Any) {   
   songs.append(Song(title: songs[thisSong].title, artist: songs[thisSong].artist))
}
我希望这样,每当用户单击该按钮时,它也会将结构保存为UserDefaults,这样每当用户退出应用程序并再次打开它时,它就会被保存。我该怎么做?

来自

默认对象必须是属性列表,即的实例(或集合的实例组合): 操作文件数据 , NSString , 数字对象 , NSDate , 不可变数组 或 字典 . 如果要存储任何其他类型的对象,通常应将其存档以创建NSData实例

您需要使用
NSKeydArchiver
。您可以从中找到文档、示例和。

默认对象必须是属性列表,即的实例(或集合的实例组合): 操作文件数据 , NSString , 数字对象 , NSDate , 不可变数组 或 字典 . 如果要存储任何其他类型的对象,通常应将其存档以创建NSData实例


您需要使用
NSKeydArchiver
。如果您只是尝试将此歌曲数组保存为用户默认值,并且没有什么特别之处,则可以找到文档、示例和。

:-

//stores the array to defaults
UserDefaults.standard.setValue(value: songs, forKey: "yourKey")

//retrieving the array

UserDefaults.standard.object(forKey: "yourKey") as! [Song]
//Make sure to typecast this as an array of Song
如果您正在存储大量数组,我建议您使用NSCoding协议或swift 4中的Codable协议

编码协议示例:-

 struct Song {
        var title: String
        var artist: String
    }

    class customClass: NSObject, NSCoding { //conform to nsobject and nscoding

    var songs: [Song] = [
        Song(title: "Title 1", artist "Artist 1"),
        Song(title: "Title 2", artist "Artist 2"),
        Song(title: "Title 3", artist "Artist 3"),
    ]

    override init(arr: [Song])
    self.songs = arr
    }

    required convenience init(coder aDecoder: NSCoder) {
    //decoding your array
    let songs = aDecoder.decodeObject(forKey: "yourKey") as! [Song]

    self.init(are: songs)
    }

    func encode(with aCoder: NSCoder) {
    //encoding
    aCoder.encode(songs, forKey: "yourKey")
    }

}

如果您只是尝试在UserDefaults中保存此歌曲数组,但没有什么特别之处,请使用以下命令:-

//stores the array to defaults
UserDefaults.standard.setValue(value: songs, forKey: "yourKey")

//retrieving the array

UserDefaults.standard.object(forKey: "yourKey") as! [Song]
//Make sure to typecast this as an array of Song
如果您正在存储大量数组,我建议您使用NSCoding协议或swift 4中的Codable协议

编码协议示例:-

 struct Song {
        var title: String
        var artist: String
    }

    class customClass: NSObject, NSCoding { //conform to nsobject and nscoding

    var songs: [Song] = [
        Song(title: "Title 1", artist "Artist 1"),
        Song(title: "Title 2", artist "Artist 2"),
        Song(title: "Title 3", artist "Artist 3"),
    ]

    override init(arr: [Song])
    self.songs = arr
    }

    required convenience init(coder aDecoder: NSCoder) {
    //decoding your array
    let songs = aDecoder.decodeObject(forKey: "yourKey") as! [Song]

    self.init(are: songs)
    }

    func encode(with aCoder: NSCoder) {
    //encoding
    aCoder.encode(songs, forKey: "yourKey")
    }

}

如果结构仅包含符合属性列表的属性,我建议添加属性
propertylisterepresentation
和相应的
init
方法

struct Song {

    var title: String
    var artist: String

    init(title : String, artist : String) {
        self.title = title
        self.artist = artist
    }

    init?(dictionary : [String:String]) {
        guard let title = dictionary["title"],
            let artist = dictionary["artist"] else { return nil }
        self.init(title: title, artist: artist)
    }

    var propertyListRepresentation : [String:String] {
        return ["title" : title, "artist" : artist]
    }
}
将歌曲数组保存到
UserDefaults
write

let propertylistSongs = songs.map{ $0.propertyListRepresentation }
UserDefaults.standard.set(propertylistSongs, forKey: "songs")
读取数组

if let propertylistSongs = UserDefaults.standard.array(forKey: "songs") as? [[String:String]] {
    songs = propertylistSongs.flatMap{ Song(dictionary: $0) }
}
<> > <代码>标题<代码>和<代码>艺术家<代码>永远不会发生变化,考虑将属性声明为常量(<代码>让/<代码>)。< /P>

这个答案是在Swift 4处于测试状态时写的。同时,符合
Codable
是更好的解决方案。

如果结构只包含符合属性列表的属性,我建议添加属性
propertylisterepresentation
和相应的
init
方法

struct Song {

    var title: String
    var artist: String

    init(title : String, artist : String) {
        self.title = title
        self.artist = artist
    }

    init?(dictionary : [String:String]) {
        guard let title = dictionary["title"],
            let artist = dictionary["artist"] else { return nil }
        self.init(title: title, artist: artist)
    }

    var propertyListRepresentation : [String:String] {
        return ["title" : title, "artist" : artist]
    }
}
将歌曲数组保存到
UserDefaults
write

let propertylistSongs = songs.map{ $0.propertyListRepresentation }
UserDefaults.standard.set(propertylistSongs, forKey: "songs")
读取数组

if let propertylistSongs = UserDefaults.standard.array(forKey: "songs") as? [[String:String]] {
    songs = propertylistSongs.flatMap{ Song(dictionary: $0) }
}
<> > <代码>标题<代码>和<代码>艺术家<代码>永远不会发生变化,考虑将属性声明为常量(<代码>让/<代码>)。< /P>

这个答案是在Swift 4处于测试状态时写的。同时,遵循
可编码的
是更好的解决方案。

在Swift 4中,这是非常简单的。只需将结构标记为采用可编码协议,即可使其可编码:

struct Song:Codable {
    var title: String
    var artist: String
}
现在让我们从一些数据开始:

var songs: [Song] = [
    Song(title: "Title 1", artist: "Artist 1"),
    Song(title: "Title 2", artist: "Artist 2"),
    Song(title: "Title 3", artist: "Artist 3"),
]
以下是如何将其转换为用户默认值:

UserDefaults.standard.set(try? PropertyListEncoder().encode(songs), forKey:"songs")
下面是如何在以后再次将其取出:

if let data = UserDefaults.standard.value(forKey:"songs") as? Data {
    let songs2 = try? PropertyListDecoder().decode(Array<Song>.self, from: data)
}
如果let data=UserDefaults.standard.value(forKey:“songs”)为?资料{
let songs2=try?PropertyListDecoder().decode(Array.self,from:data)
}

在Swift 4中,这几乎是微不足道的。只需将结构标记为采用可编码协议,即可使其可编码:

struct Song:Codable {
    var title: String
    var artist: String
}
现在让我们从一些数据开始:

var songs: [Song] = [
    Song(title: "Title 1", artist: "Artist 1"),
    Song(title: "Title 2", artist: "Artist 2"),
    Song(title: "Title 3", artist: "Artist 3"),
]
以下是如何将其转换为用户默认值:

UserDefaults.standard.set(try? PropertyListEncoder().encode(songs), forKey:"songs")
下面是如何在以后再次将其取出:

if let data = UserDefaults.standard.value(forKey:"songs") as? Data {
    let songs2 = try? PropertyListDecoder().decode(Array<Song>.self, from: data)
}
如果let data=UserDefaults.standard.value(forKey:“songs”)为?资料{
let songs2=try?PropertyListDecoder().decode(Array.self,from:data)
}

这是我在主线程中的UserDefaults扩展,用于将getCodable对象设置为UserDefaults

// MARK: - UserDefaults extensions

public extension UserDefaults {

    /// Set Codable object into UserDefaults
    ///
    /// - Parameters:
    ///   - object: Codable Object
    ///   - forKey: Key string
    /// - Throws: UserDefaults Error
    public func set<T: Codable>(object: T, forKey: String) throws {

        let jsonData = try JSONEncoder().encode(object)

        set(jsonData, forKey: forKey)
    }

    /// Get Codable object into UserDefaults
    ///
    /// - Parameters:
    ///   - object: Codable Object
    ///   - forKey: Key string
    /// - Throws: UserDefaults Error
    public func get<T: Codable>(objectType: T.Type, forKey: String) throws -> T? {

        guard let result = value(forKey: forKey) as? Data else {
            return nil
        }

        return try JSONDecoder().decode(objectType, from: result)
    }
}
// MARK: - JSONDecoder extensions

public extension JSONDecoder {

    /// Decode an object, decoded from a JSON object.
    ///
    /// - Parameter data: JSON object Data
    /// - Returns: Decodable object
    public func decode<T: Decodable>(from data: Data?) -> T? {
        guard let data = data else {
            return nil
        }
        return try? self.decode(T.self, from: data)
    }

    /// Decode an object in background thread, decoded from a JSON object.
    ///
    /// - Parameters:
    ///   - data: JSON object Data
    ///   - onDecode: Decodable object
    public func decodeInBackground<T: Decodable>(from data: Data?, onDecode: @escaping (T?) -> Void) {
        DispatchQueue.global().async {
            let decoded: T? = self.decode(from: data)

            DispatchQueue.main.async {
                onDecode(decoded)
            }
        }
    }
}

// MARK: - JSONEncoder extensions  

public extension JSONEncoder {

    /// Encodable an object
    ///
    /// - Parameter value: Encodable Object
    /// - Returns: Data encode or nil
    public func encode<T: Encodable>(from value: T?) -> Data? {
        guard let value = value else {
            return nil
        }
        return try? self.encode(value)
    }

    /// Encodable an object in background thread
    ///
    /// - Parameters:
    ///   - encodableObject: Encodable Object
    ///   - onEncode: Data encode or nil
    public func encodeInBackground<T: Encodable>(from encodableObject: T?, onEncode: @escaping (Data?) -> Void) {
        DispatchQueue.global().async {
            let encode = self.encode(from: encodableObject)

            DispatchQueue.main.async {
                onEncode(encode)
            }
        }
    }
}       

// MARK: - NSUserDefaults extensions

public extension UserDefaults {

    /// Set Encodable object in UserDefaults
    ///
    /// - Parameters:
    ///   - type: Encodable object type
    ///   - key: UserDefaults key
    /// - Throws: An error if any value throws an error during encoding.
    public func set<T: Encodable>(object type: T, for key: String, onEncode: @escaping (Bool) -> Void) throws {

        JSONEncoder().encodeInBackground(from: type) { [weak self] (data) in
            guard let data = data, let `self` = self else {
                onEncode(false)
                return
            }
            self.set(data, forKey: key)
            onEncode(true)
        }
    }

    /// Get Decodable object in UserDefaults
    ///
    /// - Parameters:
    ///   - objectType: Decodable object type
    ///   - forKey: UserDefaults key
    ///   - onDecode: Codable object
    public func get<T: Decodable>(object type: T.Type, for key: String, onDecode: @escaping (T?) -> Void) {
        let data = value(forKey: key) as? Data
        JSONDecoder().decodeInBackground(from: data, onDecode: onDecode)
    }
}
//标记:-UserDefaults扩展名
公共扩展用户默认值{
///将可编码对象设置为UserDefaults
///
///-参数:
///-对象:可编码对象
///-福克:钥匙串
///-Throws:UserDefaults错误
公共函数集(对象:T,forKey:String)抛出{
让jsonData=try JSONEncoder().encode(对象)
集合(jsonData,forKey:forKey)
}
///将可编码对象获取到UserDefaults中
///
///-参数:
///-对象:可编码对象
///-福克:钥匙串
///-Throws:UserDefaults错误
公共函数get(objectType:T.Type,forKey:String)抛出->T{
guard let结果=值(forKey:forKey)作为?数据其他{
归零
}
返回try JSONDecoder().decode(objectType,from:result)
}
}
更新这是我的后台用户默认扩展,用于将getCodable对象设置为UserDefaults

// MARK: - UserDefaults extensions

public extension UserDefaults {

    /// Set Codable object into UserDefaults
    ///
    /// - Parameters:
    ///   - object: Codable Object
    ///   - forKey: Key string
    /// - Throws: UserDefaults Error
    public func set<T: Codable>(object: T, forKey: String) throws {

        let jsonData = try JSONEncoder().encode(object)

        set(jsonData, forKey: forKey)
    }

    /// Get Codable object into UserDefaults
    ///
    /// - Parameters:
    ///   - object: Codable Object
    ///   - forKey: Key string
    /// - Throws: UserDefaults Error
    public func get<T: Codable>(objectType: T.Type, forKey: String) throws -> T? {

        guard let result = value(forKey: forKey) as? Data else {
            return nil
        }

        return try JSONDecoder().decode(objectType, from: result)
    }
}
// MARK: - JSONDecoder extensions

public extension JSONDecoder {

    /// Decode an object, decoded from a JSON object.
    ///
    /// - Parameter data: JSON object Data
    /// - Returns: Decodable object
    public func decode<T: Decodable>(from data: Data?) -> T? {
        guard let data = data else {
            return nil
        }
        return try? self.decode(T.self, from: data)
    }

    /// Decode an object in background thread, decoded from a JSON object.
    ///
    /// - Parameters:
    ///   - data: JSON object Data
    ///   - onDecode: Decodable object
    public func decodeInBackground<T: Decodable>(from data: Data?, onDecode: @escaping (T?) -> Void) {
        DispatchQueue.global().async {
            let decoded: T? = self.decode(from: data)

            DispatchQueue.main.async {
                onDecode(decoded)
            }
        }
    }
}

// MARK: - JSONEncoder extensions  

public extension JSONEncoder {

    /// Encodable an object
    ///
    /// - Parameter value: Encodable Object
    /// - Returns: Data encode or nil
    public func encode<T: Encodable>(from value: T?) -> Data? {
        guard let value = value else {
            return nil
        }
        return try? self.encode(value)
    }

    /// Encodable an object in background thread
    ///
    /// - Parameters:
    ///   - encodableObject: Encodable Object
    ///   - onEncode: Data encode or nil
    public func encodeInBackground<T: Encodable>(from encodableObject: T?, onEncode: @escaping (Data?) -> Void) {
        DispatchQueue.global().async {
            let encode = self.encode(from: encodableObject)

            DispatchQueue.main.async {
                onEncode(encode)
            }
        }
    }
}       

// MARK: - NSUserDefaults extensions

public extension UserDefaults {

    /// Set Encodable object in UserDefaults
    ///
    /// - Parameters:
    ///   - type: Encodable object type
    ///   - key: UserDefaults key
    /// - Throws: An error if any value throws an error during encoding.
    public func set<T: Encodable>(object type: T, for key: String, onEncode: @escaping (Bool) -> Void) throws {

        JSONEncoder().encodeInBackground(from: type) { [weak self] (data) in
            guard let data = data, let `self` = self else {
                onEncode(false)
                return
            }
            self.set(data, forKey: key)
            onEncode(true)
        }
    }

    /// Get Decodable object in UserDefaults
    ///
    /// - Parameters:
    ///   - objectType: Decodable object type
    ///   - forKey: UserDefaults key
    ///   - onDecode: Codable object
    public func get<T: Decodable>(object type: T.Type, for key: String, onDecode: @escaping (T?) -> Void) {
        let data = value(forKey: key) as? Data
        JSONDecoder().decodeInBackground(from: data, onDecode: onDecode)
    }
}
//标记:-JSONDecoder扩展
公共扩展JSONDecoder{
///解码一个对象,从JSON对象解码。
///
///-参数数据:JSON对象数据
///-返回:可解码对象
公共函数解码(从数据:数据?->T{
guard let data=其他数据{
归零
}
返回try?self.decode(T.self,from:data)
}
///解码后台线程中的对象,从JSON对象解码。
///
///-参数:
///-data:JSON对象数据
///-onDecode:可解码对象
公共函数解码背景(来自数据:data?,onDecode:@escaping(T?)->Void){
DispatchQueue.global().async{
let decoded:T?=self.decode(from:data)
DispatchQueue.main.async{
编码(已解码)
}
}
}
}
//MARK:-JSONECODER扩展
公共扩展编码器{
///可编码的对象
///
///-参数值:可编码对象
///-返回:数据编码或零
公共函数编码(从值:T?)->