Come faccio a ottenere un plist come dizionario in Swift?

Sto giocando con il nuovo linguaggio di programmazione Swift di Apple e ho alcuni problemi …

Attualmente sto cercando di leggere un file plist, in Objective-C farei quanto segue per ottenere il contenuto come un NSDictionary:

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

Come faccio a ottenere un plist come dizionario in Swift?

Presumo di poter ottenere il percorso per il plist con:

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

Quando funziona (Se è corretto?): Come ottengo il contenuto come dizionario?

Anche una domanda più generale:

Va bene usare le classi NS * predefinite? Penso di sì … o mi sto perdendo qualcosa? Per quanto ne so, le classi di framework NS * predefinite sono ancora valide e accettabili?

Puoi comunque utilizzare NSDictionaries in Swift:

Per Swift 3+

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

E versioni precedenti di 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 } 

Le NSClasses sono ancora disponibili e perfettamente utilizzabili in Swift. Penso che probabilmente vorranno spostare rapidamente l’attenzione su rapidamente, ma attualmente le API rapide non hanno tutte le funzionalità dei NSClasses di base.

Questo è quello che faccio se voglio convertire un .plist in un dizionario Swift:

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

Modificato per Swift 2.0:

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

Modificato per 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 } 

Nella lettura veloce di 3.0 da Plist.

 func readPropertyList() { var propertyListForamt = 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: &propertyListForamt) as! [String:AnyObject] } catch { print("Error reading plist: \(error), format: \(propertyListForamt)") } } 

Ulteriori informazioni COME USARE LIST DI PROPRIETÀ (.PLIST) IN SWIFT .

Questa risposta utilizza oggetti nativi Swift piuttosto che 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"] ?? "" 

Ho lavorato con Swift 3.0 e volevo fornire una risposta per la syntax aggiornata. Inoltre, e forse ancora più importante, sto usando l’object PropertyListSerialization per eseguire il sollevamento pesante, che è molto più flessibile dell’utilizzo del NSDictionary in quanto consente una matrice come tipo di radice del plist.

Di seguito è riportato uno screenshot del plist che sto utilizzando. È un po ‘ complicato, in modo da mostrare la potenza disponibile, ma funzionerà per qualsiasi combinazione consentita di tipi plist.

Esempio di file plist Come puoi vedere, sto usando un array di stringhe: dizionari String per memorizzare un elenco di nomi di siti web e il loro URL corrispondente.

Sto usando l’object PropertyListSerialization , come menzionato sopra, per fare il lavoro pesante per me. Inoltre, Swift 3.0 è diventato più “Swifty”, quindi tutti i nomi degli oggetti hanno perso il prefisso “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) 

Dopo che il codice di cui sopra è stato eseguito, plist sarà di tipo Array , ma sappiamo che tipo è veramente così possiamo lanciarlo sul tipo corretto:

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

E ora possiamo accedere alle varie proprietà della nostra matrice di stringhe: i dizionari string in modo naturale. Si spera di convertirli in strutture o classi fortemente tipizzate;)

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

Swift 4.0

È ora ansible utilizzare il protocollo Decodable per decodificare un file .plist in una struttura personalizzata. Troverò un esempio di base, per strutture più semplici e complicate che consiglio di leggere su Decodable / Encodable (una buona risorsa è qui: https://benscheirman.com/2017/06/swift-json/ ).

Prima configura la tua struct nel formato del tuo file .plist. Per questo esempio prenderò in considerazione un .plist con un dizionario di livello radice e 3 voci: 1 stringa con chiave “nome”, 1 int con chiave “età” e 1 booleana con chiave “singolo”. Ecco la struttura:

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

Abbastanza semplice Ora la parte interessante. Usando la class PropertyListDecoder possiamo facilmente analizzare il file .plist in un’istanza di questa struttura:

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

Non c’è molto più codice di cui preoccuparsi, ed è tutto in Swift. Meglio ancora ora abbiamo un’istanza della struttura di Config che possiamo facilmente usare:

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

Questo stampa il valore per i tasti “nome”, “età” e “singolo” nel file .plist.

È meglio usare i dizionari e gli array nativi perché sono stati ottimizzati per l’uso con swift. Detto questo puoi usare NS … lezioni in fretta e penso che questa situazione lo giustifichi. Ecco come lo implementeresti:

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

Finora (secondo me) questo è il modo più semplice ed efficace per accedere a un plist, ma in futuro prevedo che Apple aggiungerà più funzionalità (come l’uso di plist) nei dizionari nativi.

Swift – Leggi / scrivi plist e file di testo ….

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

Swift 2.0: accesso a Info.Plist

Ho un dizionario chiamato CoachMarksDictionary con un valore booleano in Info.Plist. Voglio accedere al valore bool e renderlo vero.

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

Scrivere per plistare:

Da un Plist personalizzato: – (Ricava da File-Nuovo-File-Resource-PropertyList. Aggiunte tre stringhe chiamate: 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) } 

