2014-08-28 14 views
8

Mam klasę i wewnątrz klasy jest (szybka) tablica, oparta na globalnej strukturze. Chcę zapisać tablicę z tą klasą do NSUserDefaults. To jest mój kod:Zapisz struct w klasie do NSUserDefaults przy użyciu 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() 
    } 
} 

Już podklasowałem do NSObject i wiem, że potrzebuję NSCoding. Ale nie znajduję żadnego sposobu na przekonwertowanie tablicy struct na NSMuteableArray lub coś podobnego, którą mogę przechowywać. Jedynym pomysłem do tej pory jest przejrzenie każdego wpisu i skopiowanie go bezpośrednio do nowej tablicy lub użycie kodu lub kodu obiektowego w całym projekcie, więc nigdy nie będę musiał konwertować z tablic szybkich na tablice cel-c. Oba są rzeczami, których nie chcę robić.

Odpowiedz

6

NSUserDefaults jest ograniczony typów może obsługiwać: NSData, NSString, NSNumber, NSDate, NSArray, NSDictionary i Bool. W ten sposób można zapisać obiekty lub struktury Swift. Coś innego musi zostać przekonwertowane na obiekt NSData.

NSUserDefaults nie działa tak samo, jak NSArchiver. Skoro już dodaliśmy NSCoder do swoich klas najlepszym wyborem może być, aby zapisać i przywrócić z NSArchiver do pliku w katalogu Documents ..

z 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 kodowanym są nie klasy, dlatego nie są zgodne z protokołem AnyObject. Musisz przemyśleć swoje podejście. Oto kilka sugestii:

  1. konwertować structfinal class do egzekwowania niezmienność

    final class MyStruct { 
        let start : NSDate = NSDate() 
        let stop : NSDate = NSDate() 
    } 
    
    encoder.encodeObject(mystructs) 
    
  2. mapie je jako słowników tablicę typu [String: NSDate]

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

'let structDicts = mystructs.map {[" start ": $ 0.start," stop ": 0 $.stop]} ' -> błąd krytyczny: element NSArray nie pasuje do elementu Swift Array Element – Thomas

0

mam opracowany a small library, który może pomóc. Możesz go użyć jako zamiennika dla NSCoding dla struktur Swift.

Trzeba by wdrożyć Koting protokół mystruct:

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

Ponieważ wtedy może łatwo przekształcić się struct do Data iz powrotem:

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` 

Wreszcie, to co zrobić, aby implementacja 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) 
    } 
} 

Jest to prawie ten sam kod, który można napisać, jeśli wspierane są konstrukcje Swift z obsługą NSCoding.

Powiązane problemy