Swiftで通信といえばAlamofire
等の素晴らしいライブラリが多数ありますが、実装するときにはプロジェクト毎に設計や決まりがあり、導入ライブラリが決まっていたとしても、その設計をどうするかについてはプロジェクト毎に多様です。
そこで今回はSwift4
で統一したインターフェースでAPIクライアントを作成するためのライブラリを紹介したいと思います。
今回紹介するライブラリ「Gryphon」
Gryphon: REST API client kit for Swift
特徴
Gryphon
は統一されたインターフェースのAPIクライアントを作成するためのライブラリで、以下の特徴があります。
- Pure Swift
- REST APIに対応しやすい
- 型安全
- 非同期処理を持たない
- メソッドチェーンで簡潔に記述できる
- 軽量である
使用例
API.Messages.getMessage()
.retry(max: 5)
.interval(milliseconds: 500)
.response { result in
// Do something
}
retry
やinterval
などはデフォルト機能として搭載されています。
Gryphon
でAPIを作成する場合、インターフェースは以下のようになります。
API.{機能郡名}.{機能名}
例えばユーザーに関連するエンドポイントがある場合にはこのようなイメージになります。
エンドポイント例: https://hoge.jp/users/
Gryphonを用いたネイティブでのAPI実装例:
API.Users.add() //ユーザーの追加
API.Users.delete() //ユーザーの削除
API.Users.patch() //ユーザーの更新
これにより単一のAPIという窓口から操作でき、REST風なインターフェースで操作できるため開発者的にも嬉しいです。
Swift4でAlamofireを使用した例
より具体的な例としてAlamofire
を使ったAPI設計例を紹介します。
はじめにAPI
クラスを作成します。
APIにバージョニングを含む場合にはここへバージョン情報を持たせておくと良いでしょう。
その他APIの共通事項があればここに入れます。
final class API {
....
}
次にAPI
を拡張して機能郡を作成していきます。Gryphon
では機能郡へのアクセスを統一するためにRequestable
プロトコルがあり、これに準拠したクラスを作成します。
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/"
}
}
続いてリクエストを作成します。
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の完成です。あとは以下のように利用するだけです。
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クラスの中に以下のコードを追加します。
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の関係がわかりやすくなります。
最後まで読んでいただきありがとうございました。