Facebook(Parse)が提供している Bolts-iOS は有名だし便利そうだけど、最初使い方がわかりづらい。READMEはParse利用時を想定して書かれているし、実際、ちょっととっつきにくいんですよね。サンプルコードもちゃんと書くとややこしくなりがち。それを出来るだけ簡単に説明しようと思います。そのために、細かい説明は端折ります。コードは説明的に冗長にします。まず使って、そして深めていくのが効率が高いやり方だと思うので。僕が伝えたいのは、 Bolts-iOSは実は簡単なんだぜ! ってことです!サンプルプロジェクトは末尾に用意しました。
Swiftで説明していますが、使い方はObjective-Cでも同じです。適度に読み替えて下さい。
Boltsの簡単な説明
Boltsは非同期処理をより扱いやすくします。非同期処理をよく使うのは通信周りだと思いますが、通信終了時にClosure(Objective-CではBlocks)で処理をするようなコードで非同期通信の直列処理をしようとすると、Closureのネストが深くなってややこしいことになります。Boltsを利用すれば、ネストすることなく可読性を保ったまま非同期処理を直列で書くことができます。複数の処理の結果を見て判定する必要のあるログイン処理の部分などで利用価値があると思います。
準備
cocoa podsでBoltsを追加する
Podfile
pod 'Bolts', '~> 1.1'
Terminal
pod install
Objective-Cとのブリッジファイルを作成する
適当なObjective-Cファイルを作成して追加します。その際に、Bridging-Headerを追加するか聞いてくるので、YESを押します。File > New > File > (iOS or OS X) > Source > Header File をして、 <プロジェクト名>-Bridging-Header
としても追加可能です。
Bridging-Headerファイルに下記のように記述します。
#import <Bolts.h>
これで準備が出来ました。
非同期処理を直列で処理する
一番簡単で利用頻度も高いと思われる非同期処理を直列で処理する方法について説明します。下記のサンプルコードを参照して下さい(サンプルコードは文末にリンクがあります)。例はYahooと通信してから、Googleと通信する例です。実行するとそれぞれ非同期通信にもかかわらず、必ずYahoo! -> Googleの順番で通信が実行されます。
通信処理はAFNetworkingで行うことが最近は多いと思いますが、複雑性を減らすために標準のNSURLSessionで行っています。非同期通信の結果をclosureで取得する点はAFNetworkingと同じです。
taskの中身をそのまま書いているのでコード量は多いのですが、書かれていることは単純で、
- taskを作成
- taskをcontinueWithBlockでつなげる
というだけです。これで、ネストして階層が深くなることなく、複数の非同期処理を直列で書くことができます。
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// PREPARE EMPTY TASK - FOR EXECUTING TASKS IN SERIES
var task = BFTask(result: nil)
// 1. ASYNCRONOUS CONNECTION TO YAHOO
task = task.continueWithBlock({ (task: BFTask!) -> BFTask! in
// INSTANTIATE TASK COMPLETION
let taskCompletion = BFTaskCompletionSource()
// ASYNCRONOUS NETWORK ACTIVITY
let url = "http://yahoo.co.jp"
let session = NSURLSession.sharedSession()
let sessionTask = session.dataTaskWithURL(NSURL(string: url), completionHandler: { (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void in
if let err = error {
println("1 ERROR: YAHOO!")
taskCompletion.setError(err)
}
println("1 SUCCESS: YAHOO!")
taskCompletion.setResult(data)
})
sessionTask.resume()
// RETURN TASK
return taskCompletion.task
})
// 2. ASYNCRONOUS CONNECTION TO GOOGLE
task = task.continueWithBlock({ (task: BFTask!) -> BFTask! in
// INSTANTIATE TASK COMPLETION
let taskCompletion = BFTaskCompletionSource()
// ASYNCRONOUS NETWORK ACTIVITY
let url = "http://google.com"
let session = NSURLSession.sharedSession()
let sessionTask = session.dataTaskWithURL(NSURL(string: url), completionHandler: { (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void in
if let err = error {
println("2 ERROR: GOOGLE!")
taskCompletion.setError(err)
}
println("2 SUCCESS: GOOGLE!")
taskCompletion.setResult(data)
})
sessionTask.resume()
// RETURN TASK
return taskCompletion.task
})
}
}
taskCompletionの setSuccess() や setError() をセットすると次のtaskが実行されるようになってます。
ちなみに、上記の例だと通信の結果を元にした判定を行っていないのですが、continueWithBlockの引数であるtaskには直前のtaskの結果が入っているので、 task.success() や task.error() でそれを取り出して判定をすることができます。API通信をして結果をJSONで受け取ることが多いと思うのですが、このJSONの結果やerrorを元に次の処理を変更させることもできます。
また、READMEにあるように、それぞれのtaskを返すメソッドを作ることでよりシンプルに柔軟なコードにすることができます。ただ、最初からそのようにtaskを返すメソッドを作ると理解が難しくなるので、今回は本当にシンプルで冗長な例を作成して説明しています。
サンプルプロジェクト
通信周りはAFNetworkingで行うケースが多いと思いますが、説明をシンプルにするために標準のNSURLSessionで行っています。
その他
-
commit dcea5b95b8
の段階での説明です。 - Xcode 6.0.1です。
- Swiftのバージョンの違いによってコードは動かなくなる可能性があります。その際は読み替えて下さい。
- Objective-Cでも基本は同じです。
- yahooやGoogleと接続するのは一番適切な例ではないと思います。気軽に使って良いJSONを返却するAPIなどあれば教えていただきたいです。