はじめに
AlamofireでAPIを叩いてresponseDecodable(of: StatusesShow.self)
でデコードされた結果を受け取る。
今までこの方法で使っていました。
この時返ってくる値はResult<StatusesShow, AFError>
です。
例えばTwitterのAPIを叩いたとしましょう。
TwitterErrorを使いたいと思うとこのように美しくない実装になります。
AF.request(url, method: .get, headers: headers).responseDecodable(of: StatusesShow.self) { response in
switch response.result {
case .success(let data):
print(data)
case .failure(_):
guard let data = response.data, let localRateLimited = String(data: data, encoding: .utf8) else { return }
do {
let twitterError = try JSONDecoder().decode(TwitterError.self, from: data)
print(twitterError)
} catch {
print(localRateLimited)
}
}
}
Result<StatusesShow, TwitterError>
で返ってきてほしい!!!
カスタムレスポンスを作成して美しいコードにします。
やりかた
ResponseSerializer
を使用します。
import Alamofire
import Foundation
final class TwitterDecodableResponseSerializer<T: Decodable>: ResponseSerializer {
private lazy var successSerializer = DecodableResponseSerializer<T>(decoder: JSONDecoder())
private lazy var errorSerializer = DecodableResponseSerializer<TwitterError>(decoder: JSONDecoder())
public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> Result<T, ErrorCode> {
guard error == nil else { return .failure(.code000) }
guard let response else { return .failure(.code000) }
do {
// 成功
let result = try successSerializer.serialize(request: request, response: response, data: data, error: nil)
return .success(result)
} catch {
// 失敗
let result = try errorSerializer.serialize(request: request, response: response, data: data, error: nil)
return .failure(result.errors[0].code)
}
}
}
extension DataRequest {
@discardableResult func responseTwitterDecodable<T: Decodable>(queue: DispatchQueue = DispatchQueue.global(qos: .userInitiated), of t: T.Type, completionHandler: @escaping (Result<T, ErrorCode>) -> Void) -> Self {
return response(queue: .main, responseSerializer: TwitterDecodableResponseSerializer<T>()) { response in
switch response.result {
case .success(let result):
// 成功 or 失敗
completionHandler(result)
case .failure(_):
// 想定外のレスポンス
guard let data = response.data, let localRateLimited = String(data: data, encoding: .utf8) else { return }
print(localRateLimited)
}
}
}
}
使い方
はい〜、美しい
AF.request(url, method: .get, headers: headers).responseTwitterDecodable(of: StatusesShow.self) { response in
switch response {
case .success(let data):
print(type(of: data)) // StatusesShow
case .failure(let error):
print(type(of: error)) // TwitterError
}
}
おわり
めっちゃ綺麗になりました!
参考記事