如何在Swift中将plist作为字典?

时间:2014-06-04 19:05:18

标签: ios swift

我正在使用Apple的新 Swift 编程语言并遇到一些问题......

目前我正在尝试读取一个plist文件,在Objective-C中我将执行以下操作以将内容作为NSDictionary:

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

如何在Swift中将plist作为字典?

我假设我可以通过以下方式获得plist的路径:

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

当这有效时(如果它是正确的?):如何将内容作为字典获取?

还有一个更普遍的问题:

使用默认的 NS * 类是否可以?我想是这样......或者我错过了什么?据我所知,默认框架 NS * 类仍然有效且可以使用吗?

26 个答案:

答案 0 :(得分:247)

你仍然可以在Swift中使用NSDictionaries:

适用于Swift 4

 var nsDictionary: NSDictionary?
 if let path = Bundle.main.path(forResource: "Config", ofType: "plist") {
    nsDictionary = NSDictionary(contentsOfFile: path)
 }

适用于Swift 3 +

if let path = Bundle.main.path(forResource: "Config", ofType: "plist"),
   let myDict = NSDictionary(contentsOfFile: path){
    // Use your myDict here
}

旧版本的Swift

var myDict: NSDictionary?
if let path = NSBundle.mainBundle().pathForResource("Config", ofType: "plist") {
    myDict = NSDictionary(contentsOfFile: path)
}
if let dict = myDict {
    // Use your dict here
}

NSClasses仍然可用,并且可以在Swift中使用。我认为他们可能希望尽快将重点转移到快速,但目前快速的API并不具备核心NSClasses的所有功能。

答案 1 :(得分:138)

如果我想将.plist转换为Swift词典,我就会这样做:

if let path = NSBundle.mainBundle().pathForResource("Config", ofType: "plist") {
  if let dict = NSDictionary(contentsOfFile: path) as? Dictionary<String, AnyObject> {
    // use swift dictionary as normal
  }
}

为Swift 2.0编辑:

if let path = NSBundle.mainBundle().pathForResource("Config", ofType: "plist"), dict = NSDictionary(contentsOfFile: path) as? [String: AnyObject] {
    // use swift dictionary as normal
}

为Swift 3.0编辑:

if let path = Bundle.main.path(forResource: "Config", ofType: "plist"), let dict = NSDictionary(contentsOfFile: path) as? [String: AnyObject] {
        // use swift dictionary as normal
}

答案 2 :(得分:37)

swift 3.0 从Plist阅读。

#define SIZE_OF_ARRAY(_array) (sizeof(_array) / sizeof(_array[0]))

阅读更多 HOW TO USE PROPERTY LISTS (.PLIST) IN SWIFT

答案 3 :(得分:31)

Swift 4.0

您现在可以使用Decodable协议将.plist解码为自定义结构。我将介绍一个基本的例子,对于更复杂的.plist结构,我建议阅读Decodable / Encodable(这是一个很好的资源:https://benscheirman.com/2017/06/swift-json/)。

首先将结构设置为.plist文件的格式。对于此示例,我将考虑具有根级别Dictionary和3个条目的.plist:1带有键“name”的字符串,1带有键“age”的Int和带有键“single”的1布尔值。这是结构:

struct Config: Decodable {
    private enum CodingKeys: String, CodingKey {
        case name, age, single
    }

    let name: String
    let age: Int
    let single: Bool
}

足够简单。现在很酷的部分。使用PropertyListDecoder类,我们可以轻松地将.plist文件解析为此结构的实例化:

func parseConfig() -> Config {
    let url = Bundle.main.url(forResource: "Config", withExtension: "plist")!
    let data = try! Data(contentsOf: url)
    let decoder = PropertyListDecoder()
    return try! decoder.decode(Config.self, from: data)
}

没有太多代码可以担心,而且它全部都在Swift中。更好的是,我们现在可以轻松使用Config结构的实例化:

let config = parseConfig()
print(config.name) 
print(config.age)
print(config.single) 

这将打印.plist中“name”,“age”和“single”键的值。

答案 4 :(得分:20)

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

Swift 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"] ?? ""

答案 5 :(得分:16)

我一直在使用Swift 3.0,并希望为更新的语法提供答案。另外,也许更重要的是,我使用 PropertyListSerialization 对象来完成繁重的工作,这比仅使用NSDictionary要灵活得多,因为它允许将Array作为plist的根类型

下面是我正在使用的plist的屏幕截图。它是一个 little 复杂的,以便显示可用的功率,但这适用于任何允许的plist类型组合。

Sample plist file 如您所见,我正在使用Array of String:String字典来存储网站名称列表及其相应的URL。

我正在使用 PropertyListSerialization 对象,如上所述,为我做繁重的工作。此外,Swift 3.0已经变得更加“快速”#34;所以所有的对象名称都失去了&#34; NS&#34;前缀。

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 Dictionaries的各种属性。 希望将它们转换为实际的强类型结构或类;)

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

