Installation

CocoaPods (Dependency Manager)

You'll first need install CocoaPods:

$ gem install cocoapods

Now you'll need to set up CocoaPods for your existing xcode project. Change the working directory to your xcode project's root directory and then:

$ touch Podfile

Then open the Podfile file open, and add the following contents:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'

pod 'AsyncKit'

Then run the following command, which will install the AsyncKit library into your existing xcode project

$ pod install
Manually Add Library

Another integration option is to manually add ConstraintKit to your xcode project, by cloning from github and dragging/dropping into your xcode project:

$ git clone ...

How To Use

Basic Example

AsyncKit brings the power of promises/futures to iOS development in the form of Tasks

To build a responsive iOS application, you would need to run network and long operations asynchronously within a background thread to prevent it from slowing the main (UI) thread down. AsyncKit aims to make this easier by allowing asynchronous operations to return a value when it is completed. An asynchronous function would return a Task object which can be processed using then and error methods on the Task object.

There are only two core classes: Task and TaskManager. TaskManager handles it's underlying Task object, thus you should always use TaskManager and never use Task directly.

TaskManager Completion Method Task Handler Method Note
complete then If asynchronous operation was successful
completeWithError error((NSError) -> Void) If asynchronous operation failed with a NSError object
completeWithException error((NSException) -> Void) If asynchronous operation failed with a NSException object
...
func fetchDataAsync() -> Task<NSData> {
    let manager = TaskManager<NSData>()

    dispatch_async(backgroundQueue) {
        // Fetches data
        manager.complete(data)
    }

    return manager.task
}
...
fetchDataAsync().then { data in
    // Handle data here
}
...

then Method

  • The parameter datain the then block is the result of the asychronous function fetchDataAsync().

  • The then block on a Task executes only if the task is completed successfully, which will be discussed in more detail further down.

error Method

  • The error block on a Task executes only if the task failed.

  • There are two types of error blocks, one for handling NSError and another for handling NSException

...
func fetchDataAsync() -> Task<NSData> {
    let manager = TaskManager<NSData>()

    dispatch_async(backgroundQueue) {
        let (data, error) = fetchData()

        guard let ret = data where error == nil else {
            manager.completeWithError(error)
            return
        }

        manager.complete(ret)
    }

    return manager.task
}
...
fetchDataAsync().then { data in
    // Handle data here if successfully fetched data
}.error { (error: NSError) in
    // Handle error here if data fetching failed
}
...
activityIndicator.startAnimating()
fetchDataAsync().then { data in
    // Handle data
}.error { (error: NSError) in
    // Handle error
}.finally {
    activityIndicator.stopAnimating()
}

finally Method

  • The finally block will always be executed, after the task completes, regardless of the outcome of the task

Chaining

AsyncKit also allows you to chain then blocks without having to nest them within one another.

...
fetchDataAsync().then { data in
    // Handle data
    return fetchUserAsync()
}.then { user in
    // Update user
    return saveUserAsync()
}.then { updatedUser in
    // Handle updated user
}
...

NOTE

The order of chained then blocks will be preserved when being executed. For the above example, fetchUserAsync() will only be executed after fetchDataAsync() is completed successfully and saveUserAsync() will only be executed after fetchUserAsync() is completed successfully.

Executors

The then function also takes an optional parameter, ExecutorType. The ExecutorType determines what thread the then block runs in. The default ExecutorType is the Current thread, which refers to the thread in which the TaskManager completed it's task in.

ExecutorType Details
.Current The thread in which the TaskManager completed it's task in.
.Main The main thread A.K.A. dispatch_get_main_queue()
.Async The default priority global thread A.K.A. dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
.Queue(dispatch_queue_t) Custom thread provided
// The then block will run in the main thread (where UI related operations should be ran)
fetchDataAsync().then(.Main) {
    let alertController = UIAlertController(title: "Completed", message: "Fetched Data Successfully", preferredStyle: .Alert)
    let okAction = UIAlertAction(title: "OK", style: .Cancel) { action in 
        // ...
    }
    alertController.addAction(okAction)

    self.presentViewController(alertController, animated: true) {
        // ...
    }
}

Running Multiple Tasks in Parallel

AsyncKit also allows you to run multiple tasks parallel to one another using the Task.join function

Task.join(fetchAndLoadTableAsync(), fetchAndLoadImagesAsync()).then {
    // This block executes only if all the tasks sent to the Task.join function complete successfully
}

Task Wrapper

AsyncKit also provides you with a Task wrapper function called task. The task function accepts a block as its only parameter, which is executed asynchronously. The return value of the task function is a Task object.

...
enum LoginError: ErrorType {
    case InvalidCredentials
}

task { () throws in
    // The contents of this block will be executed asynchronously in a default priority global queue

    let success = self.login(username, password)
    if (!success) {
        throw LoginError.InvalidCredentials
    }

    var ret: AnyObject? = nil
    // Fetch other data...

    return ret
}.then { ret in
    // Handle result of the task wrapped code
}.error { (error: ErrorType) in
    // Handle ErrorType (in this case, InvalidCredentials)
}
...