エラーをスローする関数があります。この関数には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:
}
}
スローするクロージャを定義すると、次のようになります。
enum MyError: ErrorType {
case Failed
}
let closure = {
throw MyError.Failed
}
次に、このクロージャーのタイプは() throws -> ()
であり、パラメーターとしてこのクロージャーを受け取る関数は、同じパラメータータイプでなければなりません。
func myFunction(completion: () throws -> ()) {
}
この関数は、completion
クロージャを同期的に呼び出すことができます。
func myFunction(completion: () throws -> ()) throws {
completion()
}
関数のシグネチャにthrows
キーワードを追加するか、try!
を使用して補完を呼び出す必要があります。
func myFunction(completion: () throws -> ()) {
try! completion()
}
または非同期:
func myFunction(completion: () throws -> ()) {
dispatch_async(dispatch_get_main_queue(), { try! completion() })
}
最後のケースでは、エラーをキャッチすることができません。
したがって、eventStore.requestAccessToEntityType
メソッドのcompletion
クロージャで、メソッド自体のシグネチャに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:
}
}
スローは同期であるため、スローしたい非同期関数には、次のようなスローする内部クロージャーが必要です。
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
}
}
次に、呼び出しサイトで次のように使用します。
insertEventToDefaultCalendar(EKEvent()) { response in
do {
try response()
// Success
}
catch {
// Error
print(error)
}
}
この場合、これは不可能です。その完了ハンドラはthrows
(およびrethrows
を持つメソッド)で宣言する必要がありますが、これはできません。
これらすべてのスローは、Objective-CのNSError **
の異なる表記法にすぎないことに注意してください(inoutエラーパラメータ)。 Objective-Cコールバックにはinoutパラメーターがないため、エラーを渡す方法はありません。
エラーを処理するには、別の方法を使用する必要があります。
一般に、Obj-CのNSError **
またはSwiftのthrows
は、エラー処理が同期的に機能するため、非同期メソッドではうまく機能しません。
requestAccessToEntityType
は、非同期で動作します。最終的に完了ハンドラが実行されると、関数はすでに返されています。したがって、提案されている方法でクロージャーからエラーをスローすることはできません。
承認部分がイベント挿入とは別に処理されるようにコードをリファクタリングし、承認ステータスが期待どおり/必要であることがわかっている場合にのみinsertEventToDefaultCalendar
を呼び出す必要があります。
1つの関数ですべてを処理したい場合は、セマフォ(または同様の手法)を使用して、非同期コード部分が関数に対して同期的に動作するようにすることができます。
func insertEventToDefaultCalendar(event :EKEvent) throws {
var accessGranted: Bool = false
let eventStore = EKEventStore()
switch EKEventStore.authorizationStatusForEntityType(.Event) {
case .Authorized:
accessGranted = true
case .Denied, .Restricted:
accessGranted = false
case .NotDetermined:
let semaphore = dispatch_semaphore_create(0)
eventStore.requestAccessToEntityType(EKEntityType.Event, completion: { (granted, error) -> Void in
accessGranted = granted
dispatch_semaphore_signal(semaphore)
})
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
}
if accessGranted {
do {
try insertEvent(eventStore, event: event)
} catch {
throw CalendarEventError.Failed
}
}
else {
throw CalendarEventError.AccessDenied
}
}
throw
では関数を作成できませんが、ステータスまたはエラーとともにclosure
を返します。不明な場合は、コードをいくつか示します。