答案 6 :(得分:8)

最好使用本机词典和数组,因为它们已针对swift进行了优化。话虽如此,你可以在swift中使用NS ...类,我认为这种情况需要保证。以下是如何实现它:

var path = NSBundle.mainBundle().pathForResource("Config", ofType: "plist")
var dict = NSDictionary(contentsOfFile: path)

到目前为止(在我看来)这是访问plist的最简单,最有效的方法,但是在未来我希望苹果会在原生词典中添加更多功能(例如使用plist)。

答案 7 :(得分:8)

Swift - 读/写plist和文本文件....

override func viewDidLoad() {
    super.viewDidLoad()

    let fileManager = (NSFileManager .defaultManager())
    let directorys : [String]? = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory,NSSearchPathDomainMask.AllDomainsMask, true) as? [String]

    if (directorys != nil){
        let directories:[String] = directorys!;
        let dictionary = directories[0]; //documents directory


        //  Create and insert the data into the Plist file  ....
        let plistfile = "myPlist.plist"
        var myDictionary: NSMutableDictionary = ["Content": "This is a sample Plist file ........."]
        let plistpath = dictionary.stringByAppendingPathComponent(plistfile);

        if !fileManager .fileExistsAtPath(plistpath){//writing Plist file
            myDictionary.writeToFile(plistpath, atomically: false)
        }
        else{            //Reading Plist file
            println("Plist file found")

            let resultDictionary = NSMutableDictionary(contentsOfFile: plistpath)
            println(resultDictionary?.description)
        }


        //  Create and insert the data into the Text file  ....
        let textfile = "myText.txt"
        let sampleText = "This is a sample text file ......... "

        let textpath = dictionary.stringByAppendingPathComponent(textfile);
        if !fileManager .fileExistsAtPath(textpath){//writing text file
            sampleText.writeToFile(textpath, atomically: false, encoding: NSUTF8StringEncoding, error: nil);
        } else{
            //Reading text file
            let reulttext  = String(contentsOfFile: textpath, encoding: NSUTF8StringEncoding, error: nil)
            println(reulttext)
        }
    }
    else {
        println("directory is empty")
    }
}

答案 8 :(得分:8)

Swift 2.0:访问Info.Plist

我有一个名为CoachMarksDictionary的词典,在Info.Plist中有一个布尔值。我想访问bool值并使其成立。

let path = NSBundle.mainBundle().pathForResource("Info", ofType: "plist")!
  let dict = NSDictionary(contentsOfFile: path) as! [String: AnyObject]

  if let CoachMarksDict = dict["CoachMarksDictionary"] {
       print("Info.plist : \(CoachMarksDict)")

   var dashC = CoachMarksDict["DashBoardCompleted"] as! Bool
    print("DashBoardCompleted state :\(dashC) ")
  }

写入Plist:

来自自定义Plist: - (从File-New-File-Resource-PropertyList中创建。添加了三个名为DashBoard_New,DashBoard_Draft,DashBoard_Completed的字符串)

func writeToCoachMarksPlist(status:String?,keyName:String?)
 {
  let path1 = NSBundle.mainBundle().pathForResource("CoachMarks", ofType: "plist")
  let coachMarksDICT = NSMutableDictionary(contentsOfFile: path1!)! as NSMutableDictionary
  var coachMarksMine = coachMarksDICT.objectForKey(keyName!)

  coachMarksMine  = status
  coachMarksDICT.setValue(status, forKey: keyName!)
  coachMarksDICT.writeToFile(path1!, atomically: true)
 }

