• Stars
    star
    130
  • Rank 276,500 (Top 6 %)
  • Language
    Swift
  • License
    MIT License
  • Created over 8 years ago
  • Updated over 7 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

An asynchronous programming library for Swift

AsyncTask

CI Status Version Carthage compatible License Platform

An asynchronous programming library for Swift

Features

AsyncTask is much more than Future and Promise.

  • It is composable, allowing you to build complex workflow.
  • It supports native error handling with do-catch and try.
  • It is protocol oriented; so you can turn any object into a Task.

Without AsyncTask:

// get a global concurrent queue
let queue = dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0)
// submit a task to the queue for background execution
dispatch_async(queue) {
    let enhancedImage = self.applyImageFilter(image) // expensive operation taking a few seconds
    // update UI on the main queue
    dispatch_async(dispatch_get_main_queue()) {
        self.imageView.image = enhancedImage
        UIView.animateWithDuration(0.3, animations: {
            self.imageView.alpha = 1
        }) { completed in
            // add code to happen next here
        }
    }
}

With AsyncTask:

Task {
    let enhancedImage = self.applyImageFilter(image)
    Task {self.imageView.image = enhancedImage}.async(.Main)
    let completed = UIView.animateWithDurationAsync(0.3) { self.label.alpha = 1 }.await(.Main)
    // add code to happen next here
}.async()

It even allows you to extend existing types:

let (data, response) = try! NSURL(string: "www.google.com")!.await()

Installation

CocoaPods

AsyncTask is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod "AsyncTask"

Carthage

Xcode 7.1 required

Add this to Cartfile

github "zhxnlai/AsyncTask" ~> 0.1
$ carthage update

Tutorial

Usage

In AsyncTask, a Task represents the eventual result of an asynchronous operation, as do Future and Promise in other libraries. It can wrap both synchronous and asynchronous APIs. To create a Task, initialize it with a closure. To make it reusable, write functions that return a task.

// synchronous API
func encrypt(message: String) -> Task<String> {
    return Task {
        encrypt(message)
    }
}
// asynchronous API
func get(URL: NSURL) -> Task<(NSData?, NSURLResponse?, NSError?)> {
    return Task {completionHandler in
        NSURLSession().dataTaskWithURL(URL, completionHandler: completionHandler).resume()
    }
}

To get the result of a Task, use async or await. async is just like dispatch_async, and you can supply a completion handler. await, on the contrary, blocks the current thread and waits for the task to finish.

// async
encrypt(message).async { ciphertext in /* do somthing */ }
get(URL).async {(data, response, error) in /* do somthing */ }

// await
let ciphertext = encrypt(message).await()
let (data, response, error) = get(URL).await()

Composing Tasks

You can use multiple await expressions to ensure that each statement completes before executing the next statement:

Task {
    print(“downloading image”)
    var image = UIImage(data: downloadImage.await())!
    updateImageView(image).await(.Main)
    print(“processing image”)
    image = processImage(image).await()
    updateImageView(image).await(.Main)
    print(“finished”)
}.async()

You can also call awaitFirst and awaitAll on a collection of tasks to execute them in parallel:

let replicatedURLs = ["https://web1.swift.org", "https://web2.swift.org"]
let first = replicatedURLs.map(get).awaitFirst()

let messages = ["1", "2", "3"]
let all = messages.map(encrypt).awaitAll()

Handling Errors

Swift provide first-class support for error handling. In AsyncTask, a ThrowableTask takes a throwing closure and propagates the error.

func load(path: String) -> ThrowableTask<NSData> {
    return ThrowableTask {
        switch path {
        case "profile.png":
            return NSData()
        case "index.html":
            return NSData()
        default:
            throw Error.NotFound
        }
    }
}

expect{try load("profile.png").await()}.notTo(throwError())
expect{try load("index.html").await()}.notTo(throwError())
expect{try load("random.txt").await()}.to(throwError())

Extending Tasks

AsyncTask is protocol oriented; it defines TaskType and ThrowableTaskType and provides the default implementation of async and await using protocol extension. In other words, these protocols are easy to implement, and you can await on any object that confronts to them. Being able to extend tasks powerful because it allows tasks to encapsulate states and behaviors.

