A better way to handle optional values is "optional binding":
if let path = NSBundle.mainBundle().pathForResource("NavigationMenu", ofType: "plist") {
// do something with `path`
} else {
NSLog("Unable to locate file : NavigationMenu.plist")
}
which tests and unwraps the optional result as a single action.
Inside the if-block, path
is the unwrapped String
.
You can do the same with the items:
if let path = NSBundle.mainBundle().pathForResource("NavigationMenu", ofType: "plist") {
if let items = NSArray(contentsOfFile: path) {
// do something with `items` ...
} else {
NSLog("Navigation menu items could not be read from plist.")
}
} else {
NSLog("Unable to locate file : NavigationMenu.plist")
}
As of Swift 1.2, multiple optional bindings can be combined in
a single if-statement, which reduces the number of nested if-levels:
if
let path = NSBundle.mainBundle().pathForResource("NavigationMenu", ofType: "plist"),
let items = NSArray(contentsOfFile: path) {
// do something with `items` ...
} else {
NSLog("Unable to load NavigationMenu")
}
The downside is that you have only a common else-block for the
error condition and cannot distinguish which of the optional bindings
failed.
Instead of objectAtIndex()
and objectForKey()
you can use
subscripting for NSArray
and NSDictionary
:
for var i = 0 ; i < items!.count ; i++ {
let item = items![i] as! NSDictionary
let navItem = NavigationItem(name: item["name"] as! String,
andSegueIdentifier: item["segueIdentifier"] as! String)
navigationItems.append(navItem)
}
But a better way is to cast the NSArray
to a Swift array of Swift dictionaries in the first step.
Then you can use array enumeration and don't need any casts later:
if
let path = NSBundle.mainBundle().pathForResource("NavigationMenu", ofType: "plist"),
let items = NSArray(contentsOfFile: path) as? [[String : String]] {
for item in items {
let navItem = NavigationItem(name: item["name"]!, andSegueIdentifier: item["segueIdentifier"]!)
navigationItems.append(navItem)
}
} else {
NSLog("Unable to load NavigationMenu")
}
This assumes that values for the "name" and "segueIdentifier" are
present (and will crash at runtime otherwise). Alternatively, use
optional binding again:
for item in items {
if let name = item["name"], ident = item["segueIdentifier"] {
let navItem = NavigationItem(name: name, andSegueIdentifier: ident)
navigationItems.append(navItem)
} else {
NSLog("Invalid navigation item")
}
}
Finally, you could replace the inner loop by a map()
operation:
if
let path = NSBundle.mainBundle().pathForResource("NavigationMenu", ofType: "plist"),
let items = NSArray(contentsOfFile: path) as? [[String : String]] {
navitationItems = map(items) {
NavigationItem(name: $0["name"]!, andSegueIdentifier: $0["segueIdentifier"]!)
}
} else {
NSLog("Unable to load NavigationMenu")
}
pathForResource
returnNSURL!
orNSURL?
and same question for everything else here that might returnnil
? – nhgrif Apr 24 '15 at 11:45pathForResource
returnsString?
.NSArray(contentsOfFile)
returns[AnyObject]?
– W.K.S Apr 24 '15 at 11:49