该方法可以称为

self.writeToCoachMarksPlist(" true - means user has checked the marks",keyName: "the key in the CoachMarks dictionary").

答案 9 :(得分:4)

实际上可以在1行

中完成
    var dict = NSDictionary(contentsOfFile: NSBundle.mainBundle().pathForResource("Config", ofType: "plist"))

答案 10 :(得分:4)

您可以通过以下方式阅读SWIFT语言中的plist:

let path = NSBundle.mainBundle().pathForResource("PriceList", ofType: "plist")
let dict = NSDictionary(contentsOfFile: path)

读取单字典值:

let test: AnyObject = dict.objectForKey("index1")

如果你想在plist中获得完整的多维字典:

let value: AnyObject = dict.objectForKey("index2").objectForKey("date")

这是plist:

<plist version="1.0">
<dict>
<key>index2</key>
<dict>
    <key>date</key>
    <string>20140610</string>
    <key>amount</key>
    <string>110</string>
</dict>
<key>index1</key>
<dict>
    <key>amount</key>
    <string>125</string>
    <key>date</key>
    <string>20140212</string>
</dict>
</dict>
</plist>

答案 11 :(得分:4)

通过Nick的回答转换为便利扩展:

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)

我愿意打赌它也可以为数组创建一个类似的扩展

答案 12 :(得分:3)

快捷键5

如果您想获取某个键的特定值,则可以使用以下扩展,该扩展使用Bundle中的 infoDictionary 属性。

Bundle.main.infoDictionary可用于获取表单字典中的所有info.plist值,因此我们可以使用Bundle上的object(forInfoDictionaryKey: key)方法直接查询

extension Bundle {
    static func infoPlistValue(forKey key: String) -> Any? {
        guard let value = Bundle.main.object(forInfoDictionaryKey: key) else {
           return nil
        }
        return value
    }
}

用法

guard let apiURL = Bundle.infoPlistValue(forKey: "API_URL_KEY") as? String else { return }

答案 13 :(得分:2)

就我而言,我创建了一个名为 NSDictionary appSettings并添加了所有需要的密钥。对于这种情况,解决方案是:

if let dict = NSBundle.mainBundle().objectForInfoDictionaryKey("appSettings") {
  if let configAppToken = dict["myKeyInsideAppSettings"] as? String {

  }
}

答案 14 :(得分:2)

我创建了一个简单的Dictionary初始值设定项,用于替换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)

答案 15 :(得分:2)

Swift 3.0

if let path = Bundle.main.path(forResource: "config", ofType: "plist") {
    let dict = NSDictionary(contentsOfFile: path)

    // use dictionary
}

在我看来这是最简单的方法。

答案 16 :(得分:2)

Here is a bit shorter version, based on @connor 's answer

guard let path = Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist"),
    let myDict = NSDictionary(contentsOfFile: path) else {
    return nil
}

let value = dict.value(forKey: "CLIENT_ID") as! String?

答案 17 :(得分:2)

您可以使用它,我在github https://github.com/DaRkD0G/LoadExtension

中为Dictionary创建一个简单的扩展名
extension Dictionary {
    /**
        Load a Plist file from the app bundle into a new dictionary

        :param: File name
        :return: Dictionary<String, AnyObject>?
    */
    static func loadPlistFromProject(filename: String) -> Dictionary<String, AnyObject>? {

        if let path = NSBundle.mainBundle().pathForResource("GameParam", ofType: "plist") {
            return NSDictionary(contentsOfFile: path) as? Dictionary<String, AnyObject>
        }
        println("Could not find file: \(filename)")
        return nil
    }
}

您可以将其用于加载

