Swift: pList со "сложными" данными
Как я прочитал и опробовал:-) Я могу сохранить только некоторые простые типы данных в файлах pList. Тем не менее, мне нравится использовать структуры, классы и т. Д. Для представления моих данных. Это должно быть как можно проще сохранено в файл 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" это не так!
Обновление 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()
}
}
1 ответ
Есть несколько способов сделать это, вы можете придерживаться NSCoding
протокол или вы можете написать методы для преобразования вашего класса / структуры в словарь и сериализации оттуда.
Вот хорошее введение в использование NSCoding
протокол
Что касается преобразования в и из словаря, то обычным способом является предоставление неисправного 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))