2015-10-19 19 views
9

私はエラーをスローする関数を持っています。この関数には、完了ハンドラからエラーをスローする必要がある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: 
    } 
} 
+0

あなたは 'eventStore.'部のブール外を保存し、エラーを投げるのではなく、内部にそれを変更して、外部のブール値をチェックし、例外がArc676 @' C必要に応じて – Arc676

+2

で投げることができompletion'クロージャーは非同期的に呼ばれ、 'completion'が呼び出される前に' insertEventToDefaultCalendar'が返されるので不可能です。 – mixel

+0

@shannogaあなたのケースの回避策を使って私の答えを更新しました。 – mixel

答えて

6

- その完了ハンドラは、(rethrowsとし、法)throwsと宣言しなければならないであろうと、この1ではありません。

Objective-C(inoutエラーパラメータ)のNSError **では、投げつけが全く異なることに注意してください。 Objective-Cコールバックにはinoutパラメータがないため、エラーを引き継ぐ方法はありません。

エラーを処理するには、別の方法を使用する必要があります。

一般に、SwiftのObj-CまたはthrowsNSError **は、エラー処理が同期して動作するため、非同期メソッドではうまく動作しません。

0

throwで機能させることはできませんが、ステータスまたはエラーのあるclosureを返してください! 明確でない場合は、私はいくつかのコードを与えることができます。

3

あなたはスロー閉鎖定義:ITのこの機能を

func myFunction(completion:() throws ->()) { 
} 

enum MyError: ErrorType { 
    case Failed 
} 

let closure = { 
    throw MyError.Failed 
} 

を、このクロージャのタイプは() throws ->()で、パラメータとして、この閉鎖をとる関数は、同じパラメータの型を持っている必要がありますあなたはcompletionクロージャを同期して呼び出すことができます:

func myFunction(completion:() throws ->()) throws { 
    completion() 
} 

あなたが署名機能やtry!で完了を呼び出すためにthrowsキーワードを追加する必要があります。

func myFunction(completion:() throws ->()) { 
    try! completion() 
} 

または非同期:最後のケースでは

func myFunction(completion:() throws ->()) { 
    dispatch_async(dispatch_get_main_queue(), { try! completion() }) 
} 

エラーをキャッチすることができません。だから、

completioneventStore.requestAccessToEntityType方法で閉鎖と方法自体はthrowscompletionその署名または場合を持っていない場合は、非同期的に、あなたはないthrowこの閉鎖からできると呼ばれています。

私はあなたにそれを投げるのではなく、callbackにエラーを渡すあなたの関数の次の実装をお勧め:

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: 
    } 
} 
+0

最初のコードボックスの入力ミスは、 "TextError"ではなく "MyError"にする必要があります。 – RenniePet

+0

@RenniePet更新されました。ありがとう。 – mixel

0

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 
    } 
} 
+0

'semaphore'の使用は本当に悪い考えです。なぜなら、' insertEventToDefaultCalendar'と 'completion'クロージャは同じスレッドで呼び出され、このスレッドは' dispatch_semaphore_wait'の後でブロックされ、 'completion'と他のコードは決して呼び出されないからです。 – mixel

+0

シミュレータ内のコードをテストし、それが機能しました。 – iOSX

+0

デバイスのテストも成功しました。 – iOSX

4

投げが同期しているので、投げたい非同期機能は、このようなスロー内側の閉鎖を、持っている必要があります。

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) 
     } 
    } 
関連する問題