RxSwiftを用いるとHTTP通信まわりの記述をすっきりと書くことができるだけでなく、通信の実行スレッドを簡単に切り替えたり、レスポンスの処理をキレイに書くことができます。
通信のたびにスレッドに処理を割り当てたり、コールバック処理を型が合うように記述するのに苦労している方は是非RxSwiftを導入して、通信処理をラップしてあげるHTTPクライアントを導入してみると良いと思います。
HTTPクライアントのインターフェース
さて今回実装するHTTPクライアントは以下のようなインターフェースです。
import RxSwift
import Foundation
public protocol HttpClient {
func get(url: NSURL, parameters: [String:String]?, headers: [String:String]?) -> Observable<(NSData!, NSURLResponse!)>
func post(url: NSURL, parameters: [String:String]?, headers: [String:String]?) -> Observable<(NSData!, NSURLResponse!)>
}
ここでは get と post の例しか示しませんが、他のHTTPメソッドも同じように実装できますので、必要に応じていい感じにやってください。
このようなインターフェースを持たせることにより、リクエストにクエリストリングを自在に付加したり、ヘッダーを差し替えたりすることができます。
実際のアプリケーション制作では、APIに対してクエリストリングを付加したり、認可が必要なAPIを叩くためにセッション情報を付加したりする必要がでてくるので、こうしておくと便利です。
HTTPクライアントの実装
さて、このprotocolの実装ですが、以下のようになります。ここではリクエストを組み立てるサポートライブラリとして Alamofire
を使いますが、このあたりはよしなに差し替えていただいて大丈夫です。
import Alamofire
import RxSwift
import Foundation
public class DefaultHttpClient: HttpClient {
private static let manager: Manager = Alamofire.Manager()
public func get
(url: NSURL, parameters: [String : String]?, headers: [String : String]?) -> Observable<(NSData!, NSURLResponse!)> {
return action(Alamofire.Method.GET, url: url, parameters: parameters, headers: headers)
}
public func post
(url: NSURL, parameters: [String:String]?, headers: [String:String]?) -> Observable<(NSData!, NSURLResponse!)> {
return action(.POST, url: url, parameters: parameters, headers: headers)
}
private func action
(method: Alamofire.Method, url: NSURL, parameters: [String:String]?, headers: [String:String]?) -> Observable<(NSData!, NSURLResponse!)> {
let request = DefaultHttpClient.manager.request(method, url, parameters: parameters, encoding: ParameterEncoding.URL).request
let mutableRequest = setHeader(headers, mutableRequest: request.mutableCopy() as! NSMutableURLRequest)
return DefaultHttpClient.manager.session.rx_response(mutableRequest)
}
private func setHeader(headers: [String:String]?, mutableRequest: NSMutableURLRequest) -> NSMutableURLRequest {
if let headers = headers {
for (key, value) in headers {
mutableRequest.setValue(value, forHTTPHeaderField: key)
}
}
return mutableRequest
}
}
アプリケーションを制作するときに、通信まわりを適当に書いていたりしている方は一度試して見ると良いと思います。この層をしっかりとラップしたインターフェースを定義しておくことにより、すべてのリクエストに特定のヘッダーを付加したくなったときに数行の変更で済んだりします。
具体的なウェブAPIのラッパーは必ずこのHTTPクライアントを使って、通信を走らせるように実装を進めていくと、良い感じに構造化されたアプリケーションになると思います。
なお、上記の実装は説明のため簡素化しているので、実際のアプリケーションに組み込む際は色々なことを考慮して自分なりに変更を加えてみてください。
たとえば private static
で持っている Alamofire.Manager
を外部からインジェクトできるようにすれば、リクエストタイムアウト時間を自在に設定できたりします。
出来上がったクライアントを利用して通信処理を走らせる
さて、こうしてできたHTTPクライアントを使って通信を走らせてみましょう。通信処理をメインスレッドで走らせると当然ながらユーザーからの入力を受け付けられなくなり、画面がガタガタになります。
しかしながらバックグランドで通信を走らせたあと、その結果を画面に描画したい場合は逆にメインスレッドでやらないと反映させられません。
ここでRxSwiftを使うと、1行追加するだけで、実行スレッドを切り替えることができます。これは非常に便利ですね。このときに用いられるのがスケジューラーというものですが、今回はその詳細は省いて大まかな使い方だけをみていきます。
class Scheduler {
let backgroundWorkScheduler: ImmediateScheduler
let mainScheduler: SerialDispatchQueueScheduler
init() {
let operationQueue = NSOperationQueue()
operationQueue.maxConcurrentOperationCount = MangaContext.MAX_CONCURRENT_OPERATION_COUNT
operationQueue.qualityOfService = NSQualityOfService.UserInitiated
backgroundWorkScheduler = OperationQueueScheduler(operationQueue: operationQueue)
mainScheduler = MainScheduler.sharedInstance
}
}
基本的にはバックグランドで処理を実行したいときに backgroundWorkScheduler
を使い、メインスレッドで処理を実行したいときに mainScheduler
を使うと思っておけば今はOKです。
こうして作ったSchedulerで実行スレッドを切り替える様をみてみましょう。
let scheduler = Scheduler()
let client = DefaultHttp(timeoutSec: 10)
client.get(NSURL(string: "http://gochiusa.com")!, parameters: nil, headers: nil)
>- observeOn(scheduler.backgroundWorkScheduler)
>- subscribeOn(scheduler.mainScheduler)
>- subscribe(next: { (e) -> Void in
println(e)
}, error: { (e) -> Void in
println(e)
}, completed: { () -> Void in
println("completed")
})
コードのなかの observeOn
と subscribeOn
がキモです。observeOn
はそれ以前の処理をどのスレッドで実行するかを指定し、subscribeOn
はそれ以降の処理をどのスレッドで実行するかを指定します。
従って、このコードは通信部分はバックグランドで実行され、その結果を処理する部分はメインスレッドで実行されます。
こんなに簡単に実行スレッドを切り替えられるなんて素晴らしいですね。是非RxSwiftを使って快適な開発に挑戦してみてください。
RxSwiftを使う上で壁にぶち当たったら、Qiitaやブログの記事よりは、公式のドキュメントや実装例を参照するのが手っ取り早いので、そちらを参照しましょう。