Swift: Reading a plist from MainBundle and writing

2020-02-16 03:04发布

问题:

Take the following file, named Permissions.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
    <key>SomeKey</key>
    <false/>
    </dict>
</plist>

I'd like to read it in from my MainBundle, modify it, and write it out to my .Documents. However, even if I leave it unmodified, the write fails. The Swift syntax seems to have changed since this question, and the other questions I could find were caused by incorrect key types, which would be odd given that I'm not modifying before writing out. Here is the complete code to reproduce the error:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Read in the plist from the main bundle.
        guard let path = NSBundle.mainBundle().pathForResource("Permissions", ofType: "plist") else {
            NSLog("Path could not be created.")
            return
        }

        guard NSFileManager.defaultManager().fileExistsAtPath(path) else {
            NSLog("File does not exist.")
            return
        }

        guard let resultDictionary = NSMutableDictionary(contentsOfFile: path) else {
            NSLog("Contents could not be read.")
            return
        }

        print(resultDictionary) // { Facebook = 0; }

        // Write it to the documents directory
        let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as NSArray

        guard let docsString = paths[0] as? String else {
            NSLog("Couldn't find documents directory; permissions could not be updated.")
            return
        }

        guard let docsURL = NSURL(string: docsString) else {
            NSLog("Couldn't convert the path to a URL; permissions could not be updated.")
            return
        }

        let plistURL = docsURL.URLByAppendingPathComponent("Permissions.plist")
        let plistPath = plistURL.path!
        let plistString = "\(plistURL)"

        if !resultDictionary.writeToURL(plistURL, atomically: false) {
            NSLog("Writing file to disk via url was unsucessful.") // Failure
        }

        if !resultDictionary.writeToFile(plistPath, atomically: false) {
            NSLog("Writing file to disk via path was unsucessful.")
        }

        if !resultDictionary.writeToFile(plistString, atomically: false) {
            NSLog("Writing file to disk via path was unsucessful.")
        }

        print("URL: ",NSMutableDictionary(contentsOfURL: plistURL)) // nil
        print("Path: ",NSMutableDictionary(contentsOfFile: plistPath)) // Prints
        print("String: ",NSMutableDictionary(contentsOfFile: plistString)) // Prints

    }

}

Edit

I made a silly logic error in the original example (missing ! in the last line) which was causing it look like it was failing when it wasn't. However, the example now fails with the URL method but works with either the path or String interpolation methods. Why is the URL method failing?

回答1:

This is wrong:

guard let docsURL = NSURL(string: docsString) else { ... }

To create an NSURL from a file path, use

let docsURL = NSURL(fileURLWithPath: docsString)

Conversely, you cannot use string interpolation to create a file path from an NSURL:

let plistString = "\(plistURL)"

Use the .path method instead:

let plistPath = plistURL.path!

Or better, use the writeToURL() method to write your dictionary, then you don't have to convert the URL to a path.



标签: swift plist