In Swift 1.2 I have this:
if let filePath = NSBundle.mainBundle().pathForResource("some", ofType: "txt"),
data = String(contentsOfFile: filePath, encoding: NSUTF8StringEncoding) {
for line in data.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet()) {
// Do something
}
} else {
println("some.txt is missing")
}
In Swift 2, I can no longer do this, because both pathForResource
and contentsOfFile
can throw, as well as returning optionals. I can fix it, but it now seems remarkably verbose:
do {
if let filePath = try NSBundle.... {
do {
if let data = try String.... {
for line in data..... {
// Do something
}
} else {
print("Nil data")
}
} catch {
print("contentsOfFile threw")
}
} else {
print("Nil pathForResource")
}
} catch {
print("pathForResource threw")
}
I expect I have missed something - any help appreciated.
Use guard syntax instead of if-let.
Here is a sample:
do {
guard let filePath = try NSBundle .... else {
// if there is exception or there is no value
throw SomeError
}
guard let data = try String .... else {
}
} catch {
}
The difference between if-let and guard is scope of the unwrapped value. If you use if-let filePath value is available only inside the if-let block. If you use guard filePath value is available to scope that is guard called in.
Here is the relevant section in swift book
A guard statement, like an if statement, executes statements depending on the Boolean value of an expression. You use a guard statement to require that a condition must be true in order for the code after the guard statement to be executed. Unlike an if statement, a guard statement always has an else clause—the code inside the else` clause is executed if the condition is not true.
Near as I can tell, only your String initializer in the above actually throws an error (although pathForResource()
may change from returning an optional value to throwing an error at some point). Therefore, the below should replicate what you did before:
if let filePath = NSBundle.mainBundle().pathForResource("some", ofType: "txt") {
do {
let data = try String(contentsOfFile: filePath, encoding: NSUTF8StringEncoding)
for line in data.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet()) {
// Do something
}
} catch {
print("Couldn't load the file somehow")
}
} else {
print("some.txt is missing")
}
Your string is no longer optional, so there's no need for an if let
there.
As mustafa points out, a guard
statement could be used to remove a level of indentation in the success case:
guard let filePath = NSBundle.mainBundle().pathForResource("some", ofType: "txt") else {
print("some.txt is missing")
}
do {
let data = try String(contentsOfFile: filePath, encoding: NSUTF8StringEncoding)
for line in data.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet()) {
// Do something
}
} catch {
print("Couldn't load the file somehow")
}
Now, if pathForResource()
changed from returning an optional to throwing an error, you can simply use these try statements in sequence:
do {
let filePath = try NSBundle.mainBundle().pathForResource("some", ofType: "txt")
let data = try String(contentsOfFile: filePath, encoding: NSUTF8StringEncoding)
for line in data.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet()) {
// Do something
}
} catch {
print("some.txt is missing")
}
The first statement to throw an error will kick out at that point, preventing execution of anything past that. A single catch statement is enough to pick up anything from a series of failable operations, which makes it easy to chain them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With