I'm impressed by the structure imposed by the new do-catch construct.
I also appreciate that the Swift team gave us try! to handle cases where we are sure that errors won't occur.
Now one case that keeps bugging me when refactoring to Swift 2.0 code is I have to write something like this:
let fileManager = NSFileManager.defaultManager()
do {
try fileManager.removeFileAtPath(cachePath1)
} catch _ { }
do {
try fileManager.removeFileAtPath(cachePath2)
} catch _ { }
do {
try fileManager.removeFileAtPath(cachePath3)
} catch _ { }
I just want to delete those paths if possible, but I don't care if they fail; they may have not existed in the first place and if they did, the system will clean the Caches sooner or later.
If I write this as
do {
try fileManager.removeFileAtPath(cachePath1)
try fileManager.removeFileAtPath(cachePath2)
try fileManager.removeFileAtPath(cachePath3)
} catch _ { }
then if cachePath1 fails, 2 and 3 will never be executed. And while we don't care if the deletes fail, not trying at all just feels wrong. What if each method I call have a side effect that I need to trigger?
On the other hand,
try! fileManager.removeFileAtPath(cachePath1)
try! fileManager.removeFileAtPath(cachePath2)
try! fileManager.removeFileAtPath(cachePath3)
will crash my app if any of the deletes fail. Definitely not what I want.
Now I don't know if someone else had the same idea before, but what if we have a try? as well to counterpart the try!? We can write
try? fileManager.removeFileAtPath(cachePath1)
which ignores any thrown errors, and more importantly, does not trap on error.
For methods that return an object, Swift 2.0 already bridges Objective-C methods of the pattern
- (SomeObject *)doSomethingAndReturnError:(NSError **)error;
to
func doSomething() throws -> SomeObject
such that doing
let object: SomeObject = try! doSomething()
returns a non-optional SomeObject instance.
We can then make "try?" return an optional:
let object = try? doSomething() // object will be bound to SomeObject?
For void methods, returning a Void? is fine, or we can borrow the bridging rules from Objective-C and just return a Bool instead.
if try? fileManager.removeFileAtPath(cachePath1) {
println("file deleted!")
}
Is this idea too crazy or am I missing something that will break this kind of syntax?
Update:
I hope the discussion does not digress into a debate wether to handle file-system errors or not; I just used removeFileAtPath as ONE of the many use cases we encountered this problem. CharlesS pointed out another good use case: methods that were primarily used to check for a state, such as NSURL.checkResourceIsReachable()