Error handling in Swift

Source: Internet
Author: User
Tags assert error status code


Objective


Any code will have errors, some of which can be remedied, and some will only crash the Program. Good error handling can improve the robustness of your code and improve the stability of your Program.



Swift version of this article: Swift 3


Objective C return Nil


If something goes wrong, returning null is a common way of handling objective C. Because in objective c, sending messages to nil is Safe. Like what:


- (instancetype)init
{
     Self = [super init];
     If (self) {

     }
     / / If the initialization fails, will return nil
     Return self;
}
Assertion


The assertion specifies the context of our method, and if the assertion is not met, it is crash directly in the debug Environment.



For Example: the Af_resume method in afnetworking


 
- (void)af_resume {
    NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
    NSURLSessionTaskState state = [self state];
    [self af_resume];

    if (state != NSURLSessionTaskStateRunning) {
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];
    }
}
Return Status Code


Return status codes and global error messages are often used Together. The way this error is handled is common in objective C to encapsulate C code, or a pure C method. For example, error handling in Sqlite:


Int result = sqlite3_open(dbPath,&_db );
If(result != SQLITE_OK) {//If an error occurs

} 


Another example is when data is written to a file


BOOL succeed = [currentData writeToFile:path atomically:YES];
Nserror

Nserror is the recommended error handling method in Cocoa .
Examples of using nserror to handle errors spread throughout the Cocoatouch Framework.
For Example: Nsfilemanager

 
NSFileManager * fm  = [NSFileManager defaultManager];
NSError * error;
[fm removeItemAtPath:path error:&error];


nsurlsession, for example, passes the error message through Nserror.


 
[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

}];


A nserror includes the details of the error: there are several main information


    • Code Error Status Code
    • Domain Error domains
    • UserInfo Error Details


For example, a common nsurlerrordomain, that is, a network request failure:


 
 
 NSURLErrorCancelled = -999,
    NSURLErrorBadURL = -1000,
    NSURLErrorTimedOut = -1001,
    NSURLErrorUnsupportedURL = -1002,
    NSURLErrorCannotFindHost = -1003,
    //....
NSException


NSException is similar to exceptions in other languages, you can catch exceptions by try-catch, etc.


 
 
@try {
    //Code that can potentially throw an exception
} @catch (NSException *exception) {
    //Handle an exception thrown in the @try block
} @finally {
    //Code that gets executed whether or not an exception is thrown
}


of course, You can also throw an exception yourself:


[NSException raise:@"ExceptionName"format:@"Contents"];
  • In objective c, it is common for us to throw an exception only if the program is in error and cannot continue Execution.
  • Objective C's exception handling is inefficient, usually not used for error handling, and Objective C does not have a similar throw keyword to indicate that a method throws an exception and it is difficult to code to determine if a try-cathe is Required.
Error handling in Swift

Swift provides first-class support for throwing, catching, propagating, and manipulating recoverable errors at Runtime.


Swift provides a complete set of error-throwing-capture-processing Mechanisms. Swift usesErrora protocol to represent the type of error, and Do-try-catch to handle code that might throw an Exception.


Optional


An optional value indicates that a value has either a value or Nil. In swift, Optional is written in enum,


public enum Optional<Wrapped> : ExpressibleByNilLiteral {
    case none
    case some(Wrapped)
     //...
}


When an error occurs, returning an optional value is a common way to handle it. however, There is one obvious drawback to this approach:


The caller is not sure why it failed, and it is not good to do the related processing.

error protocol and throws


Erroris an empty protocol used to indicate the type of Error.NSErrorandCFErrorboth have followed this agreement.



The throws keyword indicates that this method throws an error and the method caller needs to handle the Error.



In swfit, enumerations are a type of data that is particularly appropriate for handling Error. We first define a class of person to represent people


enum Sex{
    case male
    case female
}
class Person{
    let sex:Sex
    var money:CGFloat
    init(sex:Sex ,money:CGFloat) {
        self.sex = sex
        self.money = money
    }
}


A person can get married and get married with some mistakes, such as not having enough money, such as gender, and using enumerations to represent Them.


enum MarryError : Error{ case lackMoney case wrongSex}


The implementation of the method is then as Follows:


extension Person{
    func marry(with another: Person) throws -> Bool{
        guard self.sex != another.sex else{
            throw MarryError.wrongSex
        }
        guard self.money + another.money > 100000 else {
            throw MarryError.lackMoney
        }
        return true
    }
}


For a function with the throws keyword, there are two ways to choose when calling:


    • Using Do-try-catch code blocks
 
let tom = Person(sex: .male, money: 100000)
let jack = Person(sex: .male, money: 100000)
do{
    try tom.marry(with: jack)
}catch MarryError.wrongSex {
    print("Two Person have same sex")
}catch MarryError.lackMoney{
    print("Oh, they do not have enough moeny")
}catch let error{
    print(error)
}


of course, You can call this if you don't need to distinguish between each error.


 
do{
    try tom.marry(with: jack)
}catch let error{
    print(error)
}
    • Using try?, for the throws function with a return value, use try? to convert the result to an optional Value.
 