Il metodo può essere chiamato come

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

può effettivamente farlo in 1 linea

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

Puoi leggere plist in SWIFT Language in questo modo:

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

Leggi il valore del singolo dizionario:

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

Se vuoi ottenere il dizionario multidimensionale completo in plist:

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

Ecco il plist:

   index2  date 20140610 amount 110  index1  amount 125 date 20140212    

Convertito in un’estensione di convenienza tramite la risposta di Nick:

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

utilizzo:

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

Sarei disposto a scommettere che sarebbe anche utile creare un’estensione simile per gli array

Puoi usarlo, creo una semplice estensione per Dizionario in github https://github.com/DaRkD0G/LoadExtension

 extension Dictionary { /** Load a Plist file from the app bundle into a new dictionary :param: File name :return: Dictionary? */ static func loadPlistFromProject(filename: String) -> Dictionary? { if let path = NSBundle.mainBundle().pathForResource("GameParam", ofType: "plist") { return NSDictionary(contentsOfFile: path) as? Dictionary } println("Could not find file: \(filename)") return nil } } 

E puoi usarlo per caricare

 /** Example function for load Files Plist :param: Name File Plist */ func loadPlist(filename: String) -> ExampleClass? { if let dictionary = Dictionary.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 } 

nel mio caso creo un NSDictionary chiamato appSettings e aggiungo tutte le chiavi necessarie. Per questo caso, la soluzione è:

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

Ecco una versione un po ‘più breve, basata sulla risposta di @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? 

Swift 3.0

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

Il modo più semplice per farlo a mio parere.

Ho creato un semplice inizializzatore del Dictionary che sostituisce NSDictionary(contentsOfFile: path) . Basta rimuovere il 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 } } 

Puoi usarlo in questo modo:

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

Step 1 : modo semplice e veloce di analizzare plist in swift 3+

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

Passo 2: Come usare:

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

Ecco la soluzione che ho trovato:

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

Ho impostato il tipo di test su AnyObject per AnyObject un avviso relativo a un’inferenza inattesa che potrebbe verificarsi.

Inoltre, deve essere fatto in un metodo di class.

Per accedere e salvare un valore specifico di un tipo noto:

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

Uso dizionari swift ma li converto da e verso NSDictionaries nella mia class di file manager in questo modo:

  func writePlist(fileName:String, myDict:Dictionary){ 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{ let docsDir:String = dirPaths[0] as String let docPath = docsDir + "/" + fileName let thisDict = NSDictionary(contentsOfFile: docPath) return thisDict! as! Dictionary } 

Questo sembra il modo meno problematico per leggere e scrivere, ma il resto del mio codice rimane il più veloce ansible.

Plist è un semplice enumerato Swift che ho fatto per lavorare con le liste di proprietà.

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

Altri esempi:

 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 stesso è abbastanza semplice, ecco il suo elenco nel caso in cui tu faccia riferimento direttamente.

 // // 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? { 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)" } } } 

Swift 3.0

se vuoi leggere un “array bidimensionale” da .plist, puoi provarlo in questo modo:

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

Swift 4.0 elenco iOS 11.2.6 analizzato e codice per analizzarlo, basato su https://stackoverflow.com/users/3647770/ashok-r risposta sopra.

      identity blah-1 major 1 minor 1 uuid f45321 web http://web   identity  major  minor  uuid  web     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") } 

Semplice struct per accedere al file 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 } 

Uso:

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