SwiftTask、PromiseKit、Boltsを比較する(2015年3月版)

2015年3月12日
ios / swift /

次の案件で(Swiftで)Promiseライクなフロー制御を実現するために利用するライブラリを選定するため、2015/3/11時点の

を(表面だけ)使って比較してみました。

なお、昨年の7月時点では(Swiftで使うぶんには)PromiseKitが将来性があると判断し、しばらくはPromiseKitを使っていました。

その後、SwiftTaskも登場して気になっていたので、今回改めて新案件で採用するライブラリを選定したという経緯になります。

以下にそれぞれ使ってみた結果を紹介させていただきます。

更新頻度

この3つのライブラリはどれも更新頻度が多く、現在betaのSwift 1.2でも(別ブランチで)きちんと動く形になっています。

試すネタ

AlamofireでGenericにModelオブジェクトを取得する で試したAlamofireを使うコードをネタとしてそれぞれ3つのライブラリを適用してみました。

Taskを使うほうのコード

SwiftTask

request(.GET, urlString).success { [unowned self] (users: [User]) in
    self.textView?.text = "\(users)"
}.failure { [unowned self] error, _ in
    self.textView?.text = "\(error)"
}

PromiseKit

request(.GET, urlString).then { [unowned self] (users: [User]) -> Void in
    self.textView?.text = "\(users)"
    return
}.catch { [unowned self] error -> Void in
    self.textView?.text = "\(error)"
    return
}.finally {
}

Bolts

request(Alamofire.Method.GET, urlString, User.self).continueWithBlock { (task: BFTask!) -> AnyObject! in
    if let error = task.error {
        self.textView?.text = "\(error)"
    } else if let users = task.result as? [User] {
        self.textView?.text = "\(users)"
    }
    return nil
}

Taskを作るほうのコード

こちらについてはどのライブラリも分かりやすく大差はないかなと思います。

SwiftTask

// SwiftTaskのみprogress取得に対応したサンプルになっています
public func request<T: ResponseCollectionSerializable>(method: Alamofire.Method, URLString: Alamofire.URLStringConvertible, parameters: [String: AnyObject]? = nil, encoding: Alamofire.ParameterEncoding = .URL) -> Task<Progress, [T], NSError> {
    let task = Task<Progress, [T], NSError> { progress, fulfill, reject, configure in
        Alamofire.request(method, URLString, parameters: parameters, encoding: encoding)
            .progress { bytesWritten, totalBytesWritten, totalBytesExpectedToWrite in
                progress((bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) as Progress)
            }
            .validate()
            .responseCollection { (request, response, collection: [T]?, error) in
                if let error = error {
                    reject(error)
                    return
                }
                if let collection = collection {
                    fulfill(collection)
                    return
                }
                reject(NSError()) //< TODO: unexpected error
            }
    }
    return task
}

PromiseKit

public func request<T: ResponseCollectionSerializable>(method: Alamofire.Method, URLString: Alamofire.URLStringConvertible, parameters: [String: AnyObject]? = nil, encoding: Alamofire.ParameterEncoding = .URL) -> Promise<[T]> {
    let promise = Promise<[T]>({ fulfiller, rejecter in
        Alamofire.request(method, URLString, parameters: parameters, encoding: encoding)
            .validate()
            .responseCollection { (request, response, collection: [T]?, error) in
                if let error = error {
                    rejecter(error)
                    return
                }
                if let collection = collection {
                    fulfiller(collection)
                    return
                }
                rejecter(NSError()) //< TODO: unexpected error
            }
    })
    return promise
}

Bolts

public func request<T: ResponseCollectionSerializable>(method: Alamofire.Method, URLString: Alamofire.URLStringConvertible, type: T.Type, parameters: [String: AnyObject]? = nil, encoding: Alamofire.ParameterEncoding = .URL) -> BFTask {
    var task = BFTaskCompletionSource()
    Alamofire.request(method, URLString, parameters: parameters, encoding: encoding)
        .validate()
        .responseCollection { (request, response, collection: [T]?, error) in
            if let error = error {
                task.setError(error)
                return
            }
            if let object = collection {
                task.setResult(object as? AnyObject)
                return
            }
            task.setError(NSError()) //< TODO: unexpected error
        }
    return task.task
}

機能差分

- Genericなインターフェース progress用インターフェース cancel用インターフェース
SwiftTask ⚪︎ ⚪︎ ⚪︎
PromiseKit ⚪︎
Bolts ⚪︎

この表はぼくが気にした点を抽出して書いたものですのでちょっと贔屓目に見えてしまうかも。。。

この他 inamiyさんのこちらの記事 などにも機能差分がまとめられてます。

まとめ

特にBoltsは本当に表面しか触ってみていませんので、Boltsのほうがココがすげーぞ!といったご指摘は大歓迎です!

Related Entries
Latest Entries