2016-05-18 2 views
0

Как я прочитал и опробовал :-) Я могу сохранить только некоторые простые типы данных в файлах pList. Тем не менее, я люблю использовать структуры, классы и т. Д. Для представления моих данных. Это должно быть как можно проще сохранено в файле pList и перезагружено.Swift: pList с «сложными» данными

Я вижу, что NSData является допустимым типом для pLists. А также, что это общий тип данных. Так что это хорошая идея для перемещения/преобразования/принудительного изменения структуры или класса в объект NSData для сохранения и перезагрузки? Как это будет сделано?

До сих пор я использую что-то вроде этого для сохранения:

let dict: NSMutableDictionary = ["XYZ": "XYZ"] 

// saving values 
dict.setObject(myBasicArray, forKey: "BasicArray") 

dict.writeToFile(path, atomically: false) 

Обновлено: Я использовал предложенный код и расширил его для обработки на структуру:

import Cocoa 

struct SteeringItem { 
    var ext = String() // extension including dot e.g. ".JPG" 
    var bitmap = Int()  // flag for grouping file types, e.g. photos 

    init?(ext: String, bitmap: Int) { 
     // Initialize stored properties. 
     self.ext = ext 
     self.bitmap = bitmap 
    } 
} 

class Foo { 
    let one: Int 
    let two: SteeringItem 


    init(one:Int, two: SteeringItem) { 
     self.one = one 
     self.two = two 
    } 

    init?(dict:[String: AnyObject]) { 
     guard let 
      one = dict["one"] as? Int, 
      two = dict["two"] as? SteeringItem else { return nil } 

     self.one = one 
     self.two = two 
    } 

    func toDictionary() -> [String: AnyObject] { 
     var retval = [String: AnyObject]() 
     if let 
      one = self.one as? AnyObject, 
      two = self.two as? AnyObject { 
      retval["one"] = one 
      retval["two"] = two 
     } 
     return retval 
    } 
} 

// create struct 
let writeStruct = Foo(one: 1, two: SteeringItem(ext: "one",bitmap: 1)!) 
print(writeStruct, "\n") 

// write to plist 
let writeDict = writeStruct.toDictionary() as NSDictionary 
let path = ("~/test.plist" as NSString).stringByExpandingTildeInPath 
writeDict.writeToFile(path, atomically: true) 

// print contents of file 
print(try NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding)) 

// read plist and recreate struct 
if let 
    readDict = NSDictionary(contentsOfFile: path) as? [String:AnyObject], 
    readStruct = Foo(dict: readDict) { 
    print(readStruct) 
} 

, но это больше не пишет. С String это сработало, с «struct SteeringItem» это не так!

Update 2: класс вместо структуры

class SteeringItem : NSObject, NSCoding { 
    var ext = String() // extension including dot e.g. ".JPG" 
    var bitmap = Int()  // flag for grouping file types, e.g. photos 

    func encodeWithCoder(aCoder: NSCoder) { 
     aCoder.encodeObject(ext, forKey: "ext") 
     aCoder.encodeObject(bitmap, forKey: "bitmap") 
    } 

    required convenience init?(coder aDecoder: NSCoder) { 
     let ext = aDecoder.decodeObjectForKey("ext") as! String 
     let bitmap = aDecoder.decodeObjectForKey("bitmap") as! Int 

     self.init(ext: ext, bitmap: bitmap) 
    } 

    init?(ext: String, bitmap: Int) { 
     // Initialize stored properties. 
     self.ext = ext 
     self.bitmap = bitmap 

     super.init() 
    } 
} 
+0

В зависимости от сложности вашего данных, возможно, стоит проверить «CoreData», чтобы узнать, подходит ли она более подходящей. – Alexander

+0

Вам не требуется «инициализация», если она не может быть выполнена. В этом случае, если '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' 'инициализация' Таким образом, вам не нужен метод 'init' в вашей' SteeringItem' 'struct'. – ColGraff

ответ

1

Там в несколько способов сделать это, вы можете присоединиться к протоколу NSCoding или вы можете написать методы для преобразования класса /-структуры в словарь и сериализации оттуда.

Вступительное слово, чтобы использовать NSCoding protocol.

