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.