2014-07-17 11 views
13

Ich versuche, eine Funktion zu schreiben, die eine asynchrone GET-Anfrage ausführt und die Antwort zurückgibt (wie jeder Datentyp, aber hier ist es als NSData). How to use NSURLConnection completionHandler with swiftDaten aus der Vervollständigung erhaltenHandler in Swift in NSURLConnection

func getAsynchData() -> NSData { 
    var dataOutput : NSData 
    let url:NSURL = NSURL(string:"some url") 
    let request:NSURLRequest = NSURLRequest(URL:url) 
    let queue:NSOperationQueue = NSOperationQueue() 

    NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in 
      /* this next line gives the below error */ 
      dataOutput = data 
    }) 
    return dataOutput 
} 

aber ich erhalte eine Fehlermeldung: schaurig

error: variable 'dataOutput' captured by a closure before being initialized 

Ich habe versucht, den Wert aus dem completionHandler Rückkehr, aber es erfordert eine Lücke Rückkehr, die

auf diese Frage basiert erinnert mich an meine Hoffnung, dieses Problem ohne Hilfe zu lösen ...: D

Ich habe mir angeschaut: How to use completionHandler Closure with return in Swift? aber das beantwortet meine Frage nicht wirklich. Mein Ziel ist es, die Daten aus meiner asynchronen Anfrage aus dem Block zu holen, um sie an anderer Stelle in meinem Code zu verwenden. Soll ich die gesamte Arbeit mit dieser Anfrage in diesem Block erledigen und die Daten nicht rausholen?

Vielen Dank!

EDIT

ok so dass ich eine Option, die ich denke, könnte funktionieren, aber es scheint nicht richtig zu mir zu sein scheint. Kann mir jemand sagen, ob dies der beste Weg ist, um mein Ziel zu erreichen?

func doThingsWithData(data: NSData) -> String { 
    /* code to extract string from NSData */ 
    return somestring 
} 
func getAsynchData() { 
    let url:NSURL = NSURL(string:"some url") 
    let request:NSURLRequest = NSURLRequest(URL:url) 
    let queue:NSOperationQueue = NSOperationQueue() 

    NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in 
      /* this next line gives the below error */ 
      doThingsWithData(data) 
    }) 
} 

EDIT 2 -> reagiert

Dank rückgängig zu machen, rückgängig machen. Deine Antwort macht Sinn für mich. Hier ist mehr von dem Puzzle. Ich habe eine Klasse, die mein API-Handler ist. Ich möchte diese Klasse instanziieren und eine Funktion aufrufen, um Daten von der API zu erhalten. Ich würde lieber alle Daten mit einem api-Aufruf erhalten, statt jedes Mal getrennte Aufrufe für jeden Wert, den ich rausholen muss, da ein einziger API-Aufruf alle Daten enthält, die ich brauche, aber das könnte eine ganz andere Antwort sein. Hier ist der Code:

class GetInfoFromAPI { 

    func getSpecificValue(index : String) -> String { 
     /* I assume I need to send the values from this function, yea? but how do I get them here? */ 
    } 

    func doThingsWithData(data: NSData) -> String { 
     /* code to extract string from NSData */ 
     var error: NSError? 
     let jsonDict = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &error) as NSDictionary 

     specificValue1 : String = jsonDict.valueForKey("value1") as String 
     specificValue2 : String = jsonDict.valueForKey("value2") as String 
     specificValue3 : String = jsonDict.valueForKey("value3") as String 

     /* I want to get these ^^^ values into the ViewController below */ 
    } 

    func getAsynchData() { 
     let url:NSURL = NSURL(string:"some url") 
     let request:NSURLRequest = NSURLRequest(URL:url) 
     let queue:NSOperationQueue = NSOperationQueue() 

     NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in 
      /* this next line gives the below error */ 
      doThingsWithData(data) 
     }) 
    } 
} 


class ViewController: UIViewController { 

    @IBOutlet var labelVariable1: UILabel 
    @IBOutlet var labelVariable2: UILabel 
    @IBOutlet var labelVariable3: UILabel 

    let apiInstance = GetInfoFromAPI() 

    @IBAction func buttonTapped(sender : AnyObject) { 
     labelVariable1 = apiInstance.getSpecificValue(1) 
     labelVariable2 = apiInstance.getSpecificValue(2) 
     labelVariable3 = apiInstance.getSpecificValue(3) 
    } 

} 

Vielen Dank für die Zeit nehmen, um meine Fragen zu beantworten. Ich bin neu zu schnell, und Stack-Überlauf ist immens hilfreich!

+2

Wie wäre es 'var dataOutput: NSData!'? – cahn

+0

Wenn ich 'var dataOutput: NSData!' Benutze, erhalte ich den folgenden Fehler: 'fataler Fehler: unerwartet wurde nil beim Entpacken eines optionalen Wertes gefunden'. Ich glaube, das liegt wahrscheinlich daran, dass es sich um eine asynchrone Anfrage handelt und nicht sofort einen Rückgabewert hat. Das führt mich zu der Annahme, dass mein zweiter Schnitt wahrscheinlich der bessere Weg ist, – cosmikwolf

+0

zu gehen, aber gut zu wissen! Wird eine Variable einfach implizit entpackt, um auf verschachtelte Funktionen zuzugreifen? Ich habe mich geäußert, als ich Ihre Antwort sah: D – cosmikwolf

Antwort

6

Lassen Sie mich versuchen, dies zu erklären - es ist ein Missverständnis der Threading, eine, mit der ich mich zuerst bemühte. Ich werde versuchen, die Dinge ein wenig zu vereinfachen. Wenn Sie diesen Code ausführen:

NSLog("Log 1") 

NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in 
     NSLog("Log in Completion Block") 
}) 

NSLog("Log after request") 

Sie gehen Ausgabe zu erhalten, die wie folgt aussieht:

Log 1 
Log after request 
Log in completion block 

Lassen Sie mich ein Diagramm machen, ein bisschen eine Zeitleiste:

    "Log 1" (before request is sent) 
             | 
             | 
          Request sent over Internet 
             | 
            / \ 
        "Log after request" | 
       This is logged *before* | 
      the other one, because this | 
      one doesn't have to wait for | 
       the network to respond. | 
             | 
The method finishes and returns a value. | 
------------------------------------------------------------------------------ 
             | The network finally responds, 
             | and the completion block is run. 
             | 

             "Log in completion block" 

Die vertikale Linie ist der Punkt, an dem die Methode beendet und zurückgegeben wird. In allen Fällen hat Ihre Methode dem Aufrufer bereits einen Wert zurückgegeben, bevor der Abschlussblock ausgeführt wird. Du kannst nicht linear darüber nachdenken.

Am I supposed to do all of the work with this request in this block and not get the data out?

Ja, im Wesentlichen. Ich kann mehr helfen, wenn Sie mir den Code zeigen, der an erster Stelle getAsynchData() aufruft.

+0

Danke Undo, das war sehr hilfreich. Ich habe dem Hauptteil oben mehr Code hinzugefügt, der ein größeres Bild des Problems liefert. – cosmikwolf

+1

Danke! Ich bin im Moment ein wenig beschäftigt, ich werde versuchen, in ein oder zwei Tagen zurück zu kommen. In der Zwischenzeit denke ich, dass Sie sich das Konzept der Delegierten und Protokolle ansehen müssen. Das sollte dir einen guten Start geben. – Undo

+0

danke, wird es tun! Ich freue mich auf ihre Antwort. – cosmikwolf