Ich versuche derzeit, eine benutzerdefinierte Swift-Klasse in NSUserDefaults zu speichern. Hier ist der Code von meinem Spielplatz:
import Foundation
class Blog : NSObject, NSCoding {
var blogName: String?
override init() {}
required init(coder aDecoder: NSCoder) {
if let blogName = aDecoder.decodeObjectForKey("blogName") as? String {
self.blogName = blogName
}
}
func encodeWithCoder(aCoder: NSCoder) {
if let blogName = self.blogName {
aCoder.encodeObject(blogName, forKey: "blogName")
}
}
}
var blog = Blog()
blog.blogName = "My Blog"
let ud = NSUserDefaults.standardUserDefaults()
ud.setObject(blog, forKey: "blog")
Wenn ich den Code ausführe, wird der folgende Fehler angezeigt
Die Ausführung wurde unterbrochen, Grund: Signal SIGABRT.
in der letzten Zeile ( ud.setObject
...)
Der gleiche Code stürzt auch in einer App mit der Nachricht ab
"Eigenschaftsliste für Format ungültig: 200 (Eigenschaftslisten dürfen keine Objekte vom Typ 'CFType' enthalten)"
Kann jemand helfen? Ich verwende Xcode 6.0.1 auf Maverick. Vielen Dank.
swift
nsuserdefaults
sigabrt
nscoding
Georg
quelle
quelle
Antworten:
Verwenden Sie in Swift 4 oder höher Codable.
Verwenden Sie in Ihrem Fall den folgenden Code.
class Blog: Codable { var blogName: String? }
Erstellen Sie nun das Objekt. Zum Beispiel:
var blog = Blog() blog.blogName = "My Blog"
Codiere es jetzt so:
if let encoded = try? JSONEncoder().encode(blog) { UserDefaults.standard.set(encoded, forKey: "blog") }
und dekodiere es so:
if let blogData = UserDefaults.standard.data(forKey: "blog"), let blog = try? JSONDecoder().decode(Blog.self, from: blogData) { }
quelle
Das erste Problem ist, dass Sie sicherstellen müssen, dass Sie einen nicht verstümmelten Klassennamen haben:
@objc(Blog) class Blog : NSObject, NSCoding {
Dann müssen Sie das Objekt (in eine NSData) codieren, bevor Sie es in den Benutzerstandards speichern können:
ud.setObject(NSKeyedArchiver.archivedDataWithRootObject(blog), forKey: "blog")
Um das Objekt wiederherzustellen, müssen Sie es ebenfalls entarchivieren:
if let data = ud.objectForKey("blog") as? NSData { let unarc = NSKeyedUnarchiver(forReadingWithData: data) unarc.setClass(Blog.self, forClassName: "Blog") let blog = unarc.decodeObjectForKey("root") }
Beachten Sie, dass es etwas einfacher ist, wenn Sie es nicht auf dem Spielplatz verwenden, da Sie die Klasse nicht manuell registrieren müssen:
if let data = ud.objectForKey("blog") as? NSData { let blog = NSKeyedUnarchiver.unarchiveObjectWithData(data) }
quelle
setClass
wenn Sie auf dem Spielplatz arbeiten. Aus verschiedenen Gründen werden Klassen dort nicht automatisch registriert.blog
obige Variable ist nicht das benutzerdefinierte Objekt inif let data = ud.objectForKey("blog") as? NSData { let blog = NSKeyedUnarchiver.unarchiveObjectWithData(data) }
. In meinem Fall muss ich sie wie folgt in mein benutzerdefiniertes Objektif let data = ud.objectForKey("blog") as? NSData { let blog = NSKeyedUnarchiver.unarchiveObjectWithData(data) if let thisblog = blog as? Blog { return thisblog //this is the custom object from nsuserdefaults } }
Wie @ dan-beaulieu vorgeschlagen hat, beantworte ich meine eigene Frage:
Hier ist jetzt der Arbeitscode:
Hinweis: Das Entwirren des Klassennamens war nicht erforderlich, damit der Code auf Spielplätzen funktioniert.
import Foundation class Blog : NSObject, NSCoding { var blogName: String? override init() {} required init(coder aDecoder: NSCoder) { if let blogName = aDecoder.decodeObjectForKey("blogName") as? String { self.blogName = blogName } } func encodeWithCoder(aCoder: NSCoder) { if let blogName = self.blogName { aCoder.encodeObject(blogName, forKey: "blogName") } } } let ud = NSUserDefaults.standardUserDefaults() var blog = Blog() blog.blogName = "My Blog" ud.setObject(NSKeyedArchiver.archivedDataWithRootObject(blog), forKey: "blog") if let data = ud.objectForKey("blog") as? NSData { let unarc = NSKeyedUnarchiver(forReadingWithData: data) let newBlog = unarc.decodeObjectForKey("root") as Blog }
quelle
Hier ist eine Komplettlösung für Swift 4 & 5 .
Implementieren Sie zunächst Hilfsmethoden in
UserDefaults
Erweiterung:extension UserDefaults { func set<T: Encodable>(encodable: T, forKey key: String) { if let data = try? JSONEncoder().encode(encodable) { set(data, forKey: key) } } func value<T: Decodable>(_ type: T.Type, forKey key: String) -> T? { if let data = object(forKey: key) as? Data, let value = try? JSONDecoder().decode(type, from: data) { return value } return nil } }
Angenommen, wir möchten ein benutzerdefiniertes Objekt
Dummy
mit 2 Standardfeldern speichern und laden .Dummy
muss entsprechenCodable
:struct Dummy: Codable { let value1 = "V1" let value2 = "V2" } // Save UserDefaults.standard.set(encodable: Dummy(), forKey: "K1") // Load let dummy = UserDefaults.standard.value(Dummy.self, forKey: "K1")
quelle
Getestet mit Swift 2.1 & Xcode 7.1.1
Wenn Sie nicht benötigen, dass blogName optional ist (was ich glaube nicht), würde ich eine etwas andere Implementierung empfehlen:
class Blog : NSObject, NSCoding { var blogName: String // designated initializer // // ensures you'll never create a Blog object without giving it a name // unless you would need that for some reason? // // also : I would not override the init method of NSObject init(blogName: String) { self.blogName = blogName super.init() // call NSObject's init method } func encodeWithCoder(aCoder: NSCoder) { aCoder.encodeObject(blogName, forKey: "blogName") } required convenience init?(coder aDecoder: NSCoder) { // decoding could fail, for example when no Blog was saved before calling decode guard let unarchivedBlogName = aDecoder.decodeObjectForKey("blogName") as? String else { // option 1 : return an default Blog self.init(blogName: "unnamed") return // option 2 : return nil, and handle the error at higher level } // convenience init must call the designated init self.init(blogName: unarchivedBlogName) } }
Testcode könnte folgendermaßen aussehen:
let blog = Blog(blogName: "My Blog") // save let ud = NSUserDefaults.standardUserDefaults() ud.setObject(NSKeyedArchiver.archivedDataWithRootObject(blog), forKey: "blog") ud.synchronize() // restore guard let decodedNSData = ud.objectForKey("blog") as? NSData, let someBlog = NSKeyedUnarchiver.unarchiveObjectWithData(decodedNSData) as? Blog else { print("Failed") return } print("loaded blog with name : \(someBlog.blogName)")
Abschließend möchte ich darauf hinweisen, dass es einfacher wäre, NSKeyedArchiver zu verwenden und Ihr Array von benutzerdefinierten Objekten direkt in einer Datei zu speichern, anstatt NSUserDefaults zu verwenden. Mehr über ihre Unterschiede finden Sie in meiner Antwort hier .
quelle
In Swift 4 haben Sie ein neues Protokoll, das das NSCoding-Protokoll ersetzt. Es heißt
Codable
und unterstützt Klassen und Swift-Typen! (Aufzählung, Strukturen):struct CustomStruct: Codable { let name: String let isActive: Bool }
quelle
'Attempt to insert non-property list object
Codable
ist nicht zu ersetzenNSCoding
. Die einzige Möglichkeit, Objekte direkt zu speichern,UserDefaults
besteht darin, die KlasseNSCoding
kompatibel zu machen . DaNSCoding
es sich um a handeltclass protocol
, kann es nicht auf Struktur- / Aufzählungstypen angewendet werden. Sie können Ihre Struktur / Aufzählung jedoch weiterhinCoding
kompatibel machen undJSONSerializer
zum Codieren verwendenData
, in dem sie gespeichert werden kannUserDefaults
.Swift 3 Version:
class CustomClass: NSObject, NSCoding { let name: String let isActive: Bool init(name: String, isActive: Bool) { self.name = name self.isActive = isActive } // MARK: NSCoding required convenience init?(coder decoder: NSCoder) { guard let name = decoder.decodeObject(forKey: "name") as? String, let isActive = decoder.decodeObject(forKey: "isActive") as? Bool else { return nil } self.init(name: name, isActive: isActive) } func encode(with coder: NSCoder) { coder.encode(self.name, forKey: "name") coder.encode(self.isActive, forKey: "isActive") } }
quelle
Ich benutze meine eigene Struktur. Es ist viel einfacher.
struct UserDefaults { private static let kUserInfo = "kUserInformation" var UserInformation: DataUserInformation? { get { guard let user = NSUserDefaults.standardUserDefaults().objectForKey(UserDefaults.kUserInfo) as? DataUserInformation else { return nil } return user } set { NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: UserDefaults.kUserInfo) } } }
Benutzen:
let userinfo = UserDefaults.UserInformation
quelle
Sie müssen die Verlaufsmodellklasse einfach so auf NSCoding umstellen
Swift 4 & 5.
class SSGIFHistoryModel: NSObject, NSCoding { var bg_image: String var total_like: String var total_view: String let gifID: String var title: String init(bg_image: String, total_like: String, total_view: String, gifID: String, title: String) { self.bg_image = bg_image self.total_like = total_like self.total_view = total_view self.gifID = gifID self.title = title } required init?(coder aDecoder: NSCoder) { self.bg_image = aDecoder.decodeObject(forKey: "bg_image") as? String ?? "" self.total_like = aDecoder.decodeObject(forKey: "total_like") as? String ?? "" self.total_view = aDecoder.decodeObject(forKey: "total_view") as? String ?? "" self.gifID = aDecoder.decodeObject(forKey: "gifID") as? String ?? "" self.title = aDecoder.decodeObject(forKey: "title") as? String ?? "" } func encode(with aCoder: NSCoder) { aCoder.encode(bg_image, forKey: "bg_image") aCoder.encode(total_like, forKey: "total_like") aCoder.encode(total_view, forKey: "total_view") aCoder.encode(gifID, forKey: "gifID") aCoder.encode(title, forKey: "title") } }
Danach müssen Sie das Objekt in NSData archivieren, dann in den Benutzerstandards speichern und aus den Benutzerstandards abrufen und die Archivierung wieder aufheben. Sie können es so archivieren und entarchivieren
func saveGIFHistory() { var GIFHistoryArray: [SSGIFHistoryModel] = [] GIFHistoryArray.append(SSGIFHistoryModel(bg_image: imageData.bg_image, total_like: imageData.total_like, total_view: imageData.total_view, gifID: imageData.quote_id, title: imageData.title)) if let placesData = UserDefaults.standard.object(forKey: "GIFHistory") as? NSData { if let placesArray = NSKeyedUnarchiver.unarchiveObject(with: placesData as Data) as? [SSGIFHistoryModel] { for data in placesArray { if data.gifID != imageData.quote_id { GIFHistoryArray.append(data) } } } } let placesData2 = NSKeyedArchiver.archivedData(withRootObject: GIFHistoryArray) UserDefaults.standard.set(placesData2, forKey: "GIFHistory") }
quelle
Ein einfaches Beispiel für das Speichern von benutzerdefinierten Objekten in UserDefaults lautet wie folgt:
Sie müssen den Boilerplate-Code zum Speichern / Abrufen von Objekten nicht mehr mit Hilfe von THE GREAT 'CODABLE' schreiben. Das ist es, was Sie vor irritierenden manuellen Codierungen / Decodierungen bewahren können.
Entfernen Sie einfach die folgenden zwei Methoden aus Ihrem Code, wenn Sie bereits NSCoding verwenden, und wechseln Sie zum Codable-Protokoll (Kombination aus 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
Beginnen wir mit der Einfachheit von Codable:
Erstellen Sie eine benutzerdefinierte Klasse oder Struktur, die Sie in UserDefaults speichern möchten
struct Person : Codable { var name:String } OR class Person : Codable { var name:String init(name:String) { self.name = name } }
Speichern Sie das Objekt in UserDefaults wie folgt
if let encoded = try? JSONEncoder().encode(Person(name: "Dhaval")) { UserDefaults.standard.set(encoded, forKey: "kSavedPerson") }
Laden Sie das Objekt aus UserDefaults wie folgt
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)")
Das ist es.
quelle
Ich weiß, dass diese Frage alt ist, aber ich habe eine kleine Bibliothek geschrieben , die hilfreich sein könnte:
Sie speichern das Objekt folgendermaßen:
class MyObject: NSObject, Codable { var name:String! var lastName:String! enum CodingKeys: String, CodingKey { case name case lastName = "last_name" } } self.myStoredPref = Pref<MyObject>(prefs:UserDefaults.standard,key:"MyObjectKey") let myObject = MyObject() //... set the object values self.myStoredPref.set(myObject)
und dann, um das Objekt wieder auf seinen ursprünglichen Wert zu extrahieren:
let myStoredValue: MyObject = self.myStoredPref.get()
quelle
Sie können PropertyListEncoder verwenden, um Ihre benutzerdefinierten Objekte in UserDefaults zu speichern, nachdem Sie das Codable-Protokoll in Ihrer Klasse implementiert haben. Lassen Sie die benutzerdefinierte Klasse Benutzer sein
class User: NSObject, Codable { var id: Int? var name: String? var address: String? }
Codierbar bedeutet, dass sowohl decodierbare als auch codierbare Protokolle implementiert werden. Wie der Name schon sagt, können Sie durch die Implementierung von Encodable and Decodable Ihrer Klasse die Möglichkeit geben, von und zu einer externen Darstellung, z. B. JSON oder Property List, zu codieren und zu decodieren.
Jetzt können Sie Ihr in die Eigenschaftsliste übersetztes Modell speichern.
if let encodedData = try? PropertyListEncoder().encode(userModel){ UserDefaults.standard.set(encodedData, forKey:"YOUR_KEY") UserDefaults.standard.synchronize(); }
Und Sie können Ihr Modell mit PropertyListDecoder abrufen. Weil Sie es als Eigenschaftsliste codiert haben.
if let data = UserDefaults.standard.value(forKey:"YOUR_KEY") as? Data { return try? PropertyListDecoder().decode(User.self, from:data) }
quelle
Sie können ein Objekt nicht direkt in der Eigenschaftsliste speichern. Sie können nur einzelne Zeichenfolgen oder andere primitive Typen (Ganzzahlen usw.) speichern. Sie müssen sie daher als einzelne Zeichenfolgen speichern, z.
override init() { } required public init(coder decoder: NSCoder) { func decode(obj:AnyObject) -> AnyObject? { return decoder.decodeObjectForKey(String(obj)) } self.login = decode(login) as! String self.password = decode(password) as! String self.firstname = decode(firstname) as! String self.surname = decode(surname) as! String self.icon = decode(icon) as! UIImage } public func encodeWithCoder(coder: NSCoder) { func encode(obj:AnyObject) { coder.encodeObject(obj, forKey:String(obj)) } encode(login) encode(password) encode(firstname) encode(surname) encode(icon) }
quelle