I am trying to iterate over nested dict from plist file. Problem is with type mismatch while decomposing contents. I always get errors that NSObject, NSDict and other NSstuff cannot be translated to String variables including when I use "(value)", string(), as .. How to decompose a plist dict sub sets tuple?(array) into separate variables?
func LoadPlistContacts() {
let path = NSBundle.mainBundle().pathForResource("ContactList", ofType: "plist")
var AppContactsList = NSDictionary(contentsOfFile: path!)
ListKeys = sorted(AppContactsList!.allKeys as! [String])
for key in ListKeys {
var (AppPersonName, AppPersonSurname, AppPersonCompany, AppPersonPhone) = AppContactsList[key]
}
}
UPDATED: I have altered the plist with Dictionaries instead of Arrays and updated the code, yet type mismatch persists. As Airspeed Velocity and nhgrif point out in comments, example did got messier with updated plist. Should I do nested for cycles if the line with the commented error does not solve it? Thanks.
var ListKeys: [String]!
var ListContacts: [String: String]!
func LoadPlistContacts() {
if let path = NSBundle.mainBundle().pathForResource("ContactList", ofType: "plist") {
var AppContactsList = NSDictionary(contentsOfFile: path)
ListKeys = sorted(AppContactsList!.allKeys as! [String])
ListContacts = sorted(AppContactsList!.allValues as [String:String]) { $0.0 < $1.0 }
// I get an error [AnyObject] is not convertible to '[String:String]'
for contact in ListContacts {
let name = contact["Forename"] ?? ""
let surname = contact["Surname"] ?? ""
let address = contact["Company"] ?? ""
let phone = contact["Phone"] ?? ""
}
}
else {
fatalError("Could not open Contacts plist")
}
}
Btw, Airspeed Velocity, love your blog!
Swift doesn’t allow you to destructure arrays into tuples like this – primarily because it isn’t guaranteed to work (the array might not have the right number of entries).
You may find it easier, rather than arrays, to have another dictionary inside the plist:
And then to use those entries like so:
if let path = NSBundle.mainBundle().pathForResource("ContactList", ofType: "plist"),
contacts = NSDictionary(contentsOfFile: path) as? [String:[[String:String]]]
{
let sortedContacts = sorted(contacts) { lhs,rhs in lhs.0 < rhs.0 }
// destructuring works here because contacts is known
// at compile time to be an array of (key,value) pairs
for (section, contactArray) in sortedContacts {
for contact in contactArray {
let name = contact["Forename"]
let surname = contact["Surname"]
let address = contact["Company"]
let phone = contact["Phone"]
println(name,surname,address,phone)
}
}
}
else {
fatalError("Could not open Contacts plist")
}
Note, when you get those entries out, they will be optional. This is really another benefit of this approach – it means you can leave out entries in the plist, and then either default them:
// default to blank string for phone number
let phone = contact["Phone"] ?? ""
Or mandate them:
for (section, contactArray) in contacts {
for contact in contactArray {
if let name = contact["Forename"],
surname = contact["Surname"],
address = contact["Company"],
phone = contact["Phone"]
{
println(name,surname,address,phone)
}
else {
fatalError("Missing entry for \(section)")
}
}
}
Note, it is much more preferable to use if let
rather than force-unwrapping things with !
, even when you’re working from something like a plist that is configured at build-time and so in theory should never contain invalid entries – because this way, you can put in explicit error handling that will help debug if you ever accidentally put the wrong data in the plist.