2015-10-28 12 views
7

Ich versuche, eine throws zu meiner bestehenden Funktion mit einem Abschluss-Handler hinzufügen, aber ich bekomme immer eine Warnung sagen no calls throwing functions occur within try expression. In dem Abschnitt, wo ich die Fehler werfen, bekomme ich einen Fehler mit einem Körnchen Salz zu sagenKann keine Würfe zur Arbeit mit Funktion mit Abschlussbehandler

invalid conversion from throwing function of type '() throwing -> Void' to non-throwing function type.

enum LoginError: ErrorType { 
    case Invalid_Credentials 
    case Unable_To_Access_Login 
    case User_Not_Found 
} 

@IBAction func loginPressed(sender: AnyObject) { 

    do{ 
     try self.login3(dict, completion: { (result) -> Void in 

          if (result == true) 
          { 
           self.performSegueWithIdentifier("loginSegue", sender: nil) 
          }      
         }) 
        } 
        catch LoginError.User_Not_Found 
        { 
         //deal with it 
        } 
        catch LoginError.Unable_To_Access_Login 
        { 
         //deal with it 
        } 
        catch LoginError.Invalid_Credentials 
        { 
         //deal with it 
        } 
        catch 
        { 
         print("i dunno") 
        } 

} 

func login3(params:[String: String], completion: (result:Bool) throws -> Void) 
{ 
    //Request set up 
    let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in 
    do { 
      let json = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableLeaves) as? NSDictionary 
      if let parseJSON = json 
      { 
       let userID = parseJSON["user_id"] as? Int 
       let loginError = parseJSON["user_not_found"] as? String 
       let validationError = parseJSON["invalid_credentials"] as? String 
       let exception = parseJSON["unable_to_access_login"] as? String 

       var responseArray = [(parseJSON["user_id"] as? Int)] 
       if userID != nil 
       { 
        dispatch_async(dispatch_get_main_queue()) { 
         completion(result:true) 
        } 

       } 
       else if loginError != "" 
       { 
        dispatch_async(dispatch_get_main_queue()){ 
         completion(result: false) 
         self.loginErrorLabel.text = loginError 
         throw LoginError.User_Not_Found 
        } 
       } 
       else if validationError != "" 
       { 
        dispatch_async(dispatch_get_main_queue()){ 
         completion(result:false) 
         self.validationErrorLabel.text = validationError 
         throw LoginError.Invalid_Credentials 
        } 

       } 
       else if exception != nil 
       { 
        dispatch_async(dispatch_get_main_queue()){ 
         completion(result:false) 
         self.exceptionErrorLabel.text = "Unable to login" 
         throw LoginError.Unable_To_Access_Login 
        } 
       } 
      } 
      else 
      { 
      } 
     } 
     catch let parseError { 
      // Log the error thrown by `JSONObjectWithData` 
     }) 

     task.resume() 

} 
+0

Das ist eine asynchrone Funktion. In Ihrem Callback müssen Sie einen Fehler zurückgeben, nicht werfen, bis Sie Ihr Programm geworfen haben, ist bereits von dieser Funktion zurückgekehrt, daher funktioniert es nicht. – sbarow

+0

Ich bin ein bisschen verwirrt. Soll der Callback ausgelöst werden oder soll die Funktion, die den Callback enthält, geworfen werden? Ich warte auf die Antwort vom Server, um festzustellen, welche Art von Fehler zu werfen, die mich denken, dass der Callback werfen muss. – Brosef

+0

@Victor Sigler Antwort ist, was Sie suchen. – sbarow

Antwort

11

Was Sie tun können, um den Fehler in einem throwable Verschluss wie in dem folgenden Code Einkapselung zu erreichen w Hut Sie wollen:

func login3(params:[String: String], completion: (inner:() throws -> Bool) ->()) { 

    let task = session.dataTaskWithRequest(request, completionHandler: { data, response, error -> Void in 

      let json = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableLeaves) as? NSDictionary 

      if let parseJSON = json { 
       let userID = parseJSON["user_id"] as? Int 
       let loginError = parseJSON["user_not_found"] as? String 
       let validationError = parseJSON["invalid_credentials"] as? String 
       let exception = parseJSON["unable_to_access_login"] as? String 

       var responseArray = [(parseJSON["user_id"] as? Int)] 
       if userID != nil { 
       dispatch_async(dispatch_get_main_queue()) { 
        completion(inner: { return true }) 
       } 

      } 
      else if loginError != "" 
      { 
       dispatch_async(dispatch_get_main_queue()) { 
        self.loginErrorLabel.text = loginError 
        completion(inner: { throw LoginError.User_Not_Found }) 
       } 
      } 
      else if validationError != "" 
      { 
       dispatch_async(dispatch_get_main_queue()) { 
        self.validationErrorLabel.text = validationError 
        completion(inner: {throw LoginError.Invalid_Credentials}) 
       } 
      } 
      else if exception != nil 
      { 
       dispatch_async(dispatch_get_main_queue()){ 
        self.exceptionErrorLabel.text = "Unable to login" 
        completion(inner: {throw LoginError.Unable_To_Access_Login}) 
       } 
      } 
     } 
     else 
     { 
     } 
    } 

    task.resume() 
} 