Что касается преобразования в словарь и из него, то обычный способ заключается в предоставлении отказоустойчивого метода init, который принимает Dictionary<String, AnyObject>, который проверяет и копирует пары ключ: значение в переменные-члены. Вы также предоставляете метод, который возвращает Dictionary<String, AnyObject> с теми же парами ключ: значение, что и init. Затем вы можете сериализовать, вызвав метод create и сериализуя полученный результат Dictionary, вы десериализуете, читая в Dictionary и передавая это в метод init.

Вот пример преобразования:

/// Provides conversion to and from [String: AnyObject] for use in serialization 
protocol Serializable { 
    init?(dict:[String: AnyObject]) 
    func toDictionary() -> [String: AnyObject] 
} 

struct SteeringItem { 
    // Changed var to let, it's a good practice with a simple struct 
    let ext : String // extension including dot e.g. ".JPG" 
    let bitmap : Int  // flag for grouping file types, e.g. photos 
} 

struct Foo { 
    let one: Int 
    let two: SteeringItem 
} 

// Add serialization to structs 

extension SteeringItem: Serializable { 
    init?(dict:[String: AnyObject]) { 
    guard let 
     ext = dict["ext"] as? String, 
     bitmap = dict["bitmap"] as? Int else { return nil } 
    self.ext = ext 
    self.bitmap = bitmap 
    } 

    func toDictionary() -> [String: AnyObject] { 
    var retval = [String: AnyObject]() 
    if let 
     ext = self.ext as? AnyObject, 
     bitmap = self.bitmap as? AnyObject { 
     retval["ext"] = ext 
     retval["bitmap"] = bitmap 
    } 
    return retval 
    } 
} 

extension Foo: Serializable { 
    init?(dict:[String: AnyObject]) { 
    guard let 
     one = dict["one"] as? Int, 
     twoDict = dict["two"] as? [String: AnyObject], 
     two = SteeringItem(dict: twoDict) else { return nil } 

    self.one = one 
    self.two = two 
    } 

    func toDictionary() -> [String: AnyObject] { 
    var retval = [String: AnyObject]() 
    let twoDict = self.two.toDictionary() 
    if let 
     one = self.one as? AnyObject, 
     two = twoDict as? AnyObject { 
     retval["one"] = one 
     retval["two"] = two 
    } 
    return retval 
    } 
} 

Вот как проверить его (в детской площадки):

import Foundation 

// create struct 
let writeStruct = Foo(one: 1, two: SteeringItem(ext: "jpg", bitmap: 1)) 
print(writeStruct, "\n") 

// write to plist 
let writeDict = writeStruct.toDictionary() as NSDictionary 
let path = ("~/test.plist" as NSString).stringByExpandingTildeInPath 
writeDict.writeToFile(path, atomically: true) 

// print contents of file 
print(try NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding)) 

// read plist and recreate struct 
if let 
    readDict = NSDictionary(contentsOfFile: path) as? [String:AnyObject], 
    readStruct = Foo(dict: readDict) { 
    print(readStruct) 
} 

Результаты:

Foo(one: 1, two: SteeringItem(ext: "jpg", bitmap: 1)) 

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 
<plist version="1.0"> 
<dict> 
    <key>one</key> 
    <integer>1</integer> 
    <key>two</key> 
    <dict> 
     <key>bitmap</key> 
     <integer>1</integer> 
     <key>ext</key> 
     <string>jpg</string> 
    </dict> 
</dict> 
</plist> 

Foo(one: 1, two: SteeringItem(ext: "jpg", bitmap: 1)) 
+0

Ничего себе! Я так многому научился. Прямо сейчас я могу спасти свой класс. Затем я проверю, могу ли я его перезагрузить. :-) Большое спасибо! – Peter71

+0

Один вопрос: Как мне вернуть класс? Обычно я использую «if dict.objectForKey (« myVar »)! = Nil {myVar = d.objectForKey (" myVar ")! As! [String]}", но это не удается. Должен ли он быть подобным: «myVar = MyClass (dict: dict as! [String: AnyObject])!» ? – Peter71

+0

Я обновил ответ на примере чтения и записи с помощью «Словаря» – ColGraff

Смежные вопросы