Saving Values ​​at Completion Handlers - Swift

I create a completionHandler that returns a dictionary, but when I call this method in another class, its value is nil.

 func fetchLatestPosts(completionHandler: (responseDict: NSDictionary) -> Void in { // Bunch of JSON Parsing for dictionary to be completed var theDictionary = JSON Dictionary value responseDict = theDictionary as NSDictionary } 

Then I try to call it in another class, so that the value that the fetchLatestPost Dictionary can be viewed, and the data presented based on this. For instance,

 func parseDictionary() { NetworkManager.sharedInstance.fetchLatestPosts(responseDict: NSDictionary) if (responseDict != nil) { println("Dictionary is not empty") } 

The problem is that when I call the fetchLatestPosts function, I get nil when I try to call the dictionary.

In short, my question is, how can I assign values ​​to the completion handler, and can be called elsewhere in the Xcode project?

+5
source share
1 answer

Your question is distorted. Your sample code is invalid and does not display a completion handler.

I suspect that you misunderstood how asynchronous requests and completion handlers work. Here is the sequence.

  • Call a function and provide a completion handler
  • The function returns immediately, and while in future creatures that execute the request
  • The next line after the completion of the launch handler until the request even starts processing. The application continues to work. There is no result because the request is not yet complete.
  • Asynchronous request ends. The original method calls the completion handler. NOW the result is available in the completion handler code. The completion handler code does everything necessary to process the results.

Here is a realistic example of how an asynchronous process can work with completion handlers: (You can find a working Xcode project that demonstrates this code on Github in this link )

First, the AsyncManager class, which simulates downloading an image from the Internet:

 class AsyncManager { static let sharedAsyncManager = AsyncManager() func asyncFetchImage(#imageName: String, completion: (image: UIImage?, status: String) -> ()) { println("Entering \(__FUNCTION__)") //Simulate a network operation by waiting 3 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__)") } } 

This is a singleton. It has a static let sharedAsyncManager that you use to get one instance of AsyncManager.

AsyncManager provides one asyncFetchImage method that takes an image name and a completion block. The function does not return any result, because it returns immediately before the image is loaded.

The code does not actually download the image from the Internet. Instead, it simply launches the GCD dispatch_after call to wait 3 seconds before loading the image and calling the completion block.

Now I create the ViewController class and give it the "Load Image" button. I am creating an IBOutlet theImageView for a UIImageView that will display the image I'm about to load.

I am writing an IBAction method for a Load Image button. Here's what this IBAction looks like:

 @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") } } 

Now, the fun part. Here's the sequence of events:

I click the loadImage button. The IBAction method is executed.

It gets a link to the async download manager syntax.

Displays a message, then calls the AsyncManager.asyncFetchImage method. The AsyncManager.asyncFetchImage method displays a recording message, queues a call to load an image after 3 seconds, displays a β€œleave” message and returns before the image is loaded . There is nothing to return because the image loading code is not already running.

The View controller's loadImage method displays the message "After calling asyncFetchImage" and returns.

After a few seconds, the code inside asyncFetchImage is executed. It displays a message, loads an image, and then calls the completion handler.

Here is the set of messages that you return when you run the above code:

 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 

Notice that the last line is loadImage IBAction:

 println("After call to asyncFetchImage") 

displays a message until a message about loading the image appears. The code call for asyncFetchImage returns immediately before any work is done. The code after calling asyncFetchImage runs further, but the image is still not loaded. There is no way to return the result at this point, because the image loading has not yet begun.

+6
source

All Articles