Je m'amuse avec le nouveau langage de programmation Swift d'Apple et j'ai quelques problèmes ...
Actuellement, j'essaie de lire un fichier plist. Dans Objective-C, je ferais ce qui suit pour obtenir le contenu en tant que NSDictionary:
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"Config" ofType:@"plist"];
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:filePath];
Comment obtenir un plist en tant que dictionnaire dans Swift?
Je suppose que je peux obtenir le chemin du plist avec:
let path = NSBundle.mainBundle().pathForResource("Config", ofType: "plist")
Quand cela fonctionne (si c'est correct?): Comment puis-je obtenir le contenu en tant que dictionnaire?
Aussi une question plus générale:
Est-il possible d’utiliser les classes NS * par défaut? Je pense que oui ... ou est-ce que je manque quelque chose? Autant que je sache, le framework par défaut NS * est-il toujours valide et utilisable?
Dans Swift 3.0 Lecture depuis 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)")
}
}
Lire Plus COMMENT UTILISER LES LISTES DE PROPRIÉTÉS (.PLIST) DANS Swift .
Vous pouvez toujours utiliser NSDictionaries dans Swift:
Pour Swift 4
var nsDictionary: NSDictionary?
if let path = Bundle.main.path(forResource: "Config", ofType: "plist") {
nsDictionary = NSDictionary(contentsOfFile: path)
}
Pour Swift 3+
if let path = Bundle.main.path(forResource: "Config", ofType: "plist"),
let myDict = NSDictionary(contentsOfFile: path){
// Use your myDict here
}
Et les anciennes versions 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
}
Les NSClasses sont toujours disponibles et parfaitement utilisables dans Swift. Je pense qu'ils vont probablement vouloir se concentrer bientôt sur Swift, mais pour le moment, les API Swift ne disposent pas de toutes les fonctionnalités des NSClasses de base.
Voici ce que je fais si je veux convertir une liste .plist en dictionnaire 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
}
}
Édité pour Swift 2.0:
if let path = NSBundle.mainBundle().pathForResource("Config", ofType: "plist"), dict = NSDictionary(contentsOfFile: path) as? [String: AnyObject] {
// use Swift dictionary as normal
}
Edité pour 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
}
Vous pouvez maintenant utiliser le protocole Decodable pour décoder une .plist dans une structure personnalisée. Je vais donner un exemple élémentaire, pour les structures .plist plus compliquées, je recommande de lire Decodable/Encodable (une bonne ressource est ici: https://benscheirman.com/2017/06/Swift-json/ ) .
Commencez par configurer votre structure au format de votre fichier .plist. Pour cet exemple, considérons une liste .plist avec un dictionnaire de niveau racine et 3 entrées: 1 chaîne avec la clé "name", 1 int avec la clé "age" et 1 booléen avec la clé "single". Voici la structure:
struct Config: Decodable {
private enum CodingKeys: String, CodingKey {
case name, age, single
}
let name: String
let age: Int
let single: Bool
}
Assez simple. Maintenant la partie cool. En utilisant la classe PropertyListDecoder, nous pouvons facilement analyser le fichier .plist dans une instanciation de cette structure:
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)
}
Pas beaucoup plus de code à craindre, et tout est dans Swift. Mieux encore, nous avons maintenant une instanciation de la structure de configuration que nous pouvons facilement utiliser:
let config = parseConfig()
print(config.name)
print(config.age)
print(config.single)
Ceci imprime la valeur des clés "name", "age" et "single" dans la liste .plist.
Cette réponse utilise des objets natifs Swift plutôt que NSDictionary.
//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"] ?? ""
Je travaillais avec Swift 3.0 et je voulais apporter une réponse à la syntaxe mise à jour. De plus, et peut-être plus important encore, j’utilise l’objet PropertyListSerialization pour faire le gros du travail, ce qui est beaucoup plus souple que le simple NSDictionary car il permet à Array de constituer le type de racine du plist.
Vous trouverez ci-dessous une capture d'écran du plist que j'utilise. C'est un petit} compliqué, afin de montrer la puissance disponible, mais cela fonctionnera pour toute combinaison autorisée de types de plist.
Comme vous pouvez le constater, j'utilise un tableau de dictionnaires String: String pour stocker une liste de noms de sites Web et leur URL correspondante.
J'utilise l'objet PropertyListSerialization, comme mentionné ci-dessus, pour faire le gros du travail pour moi. De plus, Swift 3.0 est devenu plus "Swifty" et tous les noms d'objet ont perdu le préfixe "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)
Après que le code ci-dessus s'exécute, la variable plist
sera de type Array<AnyObject>
, mais nous savons de quel type il s'agit vraiment afin de pouvoir le transtyper au type correct:
let dictArray = plist as! [[String:String]]
// [[String:String]] is equivalent to Array< Dictionary<String, String> >
Et maintenant, nous pouvons accéder aux différentes propriétés de notre Array of String: String Dictionnaires de manière naturelle. Espérons les convertir en structures ou classes fortement typées;)
print(dictArray[0]["Name"])
Il est préférable d'utiliser des dictionnaires et des tableaux natifs, car ils ont été optimisés pour une utilisation avec Swift. Cela dit, vous pouvez utiliser des cours NS ... à Swift et je pense que cette situation le justifie. Voici comment vous le mettriez en œuvre:
var path = NSBundle.mainBundle().pathForResource("Config", ofType: "plist")
var dict = NSDictionary(contentsOfFile: path)
Jusqu'à présent (à mon avis), c'est le moyen le plus simple et le plus efficace d'accéder à un plist, mais à l'avenir, je pense qu'Apple ajoutera davantage de fonctionnalités (comme l'utilisation de plist) dans les dictionnaires natifs.
Swift - Lecture/écriture de pliste et fichier texte ....
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: Accéder à Info.Plist
J'ai un dictionnaire nommé CoachMarksDictionary avec une valeur booléenne dans Info.Plist. Je veux accéder à la valeur bool et la rendre vraie.
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) ")
}
Écrire à la pliste:
Depuis une liste personnalisée: - (Créer à partir d'un fichier, d'un nouveau fichier, d'une ressource, d'une liste de propriétés, de trois propriétés nommées: 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)
}
La méthode peut être appelée comme
self.writeToCoachMarksPlist(" true - means user has checked the marks",keyName: "the key in the CoachMarks dictionary").
peut réellement le faire en 1 ligne
var dict = NSDictionary(contentsOfFile: NSBundle.mainBundle().pathForResource("Config", ofType: "plist"))
Converti en une extension pratique via la réponse 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]
}
}
usage:
let path = Bundle.main.path(forResource: "plistName", ofType: "plist")!
let url = URL(fileURLWithPath: path)
let dict = Dictionary<String, AnyObject>.contentsOf(path: url)
Je serais prêt à parier que cela fonctionnerait également pour créer une extension similaire pour Arrays
Vous pouvez lire le plist en langue rapide de la manière suivante:
let path = NSBundle.mainBundle().pathForResource("PriceList", ofType: "plist")
let dict = NSDictionary(contentsOfFile: path)
Lire la valeur du dictionnaire unique:
let test: AnyObject = dict.objectForKey("index1")
Si vous voulez obtenir un dictionnaire multi-dimensionnel complet dans plist:
let value: AnyObject = dict.objectForKey("index2").objectForKey("date")
Voici le 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>
dans mon cas, je crée une NSDictionary
appeléeappSettings
et j'ajoute toutes les clés nécessaires. Pour ce cas, la solution est:
if let dict = NSBundle.mainBundle().objectForInfoDictionaryKey("appSettings") {
if let configAppToken = dict["myKeyInsideAppSettings"] as? String {
}
}
Swift 3.0
if let path = Bundle.main.path(forResource: "config", ofType: "plist") {
let dict = NSDictionary(contentsOfFile: path)
// use dictionary
}
La meilleure façon de faire cela à mon avis.
Voici une version un peu plus courte, basée sur la réponse 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?
Vous pouvez l'utiliser, je crée une extension simple pour Dictionary dans 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
}
}
Et vous pouvez l'utiliser pour le chargement
/**
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
}
J'ai créé un initialiseur Dictionary
simple qui remplace NSDictionary(contentsOfFile: path)
. Supprimez simplement la 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
}
}
Vous pouvez l'utiliser comme ceci:
let filePath = Bundle.main.path(forResource: "Preferences", ofType: "plist")!
let preferences = Dictionary(contentsOfFile: filePath)!
UserDefaults.standard.register(defaults: preferences)
Étape 1 : Méthode simple et rapide pour analyser le plist dans 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
}
}
Étape 2: Comment utiliser:
Bundle().parsePlist(ofName: "Your-Plist-Name")
Swift 4.0 iOS 11.2.6 liste analysé et le code à analyser, basé sur https://stackoverflow.com/users/3647770/ashok-r réponse ci-dessus.
<?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")
}
Etant donné que cette réponse n’est pas encore disponible, je voulais simplement indiquer que vous pouvez également utiliser la propriété infoDictionary pour obtenir le répertoire d’information sous forme de dictionnaire, Bundle.main.infoDictionary
.
Bien que quelque chose comme Bundle.main.object(forInfoDictionaryKey: kCFBundleNameKey as String)
puisse soit plus rapide si vous ne vous intéressez qu'à un élément spécifique du plist info.
// 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)
J'utilise les dictionnaires Swift, mais je les convertis depuis et vers NSDictionaries dans ma classe de gestionnaire de fichiers comme suit:
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>
}
Cela semble la façon la moins dérangeante de lire et d’écrire, mais le reste de mon code reste aussi rapide que possible.
Voici la solution que j'ai trouvée:
let levelBlocks = NSDictionary(contentsOfFile: NSBundle.mainBundle().pathForResource("LevelBlocks", ofType: "plist"))
let test: AnyObject = levelBlocks.objectForKey("Level1")
println(test) // Prints the value of test
J'ai défini le type de test
sur AnyObject
pour faire taire un avertissement concernant une inférence inattendue pouvant se produire.
En outre, cela doit être fait dans une méthode de classe.
Pour accéder et sauvegarder une valeur spécifique d'un type connu:
let value = levelBlocks.objectForKey("Level1").objectForKey("amount") as Int
println(toString(value)) // Converts value to String and prints it
Plist est un simple enum Swift que j'ai créé pour travailler avec des listes de propriétés.
// load an applications info.plist data
let info = Plist(NSBundle.mainBundle().infoDictionary)
let identifier = info["CFBundleIndentifier"].string!
Plus d'exemples:
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 lui-même est assez simple, voici sa liste au cas où vous vous y référeriez directement.
//
// 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)"
}
}
}
si vous voulez lire un "tableau à 2 dimensions" depuis .plist, vous pouvez l'essayer comme ceci:
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
}
}
}