In the following example, by extending NSURL to be TaskType, we make data fetching a part of the NSURL class. To confront to the TaskType protocol, just specify an action and the return type.

extension NSURL : ThrowableTaskType {

    public typealias ReturnType = (NSData, NSURLResponse)

    public func action(completion: Result<ReturnType> -> ()) {
        ThrowableTask<ReturnType> {
            let session = NSURLSession(configuration: .ephemeralSessionConfiguration())
            let (data, response, error) = Task { callback in session.dataTaskWithURL(self, completionHandler: callback).resume()}.await()
            guard error == nil else { throw error! }
            return (data!, response!)
        }.asyncResult(completion: completion)
    }

}

This extension allows us to write the following code:

let (data, response) = try! NSURL(string: "www.google.com")!.await()

A Task can represent more complicated activities, even those involving UI. In the following example, we use an ImagePickerTask to launch a UIImagePickerViewController and wait for the user to choose an image. Once the user selects an image or press the cancel button, the view controller dismisses, and the task returns with the selected image.

class ImagePickerDemoViewController: UIViewController {

    let imageView = UIImageView()

    func launchImagePicker() {
        Task {
            do {
                let data = try ImagePickerTask(viewController: self).await()
            } catch Error.PhotoLibraryNotAvailable {
                alert("Photo Library is Not Available")
            }
            guard let image = data?[UIImagePickerControllerOriginalImage] as? UIImage else {
                self.imageView.image = nil
                return
            }
            self.imageView.image = image
        }.async()
    }

}

The ImagePickerTask knows when the user has picked an image or canceled because it is the UIImagePickerViewController’s delegate. For more details, see its implementation and the example folder.

Example

To run the example project, clone the repo, and run pod install from the Example directory first.

You may also want to take a look at the test cases.

Author

Zhixuan Lai, [email protected]

License

AsyncTask is available under the MIT license. See the LICENSE file for more info.

More Repositories

1

ZLSwipeableView

A simple view for building card like interface inspired by Tinder and Potluck.
Objective-C
2,828
star
2

ZLSwipeableViewSwift

A simple view for building card like interface inspired by Tinder and Potluck.
Swift
2,235
star
3

ZLBalancedFlowLayout

A UICollectionViewFlowLayout subclass that scales items to take up space, optimized for large item set, inspired by NHBalancedFlowLayout.
Swift
309
star
4

ZLSinusWaveView

A Siri like voice visualization view using EZAudio. Modified from SISinusWaveView for iOS.
Objective-C
283
star
5

ZLMusicFlowWaveView

A ZLSinusWaveView subclass inspired by 乐流/MusicFlow
Objective-C
202
star
6

ZLPeoplePickerViewController

A multilingual replacement for ABPeoplePickerNavigationController (address book picker) that supports UILocalized​Indexed​Collation
Objective-C
189
star
7

ZLHistogramAudioPlot

A hardware-accelerated audio visualization view using EZAudio, inspired by AudioCopy.
Objective-C
185
star
8

UIColor-ChineseTraditionalColors

A swift extension that extends UIColor with a list of Chinese traditional colors
Swift
132
star
9

react-webgl-globe-basic-example

WebGL Globe basic example in react
JavaScript
70
star
10

Async

Async, await control flow for Swift.
Swift
69
star
11

Algorithms

Answers to LeetCode questions in Swift and JavaScript
Swift
52
star
12

ReactiveUI

A lightweight replacement for target action with closures, modified from Scream.swift.
Swift
51
star
13

Emoticon

An Emoticon Keyboard
Swift
22
star
14

wwdc

WWDC 2015 Student Scholarship App
Swift
6
star
15

printf

A web based visualizer for printf format string
JavaScript
6
star
16

YelpAPI

Yelp API in Swift
Swift
5
star
17

TicketsTonight

UCLA CS130 Fall 2014 Class Project
Objective-C
4
star
18

AlamofireAsync

Async extension for Alamofire
Swift
3
star
19

DongerListKeyboard

Experimenting with iOS 8 keyboard in swift
Objective-C
3
star
20

JSTester

A simple structure tester for javascript programs.
JavaScript
1
star
21

librarymanagement

Python
1
star
22

frappe_app_template

1
star