【问题标题】:How do I get a plist as a Dictionary in Swift?如何在 Swift 中将 plist 作为字典?
【发布时间】:2014-07-25 14:00:15
【问题描述】:

我正在使用 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* 类仍然有效且可以使用?

【问题讨论】:

  • 答案不再有效,能否请您选择Ashok的答案?
  • 建议澄清这个问题与Bundle有关。那是因为解决方案明显不同 Bundle 或非 Bundle。

标签: ios swift


【解决方案1】:

你仍然可以在 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 中使用。我认为他们可能希望尽快将重点转移到 swift,但目前 swift API 不具备核心 NSClass 的所有功能。

【讨论】:

  • 嗯,当我尝试使用您提供的代码时,我收到错误消息:xxx 没有名为 dict 的成员
  • 它在操场上运行良好。只是不在我的快速文档中
  • 如果 Array 看起来怎么样?
  • 在 Swift 3 中看起来 mainBundle() 只是 main
  • 这个答案已经过时了。即使在 Swift 3 中,您也不应该使用 NSArray/NSDictionary 来读取属性列表数据。 PropertyListSerialization(在 Swift 4 中或者是 Codable 协议)是合适的 API。它提供现代错误处理,并且可以将数据直接转换为原生 Swift 集合类型。
【解决方案2】:

最好使用本机字典和数组,因为它们已针对 swift 进行了优化。话虽这么说,您可以快速使用 NS... 类,我认为这种情况是值得的。以下是您将如何实现它:

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

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

【讨论】:

  • 据你所知,在原生词典中添加plist阅读已经发生了吗?
【解决方案3】:

实际上可以在 1 行中完成

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

