Genericsを用いて安全にJSONをパースする (Alamofire x Swift4)

## はじめに

SwiftでAlamofireを利用する際，毎回Modelを利用するのは面倒ですよね。
Codableを利用して簡単に書きましょう!

## Alamofireを利用した `baseRequest()` の作成

ここでは，`typealias` を利用して，`T`に様々な型が入るようにしたクロージャを定義し，
`Decodable`をクロージャで引き受けつつ通信を行う`baseRequest()`を作成しています。

```swift
import Alamofire

class Foo {
    /// Success handler
    public typealias SuccessHandler<T> = (_ model: T) -> Void

    /// Failure handler
    public typealias FailureHandler = (_ error: Error) -> Void

    /**
     Alamofire Request

     - Parameters:
         - url: API URL
         - method: HTTP method
         - parameters: Query parameters
         - success: Success handler
         - failure: Failure handler
     */
    func baseRequest<T: Decodable>(_ url: String,
                               method: HTTPMethod = .get,
                               parameters: Parameters? = nil,
                               success: SuccessHandler<T>?,
                               failure: FailureHandler?) {

        Alamofire.request(url, method: method, parameters: parameters, encoding: encoding, headers: headers)
            .responseData { response in
                switch response.result {
                case .success:
                    guard let success = success, let data = response.data else { return }
                    let decoder: JSONDecoder = JSONDecoder()
                    guard let model = try? decoder.decode(T.self, from: data) else { return }
                    success?(model)
                case .failure(let error):
                    failure?(error)
                }
        }
    }
}
```

`guard let model = try? decoder.decode(T.self, from: data) else { return }`部分では，デコード処理を記述していますが，
引数`success: SuccessHandler<T>`の`T`の型によって`T.self`が変更され，デコード先が様々な型を許容することを示しています。

## 利用方法

以下のようなModelを定義してみました。

```swift
public struct FooModel: Codable {
    public let bar: String
}
```

ここで新たに `SuccessHandler` に `FooModel` を代入した `request` methodを立てます。

```swift
func request(_ url: String,
             method: HTTPMethod = .get,
             parameters: Parameters? = nil,
             success: SuccessHandler<FooModel>?,
             failure: FailureHandler?) {

    self.baseRequest(url, 
                     method: method, 
                     parameters: parameters,
                     success: success,
                     failure: failure)
}
```

これにより， `request()` 経由で取得したResponseは`FooModel`にパースされて処理できるようになります。

呼び出しは以下の通りです。

```swift
self.request(url, success: { fooModel in
    print(fooModel.bar)
})
```

## デコードのテストを書く

せっかく便利になったので，Codableなstructがきちんとデコードされるかをテストしましょう。
Codableはしっかり定義しないと全てがnilとなってしまいます。テストは必要ですよね?

```swift
import XCTest
@testable import Foo

final class FooTests: XCTestCase {
    var foo: Foo!

    override func setUp() {
        super.setUp()
        self.foo = Foo()
    }

    func decode<T: Decodable>(_ url: String, parameters: Parameters?, model: T.Type) {
        let expect: XCTestExpectation = self.expectation(description: url)

        let success: Foo.SuccessHandler<T> = { model in
            expect.fulfill()
            XCTAssertNotNil(model)
        }
        let failure: Foo.FailureHandler = { error in
            expect.fulfill()
            XCTFail(error.localizedDescription)
        }

        self.foo.baseRequest(url, success: success, parameters: parameters, failure: failure)
        self.wait(for: [expect], timeout: 10)
    }
}
```

呼び出しは以下のように書きます。

```swift
func testDecodeFoo() {
    decode(url, parameters: parameters. model: FooModel.self)
}
```

これでModelが増えても簡単に書けるようになりました。

それでは良いAlamofireライフを!

## 参考

- [YutoMizutani/CiNiiKit - GitHub](https://github.com/YutoMizutani/CiNiiKit)
- [Alamofire Generic Response Object Serialization using ‘Codable’](https://medium.com/@mhacnagbani/alamofire-generic-response-object-serialization-using-codable-13db342347b2)
- [Advanced Usage - Alamofire/Alamofire](https://github.com/Alamofire/Alamofire/blob/ff16ce9e87aeb0ee1f30b28789db1fff01e8fb02/Documentation/AdvancedUsage.md)