Why can't Microsoft Azure (or Swift in general) update a variable to return after a table query?

I closely followed the Microsoft Azure documentation when testing the tables successfully (inserting, reading, and updating items in the database works fine), but at the end of a simple method, right from the document:

func getAllEventIDs() -> [String] { var events:[String] = [] //this is to be updated let delegate = UIApplication.sharedApplication().delegate as! AppDelegate let client = delegate.client! //boiler-plate for azure let itemTable = client.tableWithName("Events") itemTable.query().readWithCompletion { //something about this query block might be preventing successful initialization to the events array (result:MSQueryResult!, error) in //usually error-checking here for item in result.items { events.append(item.valueForKey("id") as! String) //returning events here... } //...and here (almost) work, since Swift expects a return of type Void } return events //still empty } 

I cannot pass an array as a parameter , since the .append function will mutate this array.

The return value is just the starting empty array. However, this question seems to be heavily dependent on the Azure request code block, not Swift itself.

The simplest example:

 func returnValueArray() -> [Int] { var array:[Int] = [0,0,0,0] var num:Int = 3 for var n = 0; n < array.count; n++ { array[n] = num } return array } 

This returns [3,3,3,3]. Again, not a Swift issue, but Swift may have shown the Azure return problem.

How to return the desired updated array at the end of the request method? Is it possible to pass a method to a mutable array, add values ​​and then return this array?

+5
source share
1 answer

You asked: "How can I return the desired, updated array at the end of the request method?" Short answer: you cannot.

This is the fundamental value of asynchronous coding.

The readWithCompletion method is asynchronous. It pauses your request for processing in the background, and returns immediately.

Your return events //still empty code is executed before your read request has even started processing.

You need to reorganize your getAllEventIDs method to take the completion block as a parameter. This completion block will be passed by your event array. Then inside the completion block for readWithCompletion you call the completion block for your getAllEventIDs method.

So when you call getAllEventIDs , you pass it a completion block that does what you need to do with the array of events.

EDIT:

I created a Github project called SwiftCompletionHandlers that illustrates this and how to handle it. It has an example AsyncManager class that simulates asynchronous loading.

https://github.com/DuncanMC/SwiftCompletionHandlers

He has a method that looks like this:

 func asyncFetchImage(#imageName: String, completion: ( image: UIImage?, status: String) -> ()) { println("Entering \(__FUNCTION__)") //Simulate a network operation by waiting a few seconds //before loading an image let nSecDispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(3.0 * Double(NSEC_PER_SEC))) let queue = dispatch_get_main_queue() dispatch_after(nSecDispatchTime, queue) { () -> Void in let result = UIImage(named: imageName) println("Loading image in background") let status = result != nil ? "image loaded" : "Error loading image" println("About to call completion handler") completion(image: result, status: status) } println("Leaving \(__FUNCTION__)") } 

A file name and a termination block are required. The completion block receives the passed optional UIImage and a string status message.

Once the download is complete, the method calls the completion block (aka clos.)

Here is the code that calls this method:

  @IBAction func loadImage(sender: UIButton) { let theAsyncManager = AsyncManager.sharedAsyncManager println("about to call asyncFetchImage") theAsyncManager.asyncFetchImage(imageName: "wareto_blue_150x115") { (image, status) -> () in println("Beginning completion block") self.theImageView.image = image println("In completion block, status = \(status)") } println("After call to asyncFetchImage") } 

The output of println statements is key to understanding what is happening:

 about to call asyncFetchImage Entering asyncFetchImage(imageName:completion:) Leaving asyncFetchImage(imageName:completion:) After call to asyncFetchImage Loading image in background About to call completion handler Beginning completion block In completion block, status = image loaded 

Please note that the messages "Exit asyncFetchImage" and "After calling asyncFetchImage" are printed before the message "Download image in the background." Then “About the call to the completion handler” appears, then “Start of the completion block”.

Thus, the actual operation of async does not even start until the loadImage function returns.

If you do not understand what I am describing, download the project and try it, then set breakpoints and see how they are executed.

+8
source

All Articles