【讨论】:

    【解决方案4】:

    这是我找到的解决方案:

    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
    

    【讨论】:

      【解决方案5】:

      您可以通过这种方式阅读 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 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>
      

      【讨论】:

        【解决方案6】:

        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")
            }
        }
        

        【讨论】:

          【解决方案7】:

          如果我想将 .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
          }
          

          【讨论】:

          • 我认为这是一堆“最正确”的答案,直到有一种本地快速的方法来做到这一点。
          • 这个答案已经过时了。在 Swift 3 中,您不应该使用 NSArray/NSDictionary 来读取属性列表数据。 PropertyListSerialization(在 Swift 4 中或者是 Codable 协议)是合适的 API。它提供现代错误处理,并且可以将数据直接转换为原生 Swift 集合类型。
          【解决方案8】:

          你可以使用它,我在 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
          }
          

          【讨论】:

            【解决方案9】:

            我使用 swift 字典,但在我的文件管理器类中将它们与 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>
                }
            

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

            【讨论】:

              【解决方案10】:

              简单的 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)")
              

              【讨论】:

                【解决方案11】:

                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").
                

                【讨论】:

                • 这就是我一直在寻找的!谢谢哥们!
                【解决方案12】:

                我一直在使用 Swift 3.0,并希望为更新的语法提供答案。此外,可能更重要的是,我使用 PropertyListSerialization 对象来完成繁重的工作,这比仅使用 NSDictionary 灵活得多,因为它允许将 Array 作为 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&lt;AnyObject&gt; 类型,但我们知道它到底是什么类型,所以我们可以将它转换为正确的类型:

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

                现在我们可以以自然的方式访问我们的字符串数组的各种属性:字符串字典。 希望将它们转换为实际的强类型结构或类;)

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

                【讨论】:

                  【解决方案13】:

                  swift 3.0 从 Plist 中读取。

                  func readPropertyList() {
                          var propertyListFormat =  PropertyListSerialization.PropertyListFormat.xml //Format of the Property List.
                          var plistData: [String: AnyObject] = [:] //Our data
                          let plistPath: String? = Bundle.main.path(forResource: "data", ofType: "plist")! //the path of the data
                          let plistXML = FileManager.default.contents(atPath: plistPath!)!
                          do {//convert the data to a dictionary and handle errors.
                              plistData = try PropertyListSerialization.propertyList(from: plistXML, options: .mutableContainersAndLeaves, format: &propertyListFormat) as! [String:AnyObject]
                  
                          } catch {
                              print("Error reading plist: \(error), format: \(propertyListFormat)")
                          }
                      }
                  

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

                  【讨论】:

                  • 问。今天花了很多时间试图在这个问题上找到答案!谢谢!!这工作得很好!!!
                  【解决方案14】:

                  在我的例子中,我创建了一个名为 appSettingsNSDictionary 并添加了所有需要的密钥。对于这种情况,解决方案是:

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

                  【讨论】:

                  • 谢谢。 objectForInfoDictionaryKey 正是我想要的。
                  【解决方案15】:

                  这是一个较短的版本,基于@connor 的回答

                  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?
                  

                  【讨论】:

                    【解决方案16】:

                    Plist 是我为处理属性列表而制作的一个简单的 Swift 枚举。

                    // load an applications info.plist data
                    
                    let info = Plist(NSBundle.mainBundle().infoDictionary)
                    let identifier = info["CFBundleIndentifier"].string!
                    

                    更多示例:

                    import Plist
                    
                    // initialize using an NSDictionary
                    // and retrieve keyed values
                    
                    let info = Plist(dict)
                    let name = info["name"].string ?? ""
                    let age = info["age"].int ?? 0
                    
                    
                    // initialize using an NSArray
                    // and retrieve indexed values
                    
                    let info = Plist(array)
                    let itemAtIndex0 = info[0].value
                    
                    
                    // utility initiaizer to load a plist file at specified path
                    let info = Plist(path: "path_to_plist_file")
                    
                    // we support index chaining - you can get to a dictionary from an array via
                    // a dictionary and so on
                    // don't worry, the following will not fail with errors in case
                    // the index path is invalid
                    if let complicatedAccessOfSomeStringValueOfInterest = info["dictKey"][10]["anotherKey"].string {
                      // do something
                    }
                    else {
                      // data cannot be indexed
                    }
                    
                    // you can also re-use parts of a plist data structure
                    
                    let info = Plist(...)
                    let firstSection = info["Sections"][0]["SectionData"]
                    let sectionKey = firstSection["key"].string!
                    let sectionSecret = firstSection["secret"].int!
                    

                    Plist.swift

                    Plist 本身很简单,以下是它的清单,以供您直接参考。

                    //
                    //  Plist.swift
                    //
                    
                    
                    import Foundation
                    
                    
                    public enum Plist {
                    
                        case dictionary(NSDictionary)
                        case Array(NSArray)
                        case Value(Any)
                        case none
                    
                        public init(_ dict: NSDictionary) {
                            self = .dictionary(dict)
                        }
                    
                        public init(_ array: NSArray) {
                            self = .Array(array)
                        }
                    
                        public init(_ value: Any?) {
                            self = Plist.wrap(value)
                        }
                    
                    }
                    
                    
                    // MARK:- initialize from a path
                    
                    extension Plist {
                    
                        public init(path: String) {
                            if let dict = NSDictionary(contentsOfFile: path) {
                                self = .dictionary(dict)
                            }
                            else if let array = NSArray(contentsOfFile: path) {
                                self = .Array(array)
                            }
                            else {
                                self = .none
                            }
                        }
                    
                    }
                    
                    
                    // MARK:- private helpers
                    
                    extension Plist {
                    
                        /// wraps a given object to a Plist
                        fileprivate static func wrap(_ object: Any?) -> Plist {
                    
                            if let dict = object as? NSDictionary {
                                return .dictionary(dict)
                            }
                            if let array = object as? NSArray {
                                return .Array(array)
                            }
                            if let value = object {
                                return .Value(value)
                            }
                            return .none
                        }
                    
                        /// tries to cast to an optional T
                        fileprivate func cast<T>() -> T? {
                            switch self {
                            case let .Value(value):
                                return value as? T
                            default:
                                return nil
                            }
                        }
                    }
                    
                    // MARK:- subscripting
                    
                    extension Plist {
                    
                        /// index a dictionary
                        public subscript(key: String) -> Plist {
                            switch self {
                    
                            case let .dictionary(dict):
                                let v = dict.object(forKey: key)
                                return Plist.wrap(v)
                    
                            default:
                                return .none
                            }
                        }
                    
                        /// index an array
                        public subscript(index: Int) -> Plist {
                            switch self {
                            case let .Array(array):
                                if index >= 0 && index < array.count {
                                    return Plist.wrap(array[index])
                                }
                                return .none
                    
                            default:
                                return .none
                            }
                        }
                    
                    }
                    
                    
                    // MARK:- Value extraction
                    
                    extension Plist {
                    
                        public var string: String?       { return cast() }
                        public var int: Int?             { return cast() }
                        public var double: Double?       { return cast() }
                        public var float: Float?         { return cast() }
                        public var date: Date?         { return cast() }
                        public var data: Data?         { return cast() }
                        public var number: NSNumber?     { return cast() }
                        public var bool: Bool?           { return cast() }
                    
                    
                        // unwraps and returns the underlying value
                        public var value: Any? {
                            switch self {
                            case let .Value(value):
                                return value
                            case let .dictionary(dict):
                                return dict
                            case let .Array(array):
                                return array
                            case .none:
                                return nil
                            }
                        }
                    
                        // returns the underlying array
                        public var array: NSArray? {
                            switch self {
                            case let .Array(array):
                                return array
                            default:
                                return nil
                            }
                        }
                    
                        // returns the underlying dictionary
                        public var dict: NSDictionary? {
                            switch self {
                            case let .dictionary(dict):
                                return dict
                            default:
                                return nil
                            }
                        }
                    
                    }
                    
                    
                    // MARK:- CustomStringConvertible
                    
                    extension Plist : CustomStringConvertible {
                        public var description:String {
                            switch self {
                            case let .Array(array): return "(array \(array))"
                            case let .dictionary(dict): return "(dict \(dict))"
                            case let .Value(value): return "(value \(value))"
                            case .none: return "(none)"
                            }
                        }
                    }
                    

                    【讨论】:

                      【解决方案17】:

                      Swift 3.0

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

                      我认为最简单的方法。

                      【讨论】:

                        【解决方案18】:

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

                        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)
                        

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

                        【讨论】:

                        • 这是一个不错的方法,除了崩溃。如果我没记错的话,如果解码失败,NSDictionary 上的原始 ObjC 方法只会返回 nil。
                        【解决方案19】:

                        这个答案使用 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"] ?? ""
                        

                        【讨论】:

                        • 这个有简洁的版本吗?
                        【解决方案20】:

                        斯威夫特 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】:

                          第 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")
                          

                          【讨论】:

                            【解决方案22】:

                            我创建了一个简单的 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)
                            

                            【讨论】:

                              【解决方案23】:

                              斯威夫特 4.0

                              您现在可以使用 Decodable 协议将 .plist 解码为自定义结构。我将介绍一个基本示例,对于更复杂的 .plist 结构,我建议阅读 Decodable/Encodable (这里有一个很好的资源:@​​987654321@)。

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

                              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”键的值。

                              【讨论】:

                              • 这是 Swift 4 的最佳答案。但为什么不 Bundle.main.url(forResource: "Config", withExtension: "plist") 并摆脱 URL(fileURLWithPath 呢?并且由于文件需要存在(在设计/编译时),所有值都可以强制解包。如果一切设计得当,代码一定不会崩溃。
                              • @vadian 当然你可以使用url(forResource: "Config", withExtension: "plist") 我只是在匹配 OP 在他们的代码中所做的作为比较点。至于强制解开所有东西,我尽量谨慎行事。我认为这是 Swift 的一个基本问题。我宁愿确切地知道我的代码在任何情况下都会做什么而不是崩溃。
                              • 1) 如果有更合适的API,请不要养成不良习惯。 2) 这是强制崩溃发现设计错误的少数情况之一。捆绑包中的任何文件都必须在编译时存在,并且不能在运行时更改,因为所有文件都是代码签名的。再说一遍:如果一切设计得当,代码一定不会崩溃
                              • 是的,你知道你的权利。没有意识到Bundle资源就是这种情况。
                              • @NaveenGeorgeThoppan 如果你使用这个例子作为你的字典,那么它就是decoder.decode([Config].self, from: data)。 (注意 [Config] 周围的括号)
                              【解决方案24】:

                              根据上面的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 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")
                                  }
                              

                              【讨论】:

                                【解决方案25】:

                                由于还没有这个答案,只是想指出您还可以使用 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)
                                

                                【讨论】:

                                  【解决方案26】:

                                  斯威夫特 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 }
                                  

                                  【讨论】:

                                    【解决方案27】:

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

                                    Bundle.main.infoDictionary
                                    

                                    【讨论】:

                                      猜你喜欢
                                      • 1970-01-01
                                      • 2019-05-04
                                      • 1970-01-01
                                      • 2016-03-11
                                      • 1970-01-01
                                      • 2016-11-02
                                      • 1970-01-01
                                      • 2015-05-01
                                      • 1970-01-01
                                      相关资源
                                      最近更新 更多