is it possible to swizzle the keyword `throw`? - swift

I was thinking about error handling and I learned about swizzling recently. Swizzling is certainly a tool which shouldn't be used too often, and I think I understand that, but it made me wonder. If whenever an error is thrown, if I wanted to capture the thrown error. Is there a way I could use swizzling or some such in order to intercept the error and log it somewhere without interrupting the flow of the app? I was thinking about possibly swizzling the throws keyword, but that might not work. What tools would be used for this kind of thing?

No you cannot. Many compiler checks depend on the fact that throw "interrupts the flow of the app". For example, if some path of the code throws, then that path doesn't need to return:
func foo(x: Bool) throws -> Int {
if x {
throw someError
} else {
return 1
}
}
Now if throw someError did not "interrupt the flow of the app", what would bar print in the following code?
func bar() throws {
let x = try foo(x: true)
print(x)
}
Another example is guard:
guard let value = somethingThatMayBeNil else {
throw someError
}
print(value.nowICanSafelyAccessThis)
If throw someError above didn't actually "interrupt the flow of the app" and stop running the rest of the method, something really bad is going to happen at print(value.nowICanSafelyAccessThis), because somethingThatMayBeNil is nil, and I'm not even sure value even exists.
The whole point is that throw would unwind the call stack to a point where the error can be caught, and that the code that depends on there not being an error is not executed.
If you want to "capture" the error in some way, you can use a Result. The first example can be turned into:
func foo(x: Bool) -> Result<Int, Error> {
if x {
return Result.failure(someError)
} else {
return Result.success(1)
}
}
func bar() {
let x = foo(x: true)
// now this will print "failure(...)"
print(x)
// and x is of type Result<Int, Error>, rather than Int
switch x {
case .failure(let e):
// log the error e here...
case .success(let theInt):
// do something with theInt...
}
}
You can also use init(catching:) to wrap any throwing call into a Result. Suppose if you can't change foo, then you can do this in bar instead:
func bar() {
let x = Result { try foo(x: true) }
...
The second example can be turned into:
guard let value = somethingThatMayBeNil else {
// assuming the return type is changed appropriately
return Result.failure(someError)
}
print(value.nowICanSafelyAccessThis)
Note that this will still "interrupt the flow of the app", as in the print is still not executed if somethingThatMayBeNil is nil. There is nothing you can do about that.
You could also add a convenient factory method for logging:
extension Result {
static func failureAndLog(_ error: Failure) -> Result {
// do your logging here...
return .failure(error)
}
}

No, you can't swizzle throw. But the Swift runtime has a hook, _swift_WillThrow, that lets you examine an Error at the moment it's about to be thrown. This hook is not a stable API and could be changed or removed in future versions of Swift.
If you're using Swift 5.8, which is included in Xcode 14.3 (in beta release now), you can use the _swift_setWillThrowHandler function to set the _swift_willThrow function:
#_silgen_name("_swift_setWillThrowHandler")
func setWillThrowHandler(
_ handler: (#convention(c) (UnsafeRawPointer) -> Void)?
)
var errors: [String] = []
func tryItOut() {
setWillThrowHandler {
let error = unsafeBitCast($0, to: Error.self)
let callStack = Thread.callStackSymbols.joined(separator: "\n")
errors.append("""
\(error)
\(callStack)
""")
}
do {
throw MyError()
} catch {
print("caught \(error)")
print("errors = \(errors.joined(separator: "\n\n"))")
}
}
Output:
caught MyError()
errors = MyError()
0 iPhoneStudy 0x0000000102a97d9c $s11iPhoneStudy8tryItOutyyFySVcfU_ + 252
1 iPhoneStudy 0x0000000102a97ff0 $s11iPhoneStudy8tryItOutyyFySVcfU_To + 12
2 libswiftCore.dylib 0x000000018c2f4ee0 swift_willThrow + 56
3 iPhoneStudy 0x0000000102a978f8 $s11iPhoneStudy8tryItOutyyF + 160
4 iPhoneStudy 0x0000000102a99740 $s11iPhoneStudy6MyMainV4mainyyFZ + 28
5 iPhoneStudy 0x0000000102a997d0 $s11iPhoneStudy6MyMainV5$mainyyFZ + 12
6 iPhoneStudy 0x0000000102a99f48 main + 12
7 dyld 0x0000000102d15514 start_sim + 20
8 ??? 0x0000000102e11e50 0x0 + 4343275088
9 ??? 0x9f43000000000000 0x0 + 11476016275470155776
If you're using an older Swift (but at least Swift 5.2 I think, which was in Xcode 11.4), you have to access the _swift_willThrow hook directly:
var swift_willThrow: UnsafeMutablePointer<(#convention(c) (UnsafeRawPointer) -> Void)?> {
get {
dlsym(UnsafeMutableRawPointer(bitPattern: -2), "_swift_willThrow")!
.assumingMemoryBound(to: (#convention(c) (UnsafeRawPointer) -> Void)?.self)
}
}
var errors: [String] = []
func tryItOut() {
swift_willThrow.pointee = {
let error = unsafeBitCast($0, to: Error.self)
let callStack = Thread.callStackSymbols.joined(separator: "\n")
errors.append("""
\(error)
\(callStack)
""")
}
do {
throw MyError()
} catch {
print("caught \(error)")
print("errors = \(errors.joined(separator: "\n\n"))")
}
}

Related

Is this crash in a Swift script caused by a race condition?

Running the code below usually prints:
hello
Occasionally, it prints:
hello BlockExperiment world
But sometimes I get the following crash:
hello BlockExperiment/usr/bin/swift[0x51f95a4]
/usr/bin/swift[0x51f719e]
/usr/bin/swift[0x51f987c]
/lib/x86_64-linux-gnu/libpthread.so.0(+0x12980)[0x7fc7c0650980]
/usr/lib/swift/linux/libswiftCore.so(+0x40d2c4)[0x7fc7bc9642c4]
/usr/lib/swift/linux/libswiftCore.so(+0x3fecc6)[0x7fc7bc955cc6]
/usr/lib/swift/linux/libswiftCore.so(+0x3fe20b)[0x7fc7bc95520b]
/usr/lib/swift/linux/libswiftCore.so(+0x3e456d)[0x7fc7bc93b56d]
This doesn't seem to break any of the weak/strong/unowned practices that I know of.
Is the problem a basic race condition, where self is being accessed in the print statement after it has already been deallocated?
I can't be sure because in that case, I would expect the usual error message saying the access was to a deallocated instance. If it isn't this, how can you tell and in general debug this type of issue?
And if it is a race condition, why does the print statement get cut off sometimes after "hello"?
Code:
import Foundation
final class BlockRunner {
var block: () -> ()
init(_ b: #escaping () -> ()) { block = b }
func run() { block() }
deinit { print("Deinit: \(self)") }
}
final class BlockExperiment {
func f() {
let queue = DispatchQueue(label: "Queue")
let runner = BlockRunner { print("hello", self, "world") }
queue.asyncAfter(deadline: .now() + 1) { runner.run() }
}
deinit { print("Deinit: \(self)") }
}
do {
let e = BlockExperiment()
e.f()
RunLoop.current.run(until: Date() + 1 + 0.000000000000001)
}
That is definitely a race condition. If you need to print out anything, you need to do it on the main thread. Therefore, always wrap your print statements with something like this:
DispatchQueue.main.async {
print("Whatever")
}
I was bitten by this just recently, when I didn't realize that Process() was running my task in a background thread and I was seeing the exactly same type of log text corruption.

How to exit a function scope from inner function using Swift?

With return it is possible to exit the scope of the current function but is it also possible to exit the scope of the outer function that is calling the inner function?
func innerFunction() {
guard 1 == 2 else {
// exit scope of innerFunction and outerFunction
}
}
func outerFunction() {
innerFunction()
print("should be unreachable")
}
There could be one approach using a return value of the inner function that we can check for:
func innerFunction() -> Bool {
guard 1 == 2 else {
return false
}
return true
}
func outerFunction() {
guard innerFunction() else {
return
}
print("should be unreachable")
}
The problem with this approach is that it can clutter your code pretty quickly if the functions become more complicated and you have to use them over and over again.
Consider applying this approach with XCTest. It would look like this:
func testFoobar() {
guard let unwrappedObject = helperFunction() else {
XCTFail("A failure message that can change with each helperFunction call")
return
}
print("should be unreachable when helperFunction already failed")
}
I'd like to have something similar to this:
func testFoobar() {
let unwrappedObject = helperFunction()
print("should be unreachable when helperFunction already failed")
}
This is basically what Swift's error handling does:
func outer() throws
{
try inner()
print("Unreachable")
}
struct DoNotContinue : Error {}
func inner() throws
{
throw DoNotContinue()
}
do { try outer() }
catch _ { print("Skipped the unreachable") }
Note, of course, that the caller still has control over this: it could catch the thrown error itself instead of just exiting.
problem with this approach is that it can clutter your code
There's a much more serious problem with allowing callees to directly exit their callers, and that is that the flow of control very quickly becomes incomprehensible. Imagine that you have a couple of layers of this. Reading the top-level function, you no longer have any clear idea what can happen. You must yourself recurse into every callee's callee to make sure that the original code will continue on its course.

Function with throws doesn't exit the function with executes trow

I have the following function with throws:
func doSomething(_ myArray: [Int]) throws -> Int {
if myArray.count == 0 {
throw arrayErrors.empty
}
return myArray[0]
}
But when the array is equal to 0 it will throw the error but it will continue executing the function returning empty array.
How can I exit the function when goes to the if statement ?
The function does return when you throw an error. Pop this in a playground and look at the output.
//: Playground - noun: a place where people can play
enum MyError: Error {
case emptyArray
}
func doSomething<T>(_ array: [T]) throws -> T {
guard !array.isEmpty else {
throw MyError.emptyArray
}
return array[0]
}
do {
let emptyArray: [Int] = []
let x = try doSomething(emptyArray)
print("x from emptyArray is \(x)")
} catch {
print("error calling doSeomthing on emptyArray: \(error)")
}
do {
let populatedArray = [1]
let x = try doSomething(populatedArray)
print("x from populatedArray is \(x)")
} catch {
print("error calling doSeomthing on emptyArray: \(error)")
}
You'll see the output is
error calling doSeomthing on emptyArray: emptyArray
x from populatedArray is 1
Note that you don't see the output from print("x from emptyArray is \(x)") because it was never called, because throw ends the execution of that function. You can also confirm this because guard statements require the function to exit.
Also, just to note, if you want the first thing from an array you can just use myArray.first which will return T?, and you can handle the nil case instead of having to deal with an error. For example:
//: Playground - noun: a place where people can play
let myArray = [1, 2, 3]
if let firstItem = myArray.first {
print("the first item in myArray is \(firstItem)")
} else {
print("myArray is empty")
}
let myEmptyArray: [Int] = []
if let firstItem = myEmptyArray.first {
print("the first item in myEmptyArray is \(firstItem)")
} else {
print("myEmptyArray is empty")
}
which outputs:
the first item in myArray is 1
myEmptyArray is empty
You need to understand error handling. If you insert a throws keyword, anybody calling it will need to use a do-catch statement, try?, try! or continue to propagate them. So what will happen if the error is thrown is up to the caller.
Here's an example:
do {
try doSomething(foo)
} catch arrayErrors.empty {
fatalError("Array is empty!")
}
Apple's documentation for error handling
If you want to exit once you reach the if, simply don't use error handling and call fatalError inside the if.
if myArray.count = 0 {
fatalError("Error happened")
}

Is it possible to combine catches?

I'm trying to do this:
catch LocksmithError.Duplicate, LocksmithError.Allocate {...}
But I get an error at , saying :
Expected '{' after 'catch' pattern
Does this mean you can't combine cases like case expression2, expression3 :? Any reason it's made this way?
No, it's currently not possible to combine multiple patterns in a catch clause – the grammar (as detailed by the Swift Language Guide) only allows for a single pattern to match against:
catch-clause → catch pattern­opt ­where-clause­opt ­code-block­
Another possible solution to the ones proposed already, as long as your error enum is trivial (which LocksmithError appears to be), is to use a where clause after a binding pattern:
catch let error as LocksmithError where error == .Duplicate || error == .Allocate {
// ...
}
Given that you let your LocksmithError have some rawvalue type (e.g. Int), you could, in a single catch statement, bind the error thrown and use its rawValue to test for inclusion into one of several error cases (using the where clause after binding). E.g.:
enum FooError: Int, Error {
case err1 = 1, err2, err3, err4, err5
}
func foo(_ bar: Int) throws {
if let err = FooError(rawValue: bar) { throw err }
}
func tryFoo(_ bar: Int) {
do {
try foo(bar)
} catch let err as FooError where (1...4).contains(err.rawValue) {
print("Any of 1st through 4th error!")
} catch FooError.err5 {
print("5th error!")
} catch {}
}
tryFoo(1) // Any of 1st through 4th error!
tryFoo(4) // Any of 1st through 4th error!
tryFoo(5) // 5th error!
As suggested by #user28434 (thanks!), there's really no need to apply the rawValue restraint as the same method above could be used to direcly see if the binded err is a member of an array of given cases.
enum FooError: Error {
case err1, err2, err3
}
func foo(_ bar: Int) throws {
guard bar != 1 else { throw FooError.err1 }
guard bar != 2 else { throw FooError.err2 }
guard bar != 3 else { throw FooError.err3 }
}
func tryFoo(_ bar: Int) {
do {
try foo(bar)
} catch let err as FooError where [.err1, .err2].contains(err) {
print("1st or 2nd error!")
} catch FooError.err3 {
print("3rd error!")
} catch {}
}
tryFoo(1) // 1st or 2nd error!
tryFoo(2) // 1st or 2nd error!
tryFoo(3) // 3rd error!
This reduces to basically just a variation of the accepted answer (possibly useful for catch blocks covering more than just two cases, but in such cases, possibly the error enum should consider refactoring).
It's just a syntactical problem, it may be improved in Swift 4+.
Right now you can use this:
catch let locksmithError as LocksmithError {
switch locksmithError {
case .Duplicate, .Allocate:
// your code
…
}
}

Error-Handling in Swift-Language

I haven't read too much into Swift but one thing I noticed is that there are no exceptions.
So how do they do error handling in Swift? Has anyone found anything related to error-handling?
Swift 2 & 3
Things have changed a bit in Swift 2, as there is a new error-handling mechanism, that is somewhat more similar to exceptions but different in detail.
1. Indicating error possibility
If function/method wants to indicate that it may throw an error, it should contain throws keyword like this
func summonDefaultDragon() throws -> Dragon
Note: there is no specification for type of error the function actually can throw. This declaration simply states that the function can throw an instance of any type implementing ErrorType or is not throwing at all.
2. Invoking function that may throw errors
In order to invoke function you need to use try keyword, like this
try summonDefaultDragon()
this line should normally be present do-catch block like this
do {
let dragon = try summonDefaultDragon()
} catch DragonError.dragonIsMissing {
// Some specific-case error-handling
} catch DragonError.notEnoughMana(let manaRequired) {
// Other specific-case error-handlng
} catch {
// Catch all error-handling
}
Note: catch clause use all the powerful features of Swift pattern matching so you are very flexible here.
You may decided to propagate the error, if your are calling a throwing function from a function that is itself marked with throws keyword:
func fulfill(quest: Quest) throws {
let dragon = try summonDefaultDragon()
quest.ride(dragon)
}
Alternatively, you can call throwing function using try?:
let dragonOrNil = try? summonDefaultDragon()
This way you either get the return value or nil, if any error occurred. Using this way you do not get the error object.
Which means that you can also combine try? with useful statements like:
if let dragon = try? summonDefaultDragon()
or
guard let dragon = try? summonDefaultDragon() else { ... }
Finally, you can decide that you know that error will not actually occur (e.g. because you have already checked are prerequisites) and use try! keyword:
let dragon = try! summonDefaultDragon()
If the function actually throws an error, then you'll get a runtime error in your application and the application will terminate.
3. Throwing an error
In order to throw an error you use throw keyword like this
throw DragonError.dragonIsMissing
You can throw anything that conforms to ErrorType protocol. For starters NSError conforms to this protocol but you probably would like to go with enum-based ErrorType which enables you to group multiple related errors, potentially with additional pieces of data, like this
enum DragonError: ErrorType {
case dragonIsMissing
case notEnoughMana(requiredMana: Int)
...
}
Main differences between new Swift 2 & 3 error mechanism and Java/C#/C++ style exceptions are follows:
Syntax is a bit different: do-catch + try + defer vs traditional try-catch-finally syntax.
Exception handling usually incurs much higher execution time in exception path than in success path. This is not the case with Swift 2.0 errors, where success path and error path cost roughly the same.
All error throwing code must be declared, while exceptions might have been thrown from anywhere. All errors are "checked exceptions" in Java nomenclature. However, in contrast to Java, you do not specify potentially thrown errors.
Swift exceptions are not compatible with ObjC exceptions. Your do-catch block will not catch any NSException, and vice versa, for that you must use ObjC.
Swift exceptions are compatible with Cocoa NSError method conventions of returning either false (for Bool returning functions) or nil (for AnyObject returning functions) and passing NSErrorPointer with error details.
As an extra syntatic-sugar to ease error handling, there are two more concepts
deferred actions (using defer keyword) which let you achieve the same effect as finally blocks in Java/C#/etc
guard statement (using guard keyword) which let you write little less if/else code than in normal error checking/signaling code.
Swift 1
Runtime errors:
As Leandros suggests for handling runtime errors (like network connectivity problems, parsing data, opening file, etc) you should use NSError like you did in ObjC, because the Foundation, AppKit, UIKit, etc report their errors in this way. So it's more framework thing than language thing.
Another frequent pattern that is being used are separator success/failure blocks like in AFNetworking:
var sessionManager = AFHTTPSessionManager(baseURL: NSURL(string: "yavin4.yavin.planets"))
sessionManager.HEAD("/api/destoryDeathStar", parameters: xwingSquad,
success: { (NSURLSessionDataTask) -> Void in
println("Success")
},
failure:{ (NSURLSessionDataTask, NSError) -> Void in
println("Failure")
})
Still the failure block frequently received NSError instance, describing the error.
Programmer errors:
For programmer errors (like out of bounds access of array element, invalid arguments passed to a function call, etc) you used exceptions in ObjC. Swift language does not seem to have any language support for exceptions (like throw, catch, etc keyword). However, as documentation suggests it is running on the same runtime as ObjC, and therefore you are still able to throw NSExceptions like this:
NSException(name: "SomeName", reason: "SomeReason", userInfo: nil).raise()
You just cannot catch them in pure Swift, although you may opt for catching exceptions in ObjC code.
The questions is whether you should throw exceptions for programmer errors, or rather use assertions as Apple suggests in the language guide.
Update June 9th 2015 - Very important
Swift 2.0 comes with try, throw, and catch keywords and the most exciting is:
Swift automatically translates Objective-C methods that produce errors into methods that throw an error according to Swift's native error handling functionality.
Note: Methods that consume errors, such as delegate methods or methods
that take a completion handler with an NSError object argument, do not
become methods that throw when imported by Swift.
Excerpt From: Apple Inc. “Using Swift with Cocoa and Objective-C (Swift 2 Prerelease).” iBooks.
Example: (from the book)
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *URL = [NSURL fileURLWithPath:#"/path/to/file"];
NSError *error = nil;
BOOL success = [fileManager removeItemAtURL:URL error:&error];
if (!success && error){
NSLog(#"Error: %#", error.domain);
}
The equivalent in swift will be:
let fileManager = NSFileManager.defaultManager()
let URL = NSURL.fileURLWithPath("path/to/file")
do {
try fileManager.removeItemAtURL(URL)
} catch let error as NSError {
print ("Error: \(error.domain)")
}
Throwing an Error:
*errorPtr = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCannotOpenFile userInfo: nil]
Will be automatically propagated to the caller:
throw NSError(domain: NSURLErrorDomain, code: NSURLErrorCannotOpenFile, userInfo: nil)
From Apple books, The Swift Programming Language it's seems errors should be handle using enum.
Here is an example from the book.
enum ServerResponse {
case Result(String, String)
case Error(String)
}
let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.")
switch success {
case let .Result(sunrise, sunset):
let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error(error):
let serverResponse = "Failure... \(error)"
}
From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/br/jEUH0.l
Update
From Apple news books, "Using Swift with Cocoa and Objective-C". Runtime exceptions not occur using swift languages, so that's why you don't have try-catch. Instead you use Optional Chaining.
Here is a stretch from the book:
For example, in the code listing below, the first and second lines are
not executed because the length property and the characterAtIndex:
method do not exist on an NSDate object. The myLength constant is
inferred to be an optional Int, and is set to nil. You can also use an
if–let statement to conditionally unwrap the result of a method that
the object may not respond to, as shown on line three
let myLength = myObject.length?
let myChar = myObject.characterAtIndex?(5)
if let fifthCharacter = myObject.characterAtIndex(5) {
println("Found \(fifthCharacter) at index 5")
}
Excerpt From: Apple Inc. “Using Swift with Cocoa and Objective-C.” iBooks. https://itun.es/br/1u3-0.l
And the books also encourage you to use cocoa error pattern from Objective-C (NSError Object)
Error reporting in Swift follows the same pattern it does in
Objective-C, with the added benefit of offering optional return
values. In the simplest case, you return a Bool value from the
function to indicate whether or not it succeeded. When you need to
report the reason for the error, you can add to the function an
NSError out parameter of type NSErrorPointer. This type is roughly
equivalent to Objective-C’s NSError **, with additional memory safety
and optional typing. You can use the prefix & operator to pass in a
reference to an optional NSError type as an NSErrorPointer object, as
shown in the code listing below.
var writeError : NSError?
let written = myString.writeToFile(path, atomically: false,
encoding: NSUTF8StringEncoding,
error: &writeError)
if !written {
if let error = writeError {
println("write failure: \(error.localizedDescription)")
}
}
Excerpt From: Apple Inc. “Using Swift with Cocoa and Objective-C.” iBooks. https://itun.es/br/1u3-0.l
There are no Exceptions in Swift, similar to Objective-C's approach.
In development, you can use assert to catch any errors which might appear, and need to be fixed before going to production.
The classic NSError approach isn't altered, you send an NSErrorPointer, which gets populated.
Brief example:
var error: NSError?
var contents = NSFileManager.defaultManager().contentsOfDirectoryAtPath("/Users/leandros", error: &error)
if let error = error {
println("An error occurred \(error)")
} else {
println("Contents: \(contents)")
}
The recommended 'Swift Way' is:
func write(path: String)(#error: NSErrorPointer) -> Bool { // Useful to curry error parameter for retrying (see below)!
return "Hello!".writeToFile(path, atomically: false, encoding: NSUTF8StringEncoding, error: error)
}
var writeError: NSError?
let written = write("~/Error1")(error: &writeError)
if !written {
println("write failure 1: \(writeError!.localizedDescription)")
// assert(false) // Terminate program
}
However I prefer try/catch as I find it easier to follow because it moves the error handling to a separate block at the end, this arrangement is sometimes called "Golden Path". Lucky you can do this with closures:
TryBool {
write("~/Error2")(error: $0) // The code to try
}.catch {
println("write failure 2: \($0!.localizedDescription)") // Report failure
// assert(false) // Terminate program
}
Also it is easy to add a retry facility:
TryBool {
write("~/Error3")(error: $0) // The code to try
}.retry {
println("write failure 3 on try \($1 + 1): \($0!.localizedDescription)")
return write("~/Error3r") // The code to retry
}.catch {
println("write failure 3 catch: \($0!.localizedDescription)") // Report failure
// assert(false) // Terminate program
}
The listing for TryBool is:
class TryBool {
typealias Tryee = NSErrorPointer -> Bool
typealias Catchee = NSError? -> ()
typealias Retryee = (NSError?, UInt) -> Tryee
private var tryee: Tryee
private var retries: UInt = 0
private var retryee: Retryee?
init(tryee: Tryee) {
self.tryee = tryee
}
func retry(retries: UInt, retryee: Retryee) -> Self {
self.retries = retries
self.retryee = retryee
return self
}
func retry(retryee: Retryee) -> Self {
return self.retry(1, retryee)
}
func retry(retries: UInt) -> Self {
// For some reason you can't write the body as "return retry(1, nil)", the compiler doesn't like the nil
self.retries = retries
retryee = nil
return self
}
func retry() -> Self {
return retry(1)
}
func catch(catchee: Catchee) {
var error: NSError?
for numRetries in 0...retries { // First try is retry 0
error = nil
let result = tryee(&error)
if result {
return
} else if numRetries != retries {
if let r = retryee {
tryee = r(error, numRetries)
}
}
}
catchee(error)
}
}
You can write a similar class for testing an Optional returned value instead of Bool value:
class TryOptional<T> {
typealias Tryee = NSErrorPointer -> T?
typealias Catchee = NSError? -> T
typealias Retryee = (NSError?, UInt) -> Tryee
private var tryee: Tryee
private var retries: UInt = 0
private var retryee: Retryee?
init(tryee: Tryee) {
self.tryee = tryee
}
func retry(retries: UInt, retryee: Retryee) -> Self {
self.retries = retries
self.retryee = retryee
return self
}
func retry(retryee: Retryee) -> Self {
return retry(1, retryee)
}
func retry(retries: UInt) -> Self {
// For some reason you can't write the body as "return retry(1, nil)", the compiler doesn't like the nil
self.retries = retries
retryee = nil
return self
}
func retry() -> Self {
return retry(1)
}
func catch(catchee: Catchee) -> T {
var error: NSError?
for numRetries in 0...retries {
error = nil
let result = tryee(&error)
if let r = result {
return r
} else if numRetries != retries {
if let r = retryee {
tryee = r(error, numRetries)
}
}
}
return catchee(error)
}
}
The TryOptional version enforces a non-Optional return type that makes subsequent programming easier, e.g. 'Swift Way:
struct FailableInitializer {
init?(_ id: Int, error: NSErrorPointer) {
// Always fails in example
if error != nil {
error.memory = NSError(domain: "", code: id, userInfo: [:])
}
return nil
}
private init() {
// Empty in example
}
static let fallback = FailableInitializer()
}
func failableInitializer(id: Int)(#error: NSErrorPointer) -> FailableInitializer? { // Curry for retry
return FailableInitializer(id, error: error)
}
var failError: NSError?
var failure1Temp = failableInitializer(1)(error: &failError)
if failure1Temp == nil {
println("failableInitializer failure code: \(failError!.code)")
failure1Temp = FailableInitializer.fallback
}
let failure1 = failure1Temp! // Unwrap
Using TryOptional:
let failure2 = TryOptional {
failableInitializer(2)(error: $0)
}.catch {
println("failableInitializer failure code: \($0!.code)")
return FailableInitializer.fallback
}
let failure3 = TryOptional {
failableInitializer(3)(error: $0)
}.retry {
println("failableInitializer failure, on try \($1 + 1), code: \($0!.code)")
return failableInitializer(31)
}.catch {
println("failableInitializer failure code: \($0!.code)")
return FailableInitializer.fallback
}
Note auto-unwrapping.
Edit: Although this answer works, it is little more than Objective-C transliterated into Swift. It has been made obsolete by changes in Swift 2.0. Guilherme Torres Castro's answer above is a very good introduction to the preferred way of handling errors in Swift. VOS
It took a bit of figuring it out but I think I've sussed it. It seems ugly though. Nothing more than a thin skin over the Objective-C version.
Calling a function with an NSError parameter...
var fooError : NSError ? = nil
let someObject = foo(aParam, error:&fooError)
// Check something was returned and look for an error if it wasn't.
if !someObject {
if let error = fooError {
// Handle error
NSLog("This happened: \(error.localizedDescription)")
}
} else {
// Handle success
}`
Writing the function that takes an error parameter...
func foo(param:ParamObject, error: NSErrorPointer) -> SomeObject {
// Do stuff...
if somethingBadHasHappened {
if error {
error.memory = NSError(domain: domain, code: code, userInfo: [:])
}
return nil
}
// Do more stuff...
}
Basic wrapper around objective C that gives you the try catch feature.
https://github.com/williamFalcon/SwiftTryCatch
Use like:
SwiftTryCatch.try({ () -> Void in
//try something
}, catch: { (error) -> Void in
//handle error
}, finally: { () -> Void in
//close resources
})
As Guilherme Torres Castro said, in Swift 2.0, try, catch, do can be used in the programming.
For example, In CoreData fetch data method, instead of put &error as a parameter into the managedContext.executeFetchRequest(fetchRequest, error: &error), now we only need to use use managedContext.executeFetchRequest(fetchRequest) and then handle the error with try, catch (Apple Document Link)
do {
let fetchedResults = try managedContext.executeFetchRequest(fetchRequest) as? [NSManagedObject]
if let results = fetchedResults{
people = results
}
} catch {
print("Could not fetch")
}
If you have already download the xcode7 Beta. Try to search throwing errors in Documentations and API Reference and choose the first showing result, it gives a basic idea what can be done for this new syntax. However, fully documentation is not post for many APIs yet.
More fancy Error Handling techniques can be found in
What's New in Swift (2015 Session 106 28m30s)
This is an update answer for swift 2.0. I am looking forward feature rich Error handling model like in java. Finally, they announced the good news. here
Error handling model: The new error handling model in Swift 2.0 will
instantly feel natural, with familiar try, throw, and catch keywords.
Best of all, it was designed to work perfectly with the Apple SDKs and
NSError. In fact, NSError conforms to a Swift’s ErrorType. You’ll
definitely want to watch the WWDC session on What’s New in Swift to
hear more about it.
e.g :
func loadData() throws { }
func test() {
do {
try loadData()
} catch {
print(error)
}}
Starting with Swift 2, as others have already mentioned, error handling is best accomplished through the use of do/try/catch and ErrorType enums. This works quite well for synchronous methods, but a little cleverness is required for asynchronous error handling.
This article has a great approach to this problem:
https://jeremywsherman.com/blog/2015/06/17/using-swift-throws-with-completion-callbacks/
To summarize:
// create a typealias used in completion blocks, for cleaner code
typealias LoadDataResult = () throws -> NSData
// notice the reference to the typealias in the completionHandler
func loadData(someID: String, completionHandler: LoadDataResult -> Void)
{
completionHandler()
}
then, the call to the above method would be as follows:
self.loadData("someString",
completionHandler:
{ result: LoadDataResult in
do
{
let data = try result()
// success - go ahead and work with the data
}
catch
{
// failure - look at the error code and handle accordingly
}
})
This seems a bit cleaner than having a separate errorHandler callback passed to the asynchronous function, which was how this would be handled prior to Swift 2.
Error handling is a new feature of Swift 2.0. It uses the try, throw and catch keywords.
See the Apple Swift 2.0 announcement on the official Apple Swift blog
Nice and simple lib to handle exception:
TryCatchFinally-Swift
Like a few others it wraps around the objective C exception features.
Use it like this:
try {
println(" try")
}.catch { e in
println(" catch")
}.finally {
println(" finally")
}
enum CheckValidAge : Error{
case overrage
case underage
}
func checkValidAgeForGovernmentJob(age:Int)throws -> Bool{
if age < 18{
throw CheckValidAge.underage
}else if age > 25{
throw CheckValidAge.overrage
}else{
return true
}
}
do {
try checkValidAgeForGovernmentJob(age: 26)
print("You are valid for government job ")
}catch CheckValidAge.underage{
print("You are underage for government job ")
}catch CheckValidAge.overrage{
print("You are overrage for government job ")
}
Change age in try checkValidAgeForGovernmentJob(age: 26)
Out Put
You are overrage for government job
What I have seen is that because of the nature of the device you don't want to be throwing a bunch of cryptic error handling messages at the user. That is why most functions return optional values then you just code to ignore the optional. If a function comes back nil meaning it failed you can pop a message or whatever.