从嵌套在函数中的闭包中快速抛出

sha*_*oga 20 throws ios swift2

我有一个抛出错误的函数,在这个函数中我有一个inside a闭包,我需要从它的完成处理程序抛出错误.那可能吗 ?

到目前为止,这是我的代码.

enum CalendarEventError: ErrorType {
    case UnAuthorized
    case AccessDenied
    case Failed
}

func insertEventToDefaultCalendar(event :EKEvent) throws {
    let eventStore = EKEventStore()
    switch EKEventStore.authorizationStatusForEntityType(.Event) {
    case .Authorized:
        do {
            try insertEvent(eventStore, event: event)
        } catch {
            throw CalendarEventError.Failed
        }

    case .Denied:
        throw CalendarEventError.AccessDenied

    case .NotDetermined:
        eventStore.requestAccessToEntityType(EKEntityType.Event, completion: { (granted, error) -> Void in
            if granted {
                //insertEvent(eventStore)
            } else {
                //throw CalendarEventError.AccessDenied
            }
        })
    default:
    }
}
Run Code Online (Sandbox Code Playgroud)

mix*_*xel 16

定义抛出的闭包时:

enum MyError: ErrorType {
    case Failed
}

let closure = {
    throw MyError.Failed
}
Run Code Online (Sandbox Code Playgroud)

那么这个闭包的类型是() throws -> ()和作为参数获取此闭包的函数必须具有相同的参数类型:

func myFunction(completion: () throws -> ()) {
}
Run Code Online (Sandbox Code Playgroud)

这个函数可以调用completion闭包同步:

func myFunction(completion: () throws -> ()) throws {
    completion() 
}
Run Code Online (Sandbox Code Playgroud)

并且你必须添加throws关键字到函数签名或调用完成try!:

func myFunction(completion: () throws -> ()) {
    try! completion() 
}
Run Code Online (Sandbox Code Playgroud)

或异步:

func myFunction(completion: () throws -> ()) {
    dispatch_async(dispatch_get_main_queue(), { try! completion() })
}
Run Code Online (Sandbox Code Playgroud)

在最后一种情况下,您将无法捕获错误.

因此,如果方法中的completion闭包eventStore.requestAccessToEntityType和方法本身没有throws签名或者completion是异步调用那么你就不throw能从这个闭包中获取.

我建议您使用以下函数实现将错误传递给回调而不是抛出它:

func insertEventToDefaultCalendar(event: EKEvent, completion: CalendarEventError? -> ()) {
    let eventStore = EKEventStore()
    switch EKEventStore.authorizationStatusForEntityType(.Event) {
    case .Authorized:
        do {
            try insertEvent(eventStore, event: event)
        } catch {
            completion(CalendarEventError.Failed)
        }

    case .Denied:
        completion(CalendarEventError.AccessDenied)

    case .NotDetermined:
        eventStore.requestAccessToEntityType(EKEntityType.Event, completion: { (granted, error) -> Void in
            if granted {
                //insertEvent(eventStore)
            } else {
                completion(CalendarEventError.AccessDenied)
            }
        })
    default:
    }
}
Run Code Online (Sandbox Code Playgroud)


Sul*_*han 6

在这种情况下,这是不可能的 - 完成处理程序必须用throws(和方法rethrows)声明,而这个不是.

请注意,所有抛出只是NSError **Objective-C(inout error参数)中的不同符号.Objective-C回调没有inout参数,因此无法传递错误.

您将不得不使用不同的方法来处理错误.

通常,NSError **在Obj-C或throwsSwift中,异步方法不能很好地兼容,因为错误处理可以同步工作.


Raf*_*bre 6

因为throw是同步的,所以想要抛出的异步函数必须有一个抛出的内部闭包,例如:

func insertEventToDefaultCalendar(event :EKEvent, completion: (() throws -> Void) -> Void) {
    let eventStore = EKEventStore()
    switch EKEventStore.authorizationStatusForEntityType(.Event) {
    case .Authorized:
        do {
            try insertEvent(eventStore, event: event)
            completion { /*Success*/ }
        } catch {
            completion { throw CalendarEventError.Failed }
        }

        case .Denied:
            completion { throw CalendarEventError.AccessDenied }

        case .NotDetermined:
            eventStore.requestAccessToEntityType(EKEntityType.Event, completion: { (granted, error) -> Void in
                if granted {
                    let _ = try? self.insertEvent(eventStore, event: event)
                    completion { /*Success*/ }
                } else {
                    completion { throw CalendarEventError.AccessDenied }
                }
        })
        default:
            break
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,在呼叫站点,您可以像这样使用它:

   insertEventToDefaultCalendar(EKEvent()) { response in
        do {
            try response()
            // Success
        }
        catch {
            // Error
            print(error)
        }
    }
Run Code Online (Sandbox Code Playgroud)