/**
  Example function for load Files Plist

  :param: Name File Plist
*/
func loadPlist(filename: String) -> ExampleClass? {
    if let dictionary = Dictionary<String, AnyObject>.loadPlistFromProject(filename) {
        let stringValue = (dictionary["name"] as NSString)
        let intergerValue = (dictionary["score"] as NSString).integerValue
        let doubleValue = (dictionary["transition"] as NSString).doubleValue

        return ExampleClass(stringValue: stringValue, intergerValue: intergerValue, doubleValue: doubleValue)
    }
    return nil
}

答案 18 :(得分:1)

第1步:在swift 3 +中解析plist的简单快捷方法

extension Bundle {

    func parsePlist(ofName name: String) -> [String: AnyObject]? {

        // check if plist data available
        guard let plistURL = Bundle.main.url(forResource: name, withExtension: "plist"),
            let data = try? Data(contentsOf: plistURL)
            else {
                return nil
        }

        // parse plist into [String: Anyobject]
        guard let plistDictionary = try? PropertyListSerialization.propertyList(from: data, options: [], format: nil) as? [String: AnyObject] else {
            return nil
        }

        return plistDictionary
    }
}

第2步:使用方法:

Bundle().parsePlist(ofName: "Your-Plist-Name")

答案 19 :(得分:1)

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

<?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 propertyListForamt =  PropertyListSerialization.PropertyListFormat.xml
        do {
            plistData = try PropertyListSerialization.propertyList(from: plistXML, options: .mutableContainersAndLeaves, format: &propertyListForamt) as! [[String:AnyObject]]

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

答案 20 :(得分:0)

Swift 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
        }
    }
}

答案 21 :(得分:0)

我使用swift词典但是在我的文件管理器类中将它们转换为NSDictionaries和从NSDictionaries转换为:

    func writePlist(fileName:String, myDict:Dictionary<String, AnyObject>){
        let docsDir:String = dirPaths[0] as String
        let docPath = docsDir + "/" + fileName
        let thisDict = myDict as NSDictionary
        if(thisDict.writeToFile(docPath, atomically: true)){
            NSLog("success")
        } else {
            NSLog("failure")
        }

    }
    func getPlist(fileName:String)->Dictionary<String, AnyObject>{
        let docsDir:String = dirPaths[0] as String
        let docPath = docsDir + "/" + fileName
        let thisDict = NSDictionary(contentsOfFile: docPath)
        return thisDict! as! Dictionary<String, AnyObject>
    }

这似乎是最不麻烦的读写方式,但让我的其余代码保持尽可能快。

答案 22 :(得分:0)

由于尚无此答案,只想指出您还可以使用infoDictionary属性将info plist作为字典Bundle.main.infoDictionary来获取。

如果您只对信息列表中的特定项目感兴趣,则类似Bundle.main.object(forInfoDictionaryKey: kCFBundleNameKey as String) 的速度可能会更快。

// Swift 4

// Getting info plist as a dictionary
let dictionary = Bundle.main.infoDictionary

// Getting the app display name from the info plist
Bundle.main.infoDictionary?[kCFBundleNameKey as String]

// Getting the app display name from the info plist (another way)
Bundle.main.object(forInfoDictionaryKey: kCFBundleNameKey as String)

答案 23 :(得分:0)

这是我找到的解决方案:

let levelBlocks = NSDictionary(contentsOfFile: NSBundle.mainBundle().pathForResource("LevelBlocks", ofType: "plist"))
let test: AnyObject = levelBlocks.objectForKey("Level1")
println(test) // Prints the value of test

我将test的类型设置为AnyObject,以消除可能发生意外推断的警告。

此外,它必须在类方法中完成。

访问并保存已知类型的特定值:

let value = levelBlocks.objectForKey("Level1").objectForKey("amount") as Int
println(toString(value)) // Converts value to String and prints it

答案 24 :(得分:0)

如果你有Info.plist,那么使用

Bundle.main.infoDictionary

答案 25 :(得分:-2)

简单的 struct 来访问plist文件(Swift 2.0)

struct Configuration {      
  static let path = NSBundle.mainBundle().pathForResource("Info", ofType: "plist")!
  static let dict = NSDictionary(contentsOfFile: path) as! [String: AnyObject]

  static let someValue = dict["someKey"] as! String
}

用法:

print("someValue = \(Configuration.someValue)")