2014-08-28 9 views
8

Ich habe eine Klasse und innerhalb der Klasse ist eine (schnelle) Array, basierend auf einer globalen Struktur. Ich möchte ein Array mit dieser Klasse zu NSUserDefaults speichern. Das ist mein Code:Speichern von Struct in der Klasse zu NSUserDefaults mit Swift

struct mystruct { 
    var start : NSDate = NSDate() 
    var stop : NSDate = NSDate() 
} 

class MyClass : NSObject { 

    var mystructs : [mystruct] 

    init(mystructs : [mystruct]) { 

     self.mystructs = mystructs 
     super.init() 
    } 

    func encodeWithCoder(encoder: NSCoder) { 
     //let val = mystructs.map { $0 as NSObject } //this also doesn't work 
     let objctvtmrec = NSMutableArray(mystructs) //gives error 
     encoder.encodeObject(objctvtmrec) 
     //first approach: 
     encoder.encodeObject(mystructs) //error: [mystructs] doesn't conform to protocol 'anyobject' 
    } 

} 

var records : [MyClass] { 
    get { 
     var returnValue : [MyClass]? = NSUserDefaults.standardUserDefaults().objectForKey("records") as? [MyClass] 
     if returnValue == nil 
     { 
      returnValue = [] 
     } 
     return returnValue! 
    } 
    set (newValue) { 
     let val = newValue.map { $0 as AnyObject } 
     NSUserDefaults.standardUserDefaults().setObject(val, forKey: "records") 
     NSUserDefaults.standardUserDefaults().synchronize() 
    } 
} 

Ich habe bereits NSObject subclassed, und ich weiß, ich brauche NSCoding. Aber ich finde keine Möglichkeit, das struct-Array in ein NSMuteableArray oder etwas Ähnliches zu konvertieren, das ich speichern kann. Die einzige Idee bis jetzt ist es, jeden Eintrag durchzugehen und ihn direkt in ein neues Array zu kopieren oder viel oder objective-c Code über das ganze Projekt zu verwenden, so dass ich nie von schnellen Arrays in objektive-c Arrays konvertieren muss. Beides sind Dinge, die ich nicht tun möchte.

Antwort

6

NSUserDefaults ist in den Typen beschränkt damit umgehen kann: NSData, NSString, NSNumber, NSDate, NSArray, NSDictionary und Bool. So können keine Swift Objekte oder Strukturen gespeichert werden. Alles andere muss in ein NSData Objekt konvertiert werden.

NSUserDefaults funktioniert nicht auf die gleiche Weise wie NSArchiver. Da Sie bereits im Documents Verzeichnis mit NSArchiver in einer Datei speichern und wiederherstellen können NSCoder auf Ihre Klassen die beste Wahl hinzugefügt werden ..

aus dem Apple NSUserDefaults Docs:

A default object must be a property list, that is, an instance of (or for collections a combination of instances of): NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. If you want to store any other type of object, you should typically archive it to create an instance of NSData.

10

Swift structs sind nicht Klassen, daher entsprechen sie nicht AnyObject Protokoll. Sie müssen Ihren Ansatz überdenken. Hier sind einige Vorschläge:

  1. Konvertieren Sie Ihre struct-final class Unveränderlichkeit zu erzwingen

    final class MyStruct { 
        let start : NSDate = NSDate() 
        let stop : NSDate = NSDate() 
    } 
    
    encoder.encodeObject(mystructs) 
    
  2. Karte sie als Array Wörterbücher vom Typ [String: NSDate]

    let structDicts = mystructs.map { ["start": $0.start, "stop": $0.stop] } 
    encoder.encodeObject(structDicts) 
    
+1

'lass structDicts = mystructs.map {[" start ": $ 0. Start," stop ": $ 0.stop]} ' -> Schwerwiegender Fehler: NSArray-Element konnte nicht mit dem Swift-Array-Elementtyp übereinstimmen – Thomas

0

Ich habe entwickelt a small library, die helfen können. Sie können es als Ersatz für NSCoding für Swift-Strukturen verwenden.

Sie müssten ein Koting Protokoll für mystruct implementieren:

struct mystruct: Koting { 

    var start : NSDate = NSDate() 
    var stop : NSDate = NSDate() 

    // MARK: - Koting 

    init?(koter: Koter) { 
     guard let start: NSDate = koter.dekotObject(forKey: "start"), 
       let stop: NSDate = koter.dekotObject(forKey: "stop") else { 
      return nil 
     } 
     self.init(start: start, stop: stop) 
    } 

    func enkot(with koter: Koter) { 
     koter.enkotObject(start, forKey: "start") 
     koter.enkotObject(stop, forKey: "stop") 
    } 
} 

Seitdem können Sie bequem die Struktur zu Data konvertieren und zurück:

let str = mystruct(start: NSDate(/*...*/), stop: NSDate(/*...*/)) 
guard let data = str.de_data else { return } // potentially may be nil 
let restoredStr = mystruct.de_from(data: data) // if data is irrelevant, returns `nil` 

Schließlich ist es das, was Sie tun, um implementieren NSCoding:

class MyClass: NSObject, NSCoding { 

    var mystructs: [mystruct] 

    init(mystructs: [mystruct]) { 

     self.mystructs = mystructs 
     super.init() 
    } 

    func encode(with aCoder: NSCoder) { 
     guard let datas = mystructs.flatMap { $0.de_data } else { return } 
     aCoder.encode(datas, forKey: "mystructs") 
    } 

    required convenience init?(coder aDecoder: NSCoder) { 
     guard let datas = aDecoder.decodeObject(forKey: "mystructs") as? [Data], 
       let mystructs = datas.flatMap { mystruct.de_from(data: $0) } else { 
      return nil 
     } 

     self.init(mystructs : mystructs) 
    } 
} 

Es ist ziemlich genau der gleiche Code, den Sie schreiben würden, wenn NSCoding Swift-Strukturen unterstützt.