将数组内容(自定义类型)写入 plist 作为 swift 中的键控数组

Write array content (of custom type) to plist as keyed array in swift

MacOS,Swift4.

我想将数据存储在 plist 中。我可以加载这些值,但是在写出它们时它们的格式不正确。

我正在加载 plist(在 table 视图中显示),效果很好:

    func loadBooks() {
        let path = dataFilePath() // /com.name.books/Data/Documents/

        do {
            let data = try Data(contentsOf: path)
            let decoder = PropertyListDecoder()
            let plist = try decoder.decode([String: [Book]].self, from: data)
            books = plist["BooksData"]!
        } catch {
            print("Error occurred: \(error.localizedDescription)")
        }
    }

这是我正在加载的 plist:

<plist version="1.0">
    <dict>
    <key>BooksData</key>
    <array>
        <dict>
            <key>author</key>
            <string>First Author</string>
            <key>title</key>
            <string>First Title</string>     
            <key>lentBy</key>
            <string>Person</string>
        </dict>       
        <dict>
            <key>author</key>
            <string>Second Author</string>  
            <key>title</key>
            <string>Book Title2</string>
            <key>lentBy</key>
            <string>Another Person</string>
        </dict>
    </array>  
    </dict>
</plist>

我正在尝试像这样将数组写回 plist:

  func saveBooks() {
        let data = books

        do {
            let encoder = PropertyListEncoder()
            var plist = try encoder.encode(data)
            try plist.write(to: dataFilePath())
        } catch {
            print("Error occured: \(error.localizedDescription)")
        }
    }

plist 内容丢失了它的 "wrapping" 和密钥 ("BooksData"),随后无法再使用 loadBooks() 函数读取。

这是输出:

<plist version="1.0">
    <array>    
        <dict>
            <key>author</key>
            <string>First Author</string>
            <key>title</key>
            <string>First Title</string>     
            <key>lentBy</key>
            <string>Person</string>
        </dict>       
        <dict>
            <key>author</key>
            <string>Second Author</string>  
            <key>title</key>
            <string>Book Title2</string>
            <key>lentBy</key>
            <string>Another Person</string>
        </dict>  
    </array>
</plist>

我玩过 NSMutableDictionary(),尝试用 dict.setObject(plist, forKey: "BooksData") 重新包装并用 dict.write(toFile: dataFilePath().absoluteString, atomically: true) 编写。

            let encoder = PropertyListEncoder()
            var plist = try encoder.encode(data)

            dict.setObject(plist, forKey: "BooksData" as NSCopying)
            dict.write(toFile: dataFilePath().absoluteString, atomically: true)
            print(dict)

正在打印字典 returns

{
    BooksData = <62706c69 73743030 a401080c 10d30203 04050607 55746974 6c655661 7574686f 72566c65 6e744279 59427563 68746974 656c5b46 696e6b2c 20457567 656e546e 6f6e65d3 02030409 0a0b5f10 14457266 61687275 6e672075 6e642055 72746569 6c5f100f 48757373 65726c2c 2045646d 756e645a 50617472 69636b20 5379d302 03040d0e 0f5f101a 4b726974 696b2064 65722072 65696e65 6e205665 726e756e 66745e4b 616e742c 20496d6d 616e7565 6c50d302 03041112 0f5b4369 74612041 63746976 615e4172 656e6474 2c204861 6e6e6168 080d141a 2128323e 434a6173 7e85a2b1 b2b9c500 00000000 00010100 00000000 00001300 00000000 00000000 00000000 0000d4>;
}

而且文件甚至没有写出,即 dataFilePath() 中的旧文件保持不变。

答案会在PropertyListSerialization.propertyList(from:options:format:)吗?还是CodingKeys?或者 encode(title, forKey:"BooksData")(?) 等等? NSMutableArrayKeyedDecodingContainerunkeyedContainer 等等? NSKeyedArchiver?

这似乎是一项简单的任务,但我就是做不对。似乎有一些关于此的相关线程,但没有真正应用或给我解决此问题的关键信息。该文档也没有为我阐明任何内容,我需要一些具体的指示。

请帮忙。

由于预期的类型是 [String: [Book]] 您必须 手动创建根字典

func saveBooks() {
    do {
        let encoder = PropertyListEncoder()
        let plistData = try encoder.encode(["BooksData":books])
        try plistData.write(to: dataFilePath())
    } catch {
        print("Error occured:", error) // never ever print `localizedDescription`
    }
}

但如果 plist 仅包含 books 数组,则仅加载和保存该数组。此外,我建议将可能的错误移交给调用者

var books = [Book]()

func loadBooks() throws {
    let path = dataFilePath() // /com.name.books/Data/Documents/
    let data = try Data(contentsOf: path)
    let decoder = PropertyListDecoder()
    books = try decoder.decode([Book].self, from: data)
}

然后保存方式匹配类型

func saveBooks() throws {
    let encoder = PropertyListEncoder()
    let plistData = try encoder.encode(books)
    try plistData.write(to: dataFilePath())
}