Come ottengo un plist come dizionario in Swift?


197

Sto giocando con il nuovo linguaggio di programmazione Swift di Apple e ho dei problemi ...

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

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

Come ottengo 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 posso ottenere il contenuto come dizionario?

Anche una domanda più generale:

È corretto utilizzare le classi NS * predefinite ? Penso di sì ... o mi sto perdendo qualcosa? Per quanto ne so, le classi NS * del framework di default sono ancora valide e vanno bene?


La risposta non è più valida, potresti selezionare la risposta di Ashok?
RodolfoAntonici,

Risposte:


51

In rapida lettura 3.0 da 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)")
        }
    }

Maggiori informazioni COME USARE LISTE DI PROPRIETÀ (.PLIST) IN SWIFT .


Askok. Ho perso molte ore cercando di trovare una risposta a questo oggi su questo! GRAZIE!! Questo ha funzionato perfettamente !!!
user3069232

281

Puoi comunque usare NSDictionaries in Swift:

Per Swift 4

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

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 il focus su swift presto, ma attualmente le API veloci non hanno tutte le funzionalità dei core NSClasses.


hmm quando provo a usare quel codice che hai fornito, ottengo l'errore: xxx non ha un membro di nome dict
KennyVB

funziona benissimo nel parco giochi. semplicemente non nel mio documento rapido
KennyVB

come appare se Array?
Arnlee Vizcayno,

Sembra che mainBundle()sia solo mainin Swift 3
BallpointBen

8
Questa risposta è obsoleta. Anche in Swift 3 non dovresti usare NSArray/NSDictionaryaffatto per leggere i dati dell'elenco delle proprietà. PropertyListSerialization(e in Swift 4 in alternativa il Codableprotocollo) è l'API appropriata. Fornisce una moderna gestione degli errori e i dati possono essere convertiti direttamente in tipi di raccolta Swift nativi.
Vadian

141

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<String, AnyObject> {
    // 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
}

3
Penso che questa sia la risposta "più corretta" del gruppo fino a quando non c'è un modo rapido nativo per farlo.
DudeOnRock,

1
Questa risposta è obsoleta. In Swift 3 non dovresti usare NSArray/NSDictionaryaffatto per leggere i dati dell'elenco delle proprietà. PropertyListSerialization(e in Swift 4 in alternativa il Codableprotocollo) è l'API appropriata. Fornisce una moderna gestione degli errori e i dati possono essere convertiti direttamente in tipi di raccolta Swift nativi.
Vadian

47

Swift 4.0

Ora puoi usare il protocollo Decodable per decodificare un .plist in una struttura personalizzata. Esaminerò un esempio di base, per strutture .plist più complicate che consiglio di leggere su Decodable / Encodable (una buona risorsa è qui: https://benscheirman.com/2017/06/swift-json/ ).

Prima di tutto configura la struttura nel formato del tuo file .plist. Per questo esempio considererò un .plist con un dizionario di livello radice e 3 voci: 1 stringa con chiave "nome", 1 Int con chiave "età" e 1 valore booleano con chiave "singola". 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 classe 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 altro codice di cui preoccuparsi, ed è tutto in Swift. Meglio ancora ora abbiamo un'istanza della struttura di configurazione che possiamo facilmente usare:

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

Stampa il valore per le chiavi "name", "age" e "single" nella .plist.


1
Questa è la risposta migliore per Swift 4. Ma perché no Bundle.main.url(forResource: "Config", withExtension: "plist")e liberarsene URL(fileURLWithPath? E poiché il file deve esistere (in fase di progettazione / compilazione), tutti i valori possono essere forzati da scartare. Il codice non deve arrestarsi in modo anomalo se tutto è progettato correttamente.
Vadian

@vadian Certo che puoi usare url(forResource: "Config", withExtension: "plist")Stavo solo confrontando quello che l'OP ha fatto nel loro codice come punto di confronto. Per quanto riguarda la forza di scartare tutto, cerco di sbagliare dal lato della cautela. Penso che sia una domanda fondamentale per Swift in generale. Preferirei sapere esattamente cosa farà il mio codice in qualsiasi situazione piuttosto che in crash.
ekreloff,

1) Si prega di non adottare cattive abitudini se esiste un'API più appropriata. 2) Questo è uno dei pochi casi in cui un incidente forzato rileva un errore di progettazione. È necessario che qualsiasi file nel bundle sia presente al momento della compilazione e non possa essere modificato in fase di esecuzione poiché tutti i file sono firmati con codice. Ancora una volta: il codice non deve arrestarsi in modo anomalo se tutto è progettato correttamente .
vadian

Sì, conosci il tuo diritto. Non avevo capito che era il caso delle risorse del pacchetto.
ekreloff,

2
@NaveenGeorgeThoppan se usi questo esempio come dizionario, lo sarebbe semplicemente decoder.decode([Config].self, from: data). (Notare le parentesi intorno a [Config])
ekreloff l'

22

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

Esiste una versione sintetica di questo?
harsh_v,

18

Ho lavorato con Swift 3.0 e volevo contribuire con una risposta per la sintassi aggiornata. Inoltre, e forse ancora più importante, sto usando PropertyListSerialization oggetto per eseguire il sollevamento di carichi pesanti, che è molto più flessibile del semplice utilizzo di NSDictionary in quanto consente un array come tipo root del plist.

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

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

Sto usando l' oggetto 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 le esecuzioni di codice di cui sopra plistsaranno di tipo Array<AnyObject>, ma sappiamo che tipo è in realtà, quindi possiamo lanciarlo nel tipo corretto:

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

E ora possiamo accedere alle varie proprietà del nostro array di stringhe: i dizionari di stringhe in modo naturale. Spero di convertirli in strutture o classi realmente fortemente tipizzate;)

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

