我在玩苹果的新Swift编程语言,遇到了一些问题…

目前我试图读取一个plist文件,在Objective-C中,我会做以下工作来获取内容作为NSDictionary:

NSString *filePath = [[NSBundle mainBundle] pathForResource:@"Config" ofType:@"plist"];
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:filePath];

我如何得到一个plist作为一个字典在Swift?

我假设我可以得到路径到plist:

let path = NSBundle.mainBundle().pathForResource("Config", ofType: "plist")

当这工作(如果它是正确的?):我如何获得内容作为一个字典?

还有一个更普遍的问题:

是否可以使用默认的NS*类?我想是的……还是我遗漏了什么?据我所知,默认框架NS*类仍然有效,可以使用吗?


当前回答

这个答案使用Swift本机对象而不是NSDictionary。

斯威夫特3.0

//get the path of the plist file
guard let plistPath = Bundle.main.path(forResource: "level1", ofType: "plist") else { return }
//load the plist as data in memory
guard let plistData = FileManager.default.contents(atPath: plistPath) else { return }
//use the format of a property list (xml)
var format = PropertyListSerialization.PropertyListFormat.xml
//convert the plist data to a Swift Dictionary
guard let  plistDict = try! PropertyListSerialization.propertyList(from: plistData, options: .mutableContainersAndLeaves, format: &format) as? [String : AnyObject] else { return }
//access the values in the dictionary 
if let value = plistDict["aKey"] as? String {
  //do something with your value
  print(value)
}
//you can also use the coalesce operator to handle possible nil values
var myValue = plistDict["aKey"] ?? ""

其他回答

通过尼克的回答转换成一个方便的扩展:

extension Dictionary {
    static func contentsOf(path: URL) -> Dictionary<String, AnyObject> {
        let data = try! Data(contentsOf: path)
        let plist = try! PropertyListSerialization.propertyList(from: data, options: .mutableContainers, format: nil)

        return plist as! [String: AnyObject]
    }
}

用法:

let path = Bundle.main.path(forResource: "plistName", ofType: "plist")!
let url = URL(fileURLWithPath: path)
let dict = Dictionary<String, AnyObject>.contentsOf(path: url)

我敢打赌,它也可以为数组创建类似的扩展

斯威夫特3.0

如果你想从.plist读取一个“二维数组”,你可以这样尝试:

if let path = Bundle.main.path(forResource: "Info", ofType: "plist") {
    if let dimension1 = NSDictionary(contentsOfFile: path) {
        if let dimension2 = dimension1["key"] as? [String] {
            destination_array = dimension2
        }
    }
}

我已经创建了一个简单的字典初始化器替换NSDictionary(contentsOfFile: path)。只要去掉NS。

extension Dictionary where Key == String, Value == Any {

    public init?(contentsOfFile path: String) {
        let url = URL(fileURLWithPath: path)

        self.init(contentsOfURL: url)
    }

    public init?(contentsOfURL url: URL) {
        guard let data = try? Data(contentsOf: url),
            let dictionary = (try? PropertyListSerialization.propertyList(from: data, options: [], format: nil) as? [String: Any]) ?? nil
            else { return nil }

        self = dictionary
    }

}

你可以这样使用它:

let filePath = Bundle.main.path(forResource: "Preferences", ofType: "plist")!
let preferences = Dictionary(contentsOfFile: filePath)!
UserDefaults.standard.register(defaults: preferences)

Swift 4.0 iOS 11.2.6列表解析和代码解析它,基于https://stackoverflow.com/users/3647770/ashok-r上面的答案。

<?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">
<array>
  <dict>
    <key>identity</key>
    <string>blah-1</string>
    <key>major</key>
    <string>1</string>
    <key>minor</key>
    <string>1</string>
    <key>uuid</key>
    <string>f45321</string>
    <key>web</key>
    <string>http://web</string>
</dict>
<dict>
    <key>identity</key>
    <string></string>
    <key>major</key>
    <string></string>
    <key>minor</key>
    <string></string>
    <key>uuid</key>
    <string></string>
    <key>web</key>
    <string></string>
  </dict>
</array>
</plist>

do {
   let plistXML = try Data(contentsOf: url)
    var plistData: [[String: AnyObject]] = [[:]]
    var propertyListFormat =  PropertyListSerialization.PropertyListFormat.xml
        do {
            plistData = try PropertyListSerialization.propertyList(from: plistXML, options: .mutableContainersAndLeaves, format: &propertyListFormat) as! [[String:AnyObject]]

        } catch {
            print("Error reading plist: \(error), format: \(propertyListFormat)")
        }
    } catch {
        print("error no upload")
    }

我一直在使用Swift 3.0,并希望为更新的语法贡献一个答案。此外,可能更重要的是,我使用PropertyListSerialization对象来做繁重的工作,这比仅仅使用NSDictionary灵活得多,因为它允许数组作为plist的根类型。

下面是我正在使用的plist的截图。这有点复杂,以便显示可用的功率,但这将适用于任何允许的plist类型组合。

正如你所看到的,我正在使用字符串数组:字符串字典来存储网站名称及其对应的URL列表。

如上所述,我使用PropertyListSerialization对象来为我做繁重的工作。此外,Swift 3.0变得更加“Swifty”,所以所有的对象名称都失去了“NS”前缀。

let path = Bundle.main().pathForResource("DefaultSiteList", ofType: "plist")!
let url = URL(fileURLWithPath: path)
let data = try! Data(contentsOf: url)
let plist = try! PropertyListSerialization.propertyList(from: data, options: .mutableContainers, format: nil)

在上面的代码运行后,plist的类型将是Array<AnyObject>,但我们知道它的实际类型,所以我们可以将它强制转换为正确的类型:

let dictArray = plist as! [[String:String]]
// [[String:String]] is equivalent to Array< Dictionary<String, String> >

现在我们可以以自然的方式访问Array of String:String dictionary的各种属性。希望将它们转换为实际的强类型结构体或类;)

print(dictArray[0]["Name"])