let tom = Person(sex: .male, money: 100000)
let jack = Person(sex: .male, money: 100000)

if let result = try? tom.marry(with: jack){//成功

}else{
    print("Error happen")
}
Defer keywords


The defer keyword is used to handle @finally similar to the @[email protected] @finally in Ojective C.
For example, we open the file and if we throw an error, we always want to close the file Handle.


func contents(of filePath:String) throws -> String{
    let file = open(filePath,O_RDWR)
    defer {
        close(file)
    }
    //...
}

The contents of the defer code block are executed before exiting the scope


About defer, There are two points to note


    • Multiple defer are executed in reverse Order.
    • Defer code blocks do not execute when your program encounters a serious error, such as fatalerror, or forces parsing nil, or Segfaults.
rethrow


Rethrow keywords are more common in Higher-order functions, so-called Higher-order functions, which are the parameters of a function or the return value is a function type.



The most common example is theSequenceprotocolmapmethod.


letarray = [1,2,3]letarray.map{$02}


Because the map function is passing in a closure, the closure may throw an Error. Errors thrown by parameters are passed up to the map function upwards.



Like what:


 
enum MapError : Error{
    case invalid
}
func customMapper(input:Int) throws -> Int{
    if input < 10{
        throw MapError.invalid
    }
    return input + 1
}
let array = [1,2,3]
let result = array.map(customMapper)

This is a compilation that does not pass.


Call when required: follow the throws keyword mentioned above to


 
do {
    let result = try array.map(customMapper)
} catch let error{

}


so, That's The essence of the rethrows Keyword.


The Rethrows keyword means that the function itself is throws when the parameter closure is marked as Throws. If the parameter closure does not throw an error, the function does Not.


With this keyword, you don't have to try-catch every Time.


Result type


We know that a function executes either successfully or Fails. When we succeed we want to return the data, we want to get the error message when we fail, this is the result type, a typical result type is as Follows:


enum Result<T>{
    case success(T)
    case failure(error:Error)
}


With the result type, you no longer need an optional value or Do-try-catch to wrap your Code.



We rewrite the above marry function with the result type:


extension Person{
    func marry(with another: Person)  -> Result<Bool>{
        guard self.sex != another.sex else{
            return .failure(error: MarryError.wrongSex)
        }
        guard self.money + another.money > 100000 else {
            return .failure(error: MarryError.lackMoney)
        }
        return .success(true)
    }
}


then, so call


 
let tom = Person(sex: .male, money: 100000)
let jack = Person(sex: .male, money: 100000)
let result = tom.marry(with: jack)
switch result {
    case let .success(value):
        print(value)
    case let .failure(error):
        print(error)
}
result Chain


There is an optional chain in Swift that handles successive invocations of multiple optional values. similarly, We can add chained calls to the result type:


    • If the previous call result is. success, continue calling the next
    • If the previous call result is. failure, The failure is passed to the next


We can use extension to achieve


extension Result{
    func flatMap<V>(transform:(T) throws -> (V)) rethrows -> Result<V>{
        switch self {
        case let .failure(error):
            return .failure(error: error)

        case let .success(data):
            return .success(try transform(data))
        }
    }
}


So you can call it that way.


resut.flatMap({//conversion 1}).flatMap(//conversion 2)...


Once failed, there is a flatmap conversion failure in the middle, then the conversion logic will not be executed


Advanced: The result type is large in the swift version of promise, see Promisekit's Source code, promise make asynchronous processing Elegant.

assert/precondition


At the beginning of this article, we dropped to objective C's assertion, and Swift also asserted Support. In swfit, an assertion is a function:


 
func assert(_ condition: @autoclosure () -> Bool, 
              _ message: @autoclosure () -> String = default, 
                   file: StaticString = #file, 
                   line: UInt = #line)


Assertions are only checked in debug mode to help developers find problems in the Code.



If you need to also check in relase mode, use theprecondition


 
func precondition(_ condition: @autoclosure () -> Bool, 
              _ message: @autoclosure () -> String = default, 
                   file: StaticString = #file, 
                   line: UInt = #line)
Bridge to Objective C


For the following objective methods that use Nserror to handle errors


//NSFileManager- (BOOL)removeItemAtURL:(NSURL *)URL error:(NSError * _Nullable *)error;


In swift, it is automatically converted into


func removeItem(atURLURL) throws


however, There are some problems with pure Swfit error bridging the objective C. Because Nserror needdomainandcodeso on detailed Information.



We can let Swift's error implement the CUSTOMNSERROR protocol to provide these required information.


 
 
enum MarryError : Error{
    case lackMoney
    case wrongSex
}
extension MarryError : CustomNSError{
    static let errorDomain = "com.person.marryError"
    var erroCode:Int{
        switch self {
        case .lackMoney:
            return -100001
        case .wrongSex:
            return -100002
        }
    }
    var errorUserInfo:[String:Any]{
        return [:]
    }
}


related, There are also two protocolsLocalizedErrorandRecoverableError



Error handling in Swift


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.