タマネギプログラマーの雑記

たまねぎ剣士的なニュアンス

Moyaでお手軽API通信

ブログ始めてみたんですけど、特に抱負も何もないので、しれっとMoyaで遊んだ事について書きます。

Moyaって?

github.com

APIとか叩く時に作られがちな、"APIManager"とか"NetworkModel"みたいな、 ネットワークを抽象化するレイヤーの役割をしてくれるライブラリ。 内部的にはAlamofireを叩いていて、言うならAlamofireのラッパーとも言えるかもしれない(言えないかもしれない)。

何が便利?

公式が言うには

  • 正しいAPIのエンドポイントにアクセスしてるかコンパイル時にチェックできる
  • enumの連想値使って異なるAPIの使い方をちゃんと定義できる
  • testする時stub的な役割を果たせる

個人的に思ったのは

  • siestaとかよりhttp通信の層をよく隠してくれるので楽
  • 仕組みが割とわかりやすいので使う上でリーズナブル

といったところでしょうか。

使い方

導入

Podfileに

pod 'Moya'

を記入し、pod installで簡単にインストールできます。 なお、Moyaを入れるとAlamofireも一緒に入るので別途記述する必要はありません。 ちなみにSwift Package ManagerやCarthageでも導入可能です。

今回は、 github.com を使って基本的な使い方をなぞってみます。 MoyaのレポジトリにあるBasicUsageとほぼ同じ内容です。

enumを作成

Moyaでは、自分が使いたいAPIの情報をenumに持たせます。 まずは、それぞれのAPIターゲットをenumとして列挙します。 また、それぞれのターゲットに対し渡すパラメータがある場合は、連想値でこれを定義します。

import Moya

enum HackersNewsAPI {
    case item(id: Int)
    case user(id: String)
    case maxItem
    case topStories
    case newStories
}

また、このenumTargetTypeプロトコルに準拠する必要があります。 具体的には

  • var baseURL: URL
  • var path: String
  • var method: Moya.Method
  • var parameters: [String: Any]
  • var parameterEncoding: ParameterEncoding
  • var sampleData: Data
  • var task: Task

のプロパティを設定しなければなりません。 今回は以下のように準拠させます。

extension HackersNewsAPI: TargetType {
    // ベースURL
    var baseURL: URL { 
          return URL(string: "https://hacker-news.firebaseio.com/v0")! 
    }
    
    // それぞれのターゲットごとのpath
    var path: String {
        switch self {
        case .item(let id), .user(let id):
            return "/\(id).json"
        case .maxItem:
            return "/maxitem.json"
        case .newStories:
            return "/newstories.json"
        case .topStories:
            return "/topstories.json"
        }
    }
    
    // それぞれのターゲットごとのhttpメソッド
    // 今回はgetしかないが、postなどある場合はswitch selfなどで適切な値を返す
    var method: Moya.Method {
        switch  self {
        case .item, .user, .maxItem, .newStories, .topStories:
            return .get
        }
    }

    // 今回はパラメーターとして渡さないのですべてnil
    var parameters: [String: Any]? {
        return nil
    }
    
    // パラメーターのエンコーディング指定
    // リクエストボデイにjsonとしてセットすることもできる
    var parameterEncoding: ParameterEncoding {
        return URLEncoding.default
    }
    
    // テスト時に使われる(なんでマストなのかわからない)
    // めんどくさかったので今回はずるします
    var sampleData: Data {
        return Data()
    }
    
    var task: Task {
        switch  self {
        case .item, .user, .maxItem, .newStories, .topStories:
            return .request
        }
    }
}

以上でenumの準備は完了です。

ここまで書いて、題材にしたAPIがあんま良くなかったことに気づく。 が、面倒くさいので強行します。

MoyaProviderを使ってAPIリクエストする

enumの準備が整ったら、MoyaProviderを生成し、リクエストを送ることができます。

let provider = MoyaProvider<HackersNewsAPI>()
provider.request(.newStories) { result in
    
}

provider.requestの第二引数となっているクロージャが受け取っているresultは、 .success(Moya.Response).failure(MoyaError)を持つenumです。 なので基本的には

provider.request(.newStories) { result in
    switch result {
    case let .success(moyaResponse):
        do {
            try moyaResponse.filterSuccessfulStatusCodes()
            let data = try moyaResponse.mapJSON()
            // do something with the data
        }
        catch {
           // failed to convert to JSON
        }
    case let .failure(error):
           // failed at api access
    }
            
}

のようにしてエラー検知すると良いと思います。

filterSuccessfulStatusCodesは、ステータスが200~299でなければ例外を投げるメソッド。 mapJSONは名前の通りデータをJSONマッピングしてくれるメソッドです。

どちらもMoya.Responseに組み込まれたメソッドです。

まとめ

今回は本当に触りしかやりませんでしたが、Moyaは、APIリクエストの際に挟み込めるクロージャーや、 テストを意識した機能、RX対応など、まだまだ魅力的な機能を持っています。 特にRX対応は割とAPIもイケてると思うので是非触ってみてもらえたらなと思います。

一応今回のソース

github.com