tutorial the programming objective language curso apple ios swift

ios - objective - the swift programming language pdf



¿Cómo obtengo un plist como diccionario en Swift? (24)

Estoy jugando con el nuevo lenguaje de programación Swift de Apple y tengo algunos problemas ...

Actualmente estoy tratando de leer un archivo plist, en Objective-C. Haría lo siguiente para obtener el contenido como NSDictionary:

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

¿Cómo obtengo un plist como diccionario en Swift?

Supongo que puedo obtener el camino al plist con:

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

Cuando esto funciona (¿es correcto?): ¿Cómo obtengo el contenido como un diccionario?

También una pregunta más general:

¿Está bien usar las clases predeterminadas NS * ? Creo que sí ... ¿o me estoy perdiendo algo? Por lo que yo sé, las clases NS * del marco predeterminado siguen siendo válidas y están bien de usar?


Swift 4.0

Ahora puede usar el protocolo Decodable para decodificar una .plist en una estructura personalizada. Voy a repasar un ejemplo básico, para estructuras .plist más complicadas, recomiendo leer en Decodable / Encodable (un buen recurso está aquí: https://benscheirman.com/2017/06/swift-json/ ).

Primero configure su estructura en el formato de su archivo .plist. Para este ejemplo consideraré una .plist con un diccionario de nivel de raíz y 3 entradas: 1 cadena con la clave "nombre", 1 int con clave "edad" y 1 booleano con clave "única". Aquí está la estructura:

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

Suficientemente simple. Ahora la parte genial. Usando la clase PropertyListDecoder podemos analizar fácilmente el archivo .plist en una instancia de esta estructura:

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

No hay mucho más de qué preocuparse, y todo está en Swift. Mejor aún, ahora tenemos una instancia de la estructura de configuración que podemos usar fácilmente:

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

Esto imprime el valor de las teclas "nombre", "edad" y "simple" en .plist.


Swift 3.0

si quieres leer una "matriz bidimensional" de .plist, puedes intentarlo así:

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


Aquí está la solución que encontré:

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

Establecí el tipo de test en AnyObject para silenciar una advertencia sobre una inferencia inesperada que podría ocurrir.

Además, tiene que hacerse en un método de clase.

Para acceder y guardar un valor específico de un tipo conocido:

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


Aquí hay una versión más corta, basada en la respuesta de @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?


Convertido en una extensión de conveniencia a través de la respuesta de 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] } }

uso:

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

Estaría dispuesto a apostar que también funcionaría crear una extensión similar para Arrays


Creé un inicializador de Dictionary simple que reemplaza NSDictionary(contentsOfFile: path) . Solo elimina el 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 } }

Puedes usarlo así:

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


En veloz 3.0 Lectura de 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)") } }

Leer más CÓMO UTILIZAR LISTAS DE PROPIEDADES (.PLIST) EN SWIFT .


Esta respuesta utiliza objetos nativos Swift en lugar de 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"] ?? ""


Esto es lo que hago si quiero convertir un .plist a un diccionario 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 } }

Editado para Swift 2.0:

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

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


Estructura simple para acceder al archivo 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)")


He estado trabajando con Swift 3.0 y quería contribuir con una respuesta para la sintaxis actualizada. Además, y posiblemente más importante, estoy usando el objeto PropertyListSerialization para hacer el trabajo pesado, que es mucho más flexible que el simple uso de NSDictionary, ya que permite una matriz como el tipo de raíz del plist.

A continuación se muestra una captura de pantalla de la plist que estoy usando. Es un poco complicado, para mostrar la potencia disponible, pero esto funcionará para cualquier combinación permitida de tipos plist.

Como puede ver, estoy usando un conjunto de diccionarios Array of String: String para almacenar una lista de nombres de sitios web y su URL correspondiente.

Estoy utilizando el objeto PropertyListSerialization , como se mencionó anteriormente, para hacer el trabajo pesado por mí. Además, Swift 3.0 se ha vuelto más "Swifty" por lo que todos los nombres de los objetos han perdido el prefijo "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)

Después de ejecutar el código anterior, plist será del tipo Array<AnyObject> , pero sabemos qué tipo es realmente para que podamos convertirlo al tipo correcto:

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

Y ahora podemos acceder a las diversas propiedades de nuestro Array of String: String Dictionaries de forma natural. Esperemos poder convertirlos en estructuras o clases realmente fuertemente tipadas;)

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


Lo mejor es utilizar diccionarios y matrices nativos porque se han optimizado para usar con rapidez. Dicho esto, puedes usar NS ... clases en poco tiempo y creo que esta situación lo justifica. Así es como lo implementarías:

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

Hasta ahora (en mi opinión) esta es la forma más fácil y más eficiente de acceder a un plist, pero en el futuro espero que Apple agregue más funcionalidades (como el uso de plist) a los diccionarios nativos.


Puede leer plist en SWIFT Language de esta manera:

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

Leer el valor de Single Dictionary:

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

Si quieres obtener un diccionario multidimensional completo en plist:

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

Aquí está el 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>


Puede usar eso, creo una extensión simple para Dictionary en 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 } }

Y puedes usar eso para cargar

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


Swift 4.0 lista de iOS 11.2.6 analizada y el código para analizarlo, basado en respuesta anterior.

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


Todavía puede usar NSDictionaries en Swift:

Para Swift 3+

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

Y versiones anteriores de 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 }

Las NSClasses todavía están disponibles y son perfectas para usar en Swift. Creo que probablemente quieran cambiar el foco rápidamente, pero actualmente las rápidas API no tienen toda la funcionalidad de las NSClasses centrales.


Utilizo diccionarios rápidos, pero los convierto en y desde NSDictionaries en mi clase de administrador de archivos de esta manera:

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

Esta parece ser la forma menos problemática de leer y escribir, pero permitamos que el resto de mi código se mantenga lo más rápido posible.


en mi caso, creo un NSDictionary llamado appSettings y agrego todas las claves necesarias. Para este caso, la solución es:

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


en realidad puede hacerlo en 1 línea

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


Plist es una simple enumeración de Swift que hice para trabajar con listas de propiedades.

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

Más ejemplos:

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 en sí es bastante simple, aquí está su listado en caso de que lo recomiende directamente.

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


Paso 1 : forma simple y rápida de analizar plist en 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 } }

Paso 2: Cómo usar:

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


Swift - Lectura / Escritura plist y archivo de texto ....

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: Acceder a Info.Plist

Tengo un diccionario llamado CoachMarksDictionary con un valor booleano en Info.Plist. Quiero acceder al valor de bool y hacerlo verdadero.

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

Escribiendo a Plist:

Desde una Lista personalizada: - (Hacer desde Archivo-Nuevo-Archivo-Recurso-Lista de propiedades. Se agregaron tres cadenas llamadas: Tablero_de_tabla, Nuevo, Tablero_de_tabla, Tablero_de_tabla)

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

El método se puede llamar como

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


Swift 3.0

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

La forma más fácil de hacer esto en mi opinión.