ios - user defaults swift 4
Guardar Struct en UserDefaults (5)
Tengo una estructura que quiero guardar en UserDefaults. Aquí está mi estructura
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"),
]
En otro ViewController, tengo un UIButton que se agrega a esta estructura como
@IBAction func likeButtonPressed(_ sender: Any) {
songs.append(Song(title: songs[thisSong].title, artist: songs[thisSong].artist))
}
Lo quiero para que cada vez que el usuario haga clic en ese botón, guarde la estructura en UserDefaults, de modo que cada vez que el usuario salga de la aplicación y luego la abra de nuevo, se guardará. ¿Cómo haría esto?
Desde here:
Un objeto predeterminado debe ser una lista de propiedades, es decir, una instancia de (o para colecciones, una combinación de instancias de): NSData, NSString, NSNumber, NSDate, NSArray o NSDictionary. Si desea almacenar cualquier otro tipo de objeto, normalmente debe archivarlo para crear una instancia de NSData.
Necesitas usar NSKeydArchiver
. La documentación se puede encontrar here y ejemplos here y here .
En Swift 4 esto es bastante trivial. Haga que su estructura sea codificable simplemente marcándola como adoptando el protocolo Codable:
struct Song:Codable {
var title: String
var artist: String
}
Ahora vamos a empezar con algunos datos:
var songs: [Song] = [
Song(title: "Title 1", artist: "Artist 1"),
Song(title: "Title 2", artist: "Artist 2"),
Song(title: "Title 3", artist: "Artist 3"),
]
Aquí es cómo conseguir que en UserDefaults:
UserDefaults.standard.set(try? PropertyListEncoder().encode(songs), forKey:"songs")
Y aquí está cómo volver a sacarlo más tarde:
if let data = UserDefaults.standard.value(forKey:"songs") as? Data {
let songs2 = try? PropertyListDecoder().decode(Array<Song>.self, from: data)
}
Esta es mi extensión UserDefaults , para establecer obtener el objeto Codable en 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)
}
}
Si la estructura solo contiene propiedades compatibles con la lista de propiedades, recomiendo agregar una propiedad propertyListRepresentation
y un método init
correspondiente
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]
}
}
Para guardar un conjunto de canciones en UserDefaults
write
let propertylistSongs = songs.map{ $0.propertyListRepresentation }
UserDefaults.standard.set(propertylistSongs, forKey: "songs")
Para leer la matriz
if let propertylistSongs = UserDefaults.standard.array(forKey: "songs") as? [[String:String]] {
songs = propertylistSongs.flatMap{ Song(dictionary: $0) }
}
Si el title
y el artist
nunca serán mutados, considere declarar las propiedades como constantes ( let
).
Esta respuesta se escribió mientras Swift 4 estaba en estado beta. Mientras tanto, conformarse con Codable
es la mejor solución.
Si solo está intentando guardar este conjunto de canciones en UserDefaults y no hay nada especial, use esto:
//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
Si está almacenando una matriz pesada, le sugiero que utilice el protocolo de codificación NSC o el protocolo codificable en swift 4
Ejemplo de protocolo de codificación: -
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")
}
}