java_doctor_101
java_doctor_101

Reputation: 3357

How does asynchronous closure works in Swift

I am using a nsurlssesion based wrapper class written in objective-c in my swift project. Everthing is working except I am not able to undertand how the closure works in swift.

In my swift viewcontroller:

 DownloadManager().downloadFile(forURL: url, progressBlock: { (progress) -> () in
            print("current progress is \(progress)")
        }, completionBlock: { (completion) in
            print("is completed : \(completion)")
        }, enableBackgroundMode: false)

In my Downloadmanager class (which is objc based) whenever nsurlssession delegate is called this happens:

 dispatch_async(dispatch_get_main_queue(), ^(void) {
            if(download.progressBlock){
                download.progressBlock(progress); //exception when progressblock is nil
            }
        });

Download object have a block type property called progressBlock :

typedef void(^TWRDownloadProgressBlock)(CGFloat progress);

Output:

current progress is 0.0908259372799894
current progress is 0.272477811839968
current progress is 0.363303749119957
current progress is 0.454129686399947
current progress is 0.544955623679936
current progress is 0.635781560959925
current progress is 0.726607498239915
current progress is 0.817433435519904
current progress is 1.0

Flow of the code:

  1. NSURlsession calls it's delegate method.
  2. DowloadManager updates Download object's progress variable.
  3. Somehow in my viewcontroller I can see the updated value of the progress variable inside the closure.

Question: How is point-3 happening?

Is this the standard way closure behaves in swift? Specifically, I would like to know if the closure is automatically called everytime Dowload object changes?

Upvotes: 1

Views: 578

Answers (1)

buildc0de
buildc0de

Reputation: 323

According to The Swift Guide:

Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages.

Closures can capture and store references to any constants and variables from the context in which they are defined.

In this case, a closure is defined within the view controller, then passed around and called later.

The flow would be as follows:

  1. Make a new instance of TWRDownloadManager() and call its downloadFile method.
  2. In this call, pass the progressBlock: ((CGFloat) -> Swift.Void) closure (it takes CGFloat as a parameter and returns nothing).
  3. Down the line, this block is what gets assigned to TWRDownloadObject().progressBlock
  4. Further down the line, NSURLSession Delegate method calls download.progressBlock(progress), which simply calls the closure from #2 and passes the CGFloat value as its parameter.

Here is quick Playground example:

import Foundation
import PlaygroundSupport
import TWRDownloadManager

PlaygroundPage.current.needsIndefiniteExecution = true

let downloadManager = TWRDownloadManager()
let url = "http://download.thinkbroadband.com/200MB.zip"

let progressClosure: (CGFloat) -> () = { (progress) in
    
    print("current progress is \(progress)")
    
}

let completionClosure: (Bool) -> () = { (completion) in
    
    print("is completed : \(completion)")
    
}

downloadManager.downloadFile(
    forURL: url,
    progressBlock: progressClosure,
    completionBlock: completionClosure,
    enableBackgroundMode: false
)

Upvotes: 1

Related Questions