タコさんブログ

プログラミングメモと小言

Swift Moyaでモヤっとしているネットワークレイヤーを解決

MoyaはAlamofireを直接さわることなく、ネットワークレイヤーを抽象化してくれるライブラリ。 イメージとしては、下の図のような感じ。

f:id:tiny_wing:20151223011015p:plain

Moyaの特徴として、

  • コンパイル時に正しいAPIエンドポイントのチェック
  • Associated Enumによりエンドポイントを明確にできる
  • Unitテストが簡単になる

今回の例はiTunes Apple APIを使用して音楽のアルバムを検索する。

環境

MoyaをPodでインストール

use_frameworks!

pod 'Moya'

MoyaをインストールすればAlamofireもインストールしてくれるので、AlamofireをPodfileに書く必要がない。

API ターゲットを設定

iTunesの検索には以下のURLを使用する:
https://itunes.apple.com/search?media=music&entity=album&term=検索キーワード

enumでターゲットAPIを設定する。
今回は検索するだけなので、Searchとする。

internal enum iTunes {
  case Search(String) // 検索キーワードを付随させる
}

ターゲットをTargetTypeプロトコルに準拠させる

enumで定義したターゲット(iTunes)をTargetTypeプロトコルに準拠させ、以下の4つを実装する。

  1. var baseURL: NSURL { get }
  2. var path: String { get }
  3. var method: Moya.Method { get }
  4. var parameters: [String: AnyObject]? { get }
  5. var sampleData: NSData { get }
extension iTunes: TargetType {
  // ベースURLを文字列で定義
  private var _baseURL:String {
    return "https://itunes.apple.com"
  }
  public var baseURL: NSURL { return NSURL(string: _baseURL)! }
  // enumの値に対応したパスを指定 
  public var path: String {
    switch self {
    case .Search:
        return "/search"
    }
  }
  // enumの値に対応したHTTPメソッドを指定
  public var method: Moya.Method {
    switch self {
    case .Search:
        return .GET
    }
  }
  // enumの値に対応したパラメータを指定
  public var parameters: [String: AnyObject]? {
    switch self {
    case .Search(let term):  // 検索タームを付随させる
        return ["media" : "music", "entity" : "album", "term" : term]
    }
  }
  // スタブデータの設定
  // 便宜のためベタに書いているが
  // Moyaを使ったアプリeidolonではMainBundleからロードさせている
  public var sampleData: NSData {  
    switch self {
    case .Search:
        return "Stub data".dataUsingEncoding(NSUTF8StringEncoding)!
    }
  }
}

簡単に使う場合、Moyaの設定はこれでOK!!

リクエスト方法

Moyaを使ってリクエストする。

// providerを生成
let provider = MoyaProvider<iTunes>()
// 検索ターム"swift"を指定して、Search APIをリクエストする
provider.request(.Search("swift")) { result in
  switch result {
  case .Success(let response):
    let json = try! response.mapJSON()
    print(json)
  case .Failure(let error):
    print("request error: ", error)
  }
}

リクエストのキャンセル

リクエストのキャンセルにはリクエストした時に、Cancellableオブジェクトが戻り値として 受け取れるので、このオブジェクトを保持しておき、キャンセルする。

以下のような感じ。

var cancellable: Cancellable?

let provider = MoyaProvider<iTunes>()
cancellable = provider.request(...) // Cancellableオブジェクトを取得

// 後でキャンセルする場合
cancellable?.cancel()

感想

Moyaを使ったアプリ Artsy/eidolon のコードがGitHub上に公開されているので、高度な使用例が見れて良いと思う。

参考URL