2014-08-28 30 views
8

Ho una classe e all'interno della classe c'è una matrice (veloce), basata su una struttura globale. Voglio salvare un array con questa classe su NSUserDefaults. Questo è il mio codice:Salva struct in class a NSUserDefaults usando 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() 
    } 
} 

Sono già sottoclasse a NSObject e so che ho bisogno di NSCoding. Ma non trovo alcun modo per convertire l'array struct in un NSMuteableArray o qualcosa di simile che posso memorizzare. L'unica idea fino ad ora è di passare attraverso ogni voce e copiarla direttamente in un nuovo array o di usare molto o codice obiettivo-c su tutto il progetto, quindi non ho mai bisogno di convertire da matrici veloci a matrici di obiettivi-c. Entrambe sono cose che non voglio fare.

risposta

6

NSUserDefaults è limitato nei tipi che può gestire: NSData, NSString, NSNumber, NSDate, NSArray, NSDictionary e Bool. Quindi no È possibile salvare oggetti o strutture Swift. Qualsiasi altra cosa deve essere convertita in un oggetto NSData.

NSUserDefaults non funziona allo stesso modo di NSArchiver. Dal momento che avete già aggiunto NSCoder alle classi la scelta migliore potrebbe essere per salvare e ripristinare con NSArchiver in un file nella directory Documents ..

da 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 struct sono non classi, pertanto non sono conformi al protocollo AnyObject. Devi ripensare al tuo approccio. Ecco alcuni suggerimenti:

  1. di convertire il struct-final class per far rispettare immutabilità

    final class MyStruct { 
        let start : NSDate = NSDate() 
        let stop : NSDate = NSDate() 
    } 
    
    encoder.encodeObject(mystructs) 
    
  2. mapparli come un array dizionari di tipo [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]} -> errore fatale: l'elemento NSArray non è riuscito a far corrispondere il tipo di elemento di array Swift – Thomas

0

ho sviluppato a small library che può aiutare. Puoi usarlo come sostituzione di NSCoding per le strutture di Swift.

Si avrebbe bisogno di implementare un protocollo Koting per 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") 
    } 
} 

Da allora si può facilmente convertire la struttura per Data e ritorno:

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` 

Infine, questo è ciò che si fa a attuare 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) 
    } 
} 

È praticamente lo stesso codice che scriverebbe se NSCoding supporta le strutture di Swift.

Problemi correlati