SwiftでREST APIクライアントを作る

  • 36
    いいね
  • 0
    コメント

Swiftで通信といえばAlamofire等の素晴らしいライブラリが多数ありますが、実装するときにはプロジェクト毎に設計や決まりがあり、導入ライブラリが決まっていたとしても、その設計をどうするかについてはプロジェクト毎に多様です。

そこで今回はSwift3で統一したインターフェースでAPIクライアントを作成するためのライブラリを紹介したいと思います。

今回紹介するライブラリ「Gryphon」

Gryphon: REST API client kit for Swift

特徴

Gryphonは統一されたインターフェースのAPIクライアントを作成するためのライブラリで、以下の特徴があります。

  • Pure Swift
  • REST APIに対応しやすい
  • 型安全
  • 非同期処理を持たない
  • メソッドチェーンで簡潔に記述できる
  • 軽量である

使用例

client.swift
API.Messages.getMessage()
        .retry(max: 5)
        .interval(milliseconds: 500)
        .response { result in
            // Do something
        }

retryintervalなどはデフォルト機能として搭載されています。
GryphonでAPIを作成する場合、インターフェースは以下のようになります。

API.{機能郡名}.{機能名}

例えばユーザーに関連するエンドポイントがある場合にはこのようなイメージになります。

エンドポイント例: https://hoge.jp/users/

Gryphonを用いたネイティブでのAPI実装例:

API.Users.add()     //ユーザーの追加
API.Users.delete()  //ユーザーの削除
API.Users.patch()   //ユーザーの更新

これにより単一のAPIという窓口から操作でき、REST風なインターフェースで操作できるため開発者的にも嬉しいです。

Swift3でAlamofireを使用した例

より具体的な例としてSwift3Alamofireを使ったAPI設計例を紹介します。

はじめにAPIクラスを作成します。
APIにバージョニングを含む場合にはここへバージョン情報を持たせておくと良いでしょう。
その他APIの共通事項があればここに入れます。

API.swift
final class API {
    ....
}

次にAPIを拡張して機能郡を作成していきます。Gryphonでは機能郡へのアクセスを統一するためにRequestableプロトコルがあり、これに準拠したクラスを作成します。

Messages.swift
extension API {

  final class Messages: Requestable {

        // required `Requestable`
        static var baseURL: String {
            return "https://hoge.jp/"
        }

        // required `Requestable`
        static var path: String {
            return baseURL + "messages/"
        }

}

続いてリクエストを作成します。

Messages.swift
exntension Messages {

       // Task<通信に成功した時に返す型, エラー時に返す型>
       class func getMessage() -> Task<String, Error> {

            let task = Task<Message, Error> { result in

                Alamofire.request(path, method: .get, encoding: JSONEncoding.default)
                    .responseJSON(completionHandler: { response in

                        // JSONを好きな方法でデコード + バリデーション
                        let message: String = ...

                        result(APIResult<String>.response(message))

                    })

            }

            return task

        }

}

Task型の指定にはgetMessageで成功したときに返却するオブジェクトの型(今回はString)を指定します。

例えば、成功したときにはメッセージの配列、失敗したときにはステータスコードを取得したい場合には以下のように定義できます。

class func getMessages() -> Task<[Message],Int>

これでAPIの完成です。あとは以下のように利用するだけです。

client.swift
API.Messages.getMessage().response { result in

          switch result {
          case let .response(message):
          // `message`は非nilのString型で取得される
          // Do something
          case let .error(reason):
          // `reason`は非nilのError型で取得される
          // Do something
          }

        }

GryphonではTaskで指定した値が非Optionalでくるため、そのまま扱うことができます。
また、APIResult型により成功または失敗しかないためAPIの結果処理の網羅性も確保できます。

【応用編】 エンドポイントのスイッチング

またREST APIでは基本的にHTTPメソッドで操作しますが例えば以下のようなエンドポイントがあったとします。

http://hoge.jp/messages/ :GET
http://hoge.jp/messages/A/ :POST

このような場合は先ほどのMessageクラスの中に以下のコードを追加します。

Messages.swift
enum Router: String {
  case getMessage = ""
  case postMessage = "A/"
}

static var router: Router = .getMessage // Default routing

そして各リクエスト関数の中で
router = .postMessageにようにスイッチングするコードを記述し
Requestableのプロパティであるpathで以下のように設定します。

static var path: String {
    return baseURL + "messages/" + router.rawValue
}

このようにすることでエンドポイントは常にpathを監視すれば良いことになり複数のURIに安全にスイッチングすることができます。
また、リクエスト毎にurlプロパティを追加する必要がなくAPIとURIの関係がわかりやすくなります。

最後まで読んでいただきありがとうございました。