8

È preferibile utilizzare dizionari e array nativi perché sono stati ottimizzati per l'uso con swift. Detto questo, puoi usare rapidamente le classi NS ... 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 efficiente per accedere a un plist, ma in futuro mi aspetto che apple aggiungerà più funzionalità (come l'uso di plist) nei dizionari nativi.


Per quanto ne sai, l'aggiunta della lettura plist ai dizionari nativi è già avvenuta?
SpacyRicochet,

8

Swift - Lettura / scrittura 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")
    }
}

8

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 plist:

Da un piano personalizzato: - (Crea da File-New-File-Resource-PropertyList. Aggiunte tre stringhe denominate: 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").

Questo è quello che stavo cercando! Grazie compagno!
Jayprakash Dubey,

6

Convertito in una comoda estensione tramite la risposta di 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]
    }
}

utilizzo:

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

Sarei disposto a scommettere che avrebbe funzionato anche per creare un'estensione simile per Array


5

può effettivamente farlo in 1 riga

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

5

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 Dizionario singolo:

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

Se vuoi ottenere un dizionario multidimensionale completo in plist:

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

Ecco la lista:

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

5

Poiché questa risposta non è ancora qui, volevo solo sottolineare che puoi anche utilizzare la proprietà infoDictionary per ottenere la scheda informazioni come dizionario, Bundle.main.infoDictionary .

Anche se qualcosa del genere Bundle.main.object(forInfoDictionaryKey: kCFBundleNameKey as String) potrebbe essere più veloce se sei interessato solo a un elemento specifico nella scheda informazioni.

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

3

nel mio caso creo una NSDictionarychiamata appSettingse aggiungo tutte le chiavi necessarie. In questo caso, la soluzione è:

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

  }
}

Grazie. objectForInfoDictionaryKeyera esattamente quello che stavo cercando.
LunaCodeGirl

2

Puoi usarlo, creo una semplice estensione per Dictionary 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<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
    }
}

E puoi usarlo per caricare

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

2

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?

2

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 secondo me.


2

Ho creato un semplice Dictionaryinizializzatore 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 così:

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

2

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

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

1

Passaggio 1 : modo semplice e veloce per analizzare il programma in rapido 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
    }
}

Passaggio 2: come usare:

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

0

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 testaAnyObject per silenziare un avvertimento su un'inferenza inattesa che potrebbe verificarsi.

Inoltre, deve essere fatto in un metodo di classe.

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

0

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

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

Questo sembra il modo meno preoccupante per leggere e scrivere, ma lasciamo che il resto del mio codice rimanga il più veloce possibile.


0

Plist è un semplice enumeratore Swift che ho creato per lavorare con elenchi 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>() -> 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)"
        }
    }
}

0

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

-2

Struct semplice 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)")
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.