-4

I'm new to Swift and I'm trying to add a completion block. I remember this being pretty simple in objective-c, but I'm kinda lost with the syntax here. This function parses some json and adds the relevant content to an array. I need to refresh a tableview once the function is complete, since I can't do this within the block I need to add a completion block.

How do I do add a completion block to this function in Swift, and how would the new method call look like?

func getSetParameter()
{
    let param = ["format":"json"]
    let jsonUrl: String! = "http://somewebsite.com"

    let manager: AFHTTPSessionManager = AFHTTPSessionManager()
    manager.GET(jsonUrl, parameters: param, success: {
        (task: NSURLSessionDataTask!, JSONResponse: AnyObject!) in

            let responseDictionary = JSONResponse as! NSDictionary
            let responseArray = responseDictionary.objectForKey("response") as! NSArray
            for thumbnailsOnVideoDictionary in responseArray
            {
                let thumbnailsOnVideoArray = thumbnailsOnVideoDictionary.objectForKey("thumbnails") as! NSArray
                if thumbnailsOnVideoArray.count == 0 {
                    self.thumbnails.append(nil)
                }
                else {
                    let smallThumbnail = thumbnailsOnVideoArray[1];
                    let aspect_ratio: Float = (smallThumbnail.objectForKey("aspect_ratio") as! Float)
                    let height: UInt = (smallThumbnail.objectForKey("height") as! UInt)
                    let name: AnyObject = smallThumbnail.objectForKey("name")!
                    let url: String = (smallThumbnail.objectForKey("url") as! String)
                    let width: UInt = (smallThumbnail.objectForKey("width") as! UInt)

                    let newThumbnail = Thumbnail(aspect_ratio: aspect_ratio, height: height, name: name, url: url, width: width)
                    self.thumbnails.append(newThumbnail)
                }
            }
        }, failure: {(task: NSURLSessionDataTask?, error: NSError!) in

    })
}
xoudini
  • 7,001
  • 5
  • 23
  • 37
kiaraRobles
  • 587
  • 1
  • 4
  • 14
  • 1
    Really just the same as http://stackoverflow.com/questions/30133490/run-code-only-after-asynchronous-function-finishes-executing – matt Feb 07 '16 at 20:05

2 Answers2

2

Update your function with an argument for the handler. In this example, I used a closure that's passed an array of thumbnails, and returns nothing:

func getSetParameter(handler:([Thumbnail]) -> ()) {

    // generate an array of Thumbnails
    let someArray = [Thumbnail(), Thumbnail()]

    // call the handler, passing the array
    handler(someArray)
}

Since the last argument is a closure, you can use Swift's trailing closure syntax:

// call the function

getSetParameter { thumbnails in
    print(thumbnails)
}

Or even more succinctly:

getSetParameter {
    print($0)
}

The more verbose version of this (without the trailing closure syntax) also works:

getSetParameter({ (thumbnails) -> () in
    print(thumbnails)
})

But that's much uglier in my opinion.

Aaron Brager
  • 65,323
  • 19
  • 161
  • 287
-2

Not sure how to use a closure with a handler, the code in brackets of the self.getSetParameter function gets called before the function itself. Instead of at the completion of the function.

override func viewDidLoad()
{
    self.getSetParameter { (thumbnails: [Thumbnail?]) -> () in
        for var index = 0; index < thumbnails.count; ++index {
            let currentThumbnail = thumbnails[index]

            var title: String
            var image: UIImage
            if currentThumbnail!.isKindOfClass(Thumbnail) {
                title = "Title " + String(index)
                image = currentThumbnail!.image
            }
            else {
                title = "Nil"
                image = UIImage(named: "image0")!
            }

            self.models.append(CustomCell(title: title, image: image))
        }
    }
}

func getSetParameter(handler:([Thumbnail?]) -> ()) {
    var thumbnails = [Thumbnail?]()
    let param = ["format":"json"]
    let jsonUrl: String! = "http://somewebsite.com"

    let manager: AFHTTPSessionManager = AFHTTPSessionManager()
    manager.GET(jsonUrl, parameters: param, success: {
        (task: NSURLSessionDataTask!, JSONResponse: AnyObject!) in

            let responseDictionary = JSONResponse as! NSDictionary
            let responseArray = responseDictionary.objectForKey("response") as! NSArray
            for thumbnailsOnVideoDictionary in responseArray
            {
                let thumbnailsOnVideoArray = thumbnailsOnVideoDictionary.objectForKey("thumbnails") as! NSArray
                if thumbnailsOnVideoArray.count == 0 {
                    thumbnails.append(nil)
                }
                else {
                    let smallThumbnail = thumbnailsOnVideoArray[1];
                    let aspect_ratio: Float = (smallThumbnail.objectForKey("aspect_ratio") as! Float)
                    let height: UInt = (smallThumbnail.objectForKey("height") as! UInt)
                    let name: AnyObject = smallThumbnail.objectForKey("name")!
                    let url: String = (smallThumbnail.objectForKey("url") as! String)
                    let width: UInt = (smallThumbnail.objectForKey("width") as! UInt)

                    let newThumbnail = Thumbnail(aspect_ratio: aspect_ratio, height: height, name: name, url: url, width: width)
                    thumbnails.append(newThumbnail)
                }
                handler(thumbnails)
            }
        }, failure: {(task: NSURLSessionDataTask?, error: NSError!) in

    })
}
kiaraRobles
  • 587
  • 1
  • 4
  • 14
  • This would call `handler` multiple times, which would be unexpected. Try moving `handler` outside of the `for` loop, so it's called once the loop completes. Also, it's probably better if you don't use optionals with your handler, that way you won't have to unwrap them later. – Aaron Brager Feb 08 '16 at 05:22