Money Forward Developers Blog

株式会社マネーフォワード公式開発者向けブログです。技術や開発手法、イベント登壇などを発信します。サービスに関するご質問は、各サービス窓口までご連絡ください。

20230215130734

Swift2.0で作るAPI通信基盤

こんにちは。エンジニアの浅井です。 普段はマネーフォワードのiOSアプリ開発を担当しています。

弊社では、全社で使用する共通ライブラリの開発を積極的に行っています。 今回は、先日正式リリースされたSwift2.0で作成中のAPI通信基盤をご紹介します。

APIKitを参考に、下記のライブラリを用いて実装しています。 * Alamofire(HTTP通信) * ObjectMapper(JSONのパース)  

本エントリーの肝

本エントリーではSwift2.0で追加された、ProtocolExtensionを利用しています。 プロトコルに実装を定義できるだけでなく、細かく条件指定ができるので、そのプロトコルに準拠したクラスの実装を少なく抑えることができます。 また、typealiasによるジェネリクスを利用することで、タイプセーフな設計になっています。  

利用側のコード

兎にも角にもまずは利用側のコードです。

API.call(Endpoint.GetUsers()) { result in
    switch result {
    case .Success(let users):
        // 成功時の処理
        println("success")
    case .Failure(let data, let error):
        // 失敗時の処理
        println("failure")
    }
}
  • API.callをリクエストオブジェクトを引数にして呼び出す(後述RequestProtocol
  • 返却されるresultAlamofire.Result
  • Success時のusersはジェネリクスにより型が明らかになっている(後述ResponseType

APIの例

例えばこんなAPIがあったとします。

URL
https://api.test.com/users
Response
{
  "users": [
    {
      id: 1,
      name: "yuki"
    },
    {
      id: 2,
      name: "asai"
    }
  ]
}

こんなときObjectMapperを用いたモデルクラスは以下のように定義するでしょう。

class Users: Mappalbe {
    var users: [User]?
 
    required init?(_ map: Map) {
        mapping(map)
    }

    func mapping(map: Map) {
        users <- map["users"]
    }
}

class User: Mappable {
    var id: Int?
    var name: String?

    required init?(_ map: Map) {
        mapping(map)
    }

    func mapping(map: Map) {
        id <- map["id"]
        name <- map["name"]
    }
}

そしてリクエストオブジェクトは以下のように定義します。

class Endpoint {
    class GetUsers: RequestProtocol {
        typealias ResponseType = Users

        var baseUrl: String {
            return "https://api.test.com/"
        }

        var path: String {
            return "users"
        }
    }
}

このResponseTypeが成功時のresultの値の型になります。  

ライブラリ側のコード

APIの定義

Alamofireを用いることで、HTTP通信の処理もすっきりしています。

class API {
    class func call<T: RequestProtocol, V where T.ResponseType == V>(request: T, completion: (Result<V>) -> Void) {
        Alamofire.request(request)
            .responseJSON { req, res, result in
                switch result {
                case .Success(let json):
                    completion(request.fromJson(json))
                case .Failure(let data, let error):
                    completion(.Failure(data, error))
                }
            }
    }
}

RequestProtocolの定義

protocol RequestProtocol: URLRequestConvertible {
    typealias ResponseType
    
    var method: Method { get }
    var baseUrl: String { get }
    var path: String { get }
    var parameters: [String: AnyObject]? { get }
    var encoding: ParameterEncoding { get }
    var headers: [String: String]? { get }
    
    func fromJson(json: AnyObject) -> Result<ResponseType>
}

extension RequestProtocol {
    var method: Method {
        return .GET
    }
    
    var parameters: [String: AnyObject]? {
        return nil
    }
    
    var encoding: ParameterEncoding {
        return .URL
    }
    
    var headers: [String: String]? {
        return nil
    }

    func fromJson(json: AnyObject) -> Result<ResponseType> {
        guard let value = json as? ResponseType else {
            return .Failure(nil, error(0, localizedDescription: "Convert object failed"))
        }
        return .Success(value)
    }
}
ObjectMapper対応

ResponseTypeがObjectMapper.Mappalbeに準拠したクラスでであれば、ObjectMapperのマッピング処理を行うよう、ProtocolExtensionで定義します。

extension RequestProtocol where ResponseType: Mappable {
    func fromJson(json: AnyObject) -> Result<ResponseType> {
        guard let value = Mapper<ResponseType>().map(json) else {
            return .Failure(nil, error(0, localizedDescription: "Mapping object failed"))
        }
        return .Success(value)
    }
}

JSONをそのまま利用したい場合はRequestProtocolを準拠したクラスでfromJsonを都度実装してやればよいですし、ProtocolExtensionで拡張していくことで、様々なマッパーに対応させることができるでしょう。  

最後に

マネーフォワードでは、新しい技術も積極的に取り入れていくエンジニアを募集しています。 ご応募お待ちしております!

【採用サイト】 ■『マネーフォワード採用サイト』 https://recruit.moneyforward.com/『Wantedly』 https://www.wantedly.com/companies/moneyforward

【公開カレンダー】 ■マネーフォワード公開カレンダー

【プロダクト一覧】 ■家計簿アプリ・クラウド家計簿ソフト『マネーフォワード』 https://moneyforward.com/家計簿アプリ・クラウド家計簿ソフト『マネーフォワード』 iPhone,iPad家計簿アプリ・クラウド家計簿ソフト『マネーフォワード』 Androidクラウド型会計ソフト『MFクラウド会計』 https://biz.moneyforward.com/クラウド型請求書管理ソフト『MFクラウド請求書』 https://invoice.moneyforward.com/クラウド型給与計算ソフト『MFクラウド給与』 https://payroll.moneyforward.com/消込ソフト・システム『MFクラウド消込』 https://biz.moneyforward.com/reconciliation/マイナンバー対応『MFクラウドマイナンバー』 https://biz.moneyforward.com/mynumber