Search code examples
iosswiftplistnsfilemanager

Bundle.main.url is unable to locate plist saved with PropertyListSerialization


I'm reading from and writing to plist, but one of the two processes is not working.

Reading

var data: [String: Int] = [:]
if let url = Bundle.main.url(forResource: "Calendar", withExtension: "plist") {
    do {
        print("url: \(url)")
        let dataContent = try Data(contentsOf: url)
        if let dict = try PropertyListSerialization.propertyList(from: dataContent, format: nil) as? [String: Int] {
            data = dict
        }
    } catch {
        print(error)
    }
}

url here is not being printed which means Bundle.main.url is either unable to locate Calendar.plist or it's not being saved properly.

Writing

do {
    let path = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("Calendar.plist")
    let plistData = try PropertyListSerialization.data(fromPropertyList: data, format: .xml, options: 0)
    try plistData.write(to: path)
} catch {
    print(error)
}

data is ["test": 1] tentatively.


Solution

  • Here is what you are doing:

    • when reading, you read your Calendar.plist from the app's bundle root folder.
    • when writing, you write your Calendar.plist into the app's bundle Documents folder.

    Since the app's bundle root folder (from where you're reading) is not writable for you, the proper location for your Calendar.plist is Documents folder, and you should create the URL or path for it in the same way as you do it when writing, better in a dedicated getter that you call from both reading and writing routines.

    func calendarURL() -> URL? {
        guard let result = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("Calendar.plist") else { return nil }
        return result
    }
    

    Reading

    var data: [String: Int] = [:]
    if let url = calendarURL() {
        ...
    

    Writing

    if let path = calendarURL() {
        do {
            let plistData = try PropertyListSerialization.data(fromPropertyList: data, format: .xml, options: 0)
            ...