Und Sie es wie in der folgenden Art und Weise können auch anrufen:

self.login3(dict) { (inner:() throws -> Bool) -> Void in 
    do { 
    let result = try inner() 
    self.performSegueWithIdentifier("loginSegue", sender: nil) 
    } catch let error { 
     print(error) 
    } 
} 

Der Trick besteht darin, dass die login3 Funktion zusätzliche Schließung nimmt eine 'inner' des Typs () throws -> Bool genannt. Diese Schließung liefert entweder das Ergebnis der Berechnung oder es wird geworfen.Der Verschluß selbst wird während der Berechnung durch eine von zwei Mitteln konstruiert:

  • Im Falle eines Fehlers: inner: {throw error}
  • Bei Erfolg: inner: {return result}

empfehle ich Ihnen einen ausgezeichneten Artikel über die Verwendung try/catch in async Anrufe Using try/catch in Swift with asynchronous closures

Ich hoffe, das hilft Ihnen.

+0

Oben funktioniert nicht, wenn Sie den Login-Fehler im Completion-Block auf eine andere Funktion werfen wollen. –

0

die unten lesen. Ich habe noch nicht viel mit Swift 2.0 gearbeitet:

Sie haben eine "dataTask" erstellt task wer ist der Abschluss-Handler hat einen Wurf drin, aber der einzige tatsächliche Code in Ihrer login3-Methode ist task.resume(). Der Beendigungshandler wird erst ausgeführt, nachdem login3 zurückgegeben wurde. (In der Tat, es ist ein Parameter für ein anderes Objekt, so dass der Compiler keine Ahnung hat, was mit diesem Code passieren wird.)

Wie ich es verstehe, muss die tatsächliche Top-to-bottom-Körper Ihrer login3 Methode enthalten werfen. Da es sich um eine asynchrone Methode handelt, können Sie das nicht tun. Also, lassen Sie Ihre login3 Funktion nicht werfen. Stattdessen muss er ein Fehlerobjekt an seinen Abschluss-Handler übergeben.

0

In meinem Eindruck wird dies durch die Signatur Ihrer Funktionen verursacht. In @IBAction func loginPressed(sender: AnyObject) müssen Sie nicht versuchen, wenn Sie login3 aufrufen, da die Funktion selbst nicht als werfen markiert ist, aber der Abschluss-Handler ist. Aber in der Tat wird die Fertigstellung Schließung login3 nie werfen, wie Sie alle werfen Funktionen in einem do {} catch {} Block ausführen, so können Sie versuchen, die throws Annotation von der login3 Abschluss Schließung entfernen und auch die Schließung aufrufen, wenn Sie einen Fehler in login3 mit einem entsprechendes Ergebnis. Auf diese Weise behandeln Sie alle Wurffunktionen innerhalb von login3 in einem do {} catch {} Block und rufen den Beendigungshandler mit einem geeigneten Wert auf.

Allgemein ist mir auch nicht bewusst, dass Sie ohne einen vorangestellten do {} Block wie in der @IBAction blockieren können.

+0

Das Eingeben von 'login3' ist auch nicht notwendig, da Sie Ihre zuvor geworfenen Fehler direkt im catch-Block abfangen können. Was ich empfehlen würde, ist, gegen Fehlerbedingungen zu prüfen und die Beendigung des Abschlusses mit einem Fehler oder in Ihrem Fall mit falsch als Ergebnis aufzurufen. Wenn Sie die Abschlussparameter ändern, sodass Sie einen Fehler an den Abschluss übergeben können, können Sie in Ihrer @IBAction nach anderen Fehlerbedingungen suchen. – Daehn

0

Sie sind für X zu fragen, und ich bin der Beantwortung Y, aber nur für den Fall ...

Es gibt immer die Möglichkeit, die Wurffähigkeit auf Ihre Funktion anstelle der Fertigstellung Handler hinzuzufügen:

func login3(params:[String: String], completion: (result:Bool) -> Void) throws { 
    ... 
} 

Dann können Sie es von innen IBAction nennen:

do { 
    try self.login3(dict) { result -> Void in 
     ... 
    } 
} catch { 
    print(error) 
} 
+0

Ich habe versucht, Ihren Ansatz, aber war auf einem Fehler gefangen. Bei 'dispatch_async (dispatch_get_main_queue()) { self.loginErrorLabel.text = loginError Vervollständigung (inner: {throw LoginError.User_Not_Found}) }' Ich bekomme immer noch den Fehler, der besagt, dass eine ungültige Konvertierung vom Typ '() werfen -> Void 'zu nicht-werfen Funktionstyp. – Brosef

+0

@Brosef Mit dieser Version brauchen Sie Ihre Ergänzungen nicht zu ändern, behalten Sie sie wie sie waren (keine "inneren" Sachen). Ich brauche nichts anderes als das, was ich zeige. – Moritz

+0

Ich versuche, Ihre Lösung zu implementieren, aber wenn ich den 'throw' in' dispatch_async' platziere, erhalte ich den Fehler, den ich in meinem vorherigen Kommentar beschrieben habe. – Brosef