[Swift] Moya + Alamofire

안드로이드에서는 Retrofit을 이용해서 꽤 깔끔한 구조의 네트워크 모듈을 만들 수 있는데, swift에서 alamofire로 하려니까 좀 어려운 감이 있다. 찾아보니 Moya라는 것이 있어서 공부해봤는데, 완전히 마음에 드는 건 아니지만 RxAlamofire 보다는 나은 것 같다.

상속용 Repository 클래스를 만든다.

import Foundation
import Moya
import RxMoya
import RxSwift

/// 네트워크 호출 상속용
/// https://github.com/Moya/Moya
class BaseRepository<API: TargetType> {
  let disposeBag = DisposeBag()
  private let provider = MoyaProvider<API>()
  lazy var rx = provider.rx
}

상속용 API 클래스도 만든다. 저 변수들은 일종의 초기값을 지정해두는 거고, 필요하면 자식 클래스에서 다시 선언해서 사용하면 된다.

import Foundation
import Moya

protocol BaseAPI: TargetType {}

extension BaseAPI {
  var baseURL: URL { URL(string: "https://api.github.com")! }

  var method: Moya.Method { .get }

  var sampleData: Data { Data() }

  var task: Task { .requestPlain }

  var headers: [String: String]? { nil }
}

자동으로 변환받을 모델 클래스도 만든다.

import Foundation

struct TestModel: Decodable {
  var id: Int
  var name: String
  var login: String
}

실제 Repository 클래스를 만든다.

import Foundation
import Moya

final class TestRepository: BaseRepository<TestAPI> {
  static let shared = TestRepository()
  private override init() {}

  /// 아이디로 사용자 정보 가져오기
  /// - Parameters:
  ///   - name: 로그인 아이디
  ///   - completion: 완료 후 호출
  func user(_ name: String, _ completion: @escaping (TestModel?, Error?) -> Void) {
    rx.request(.profile(name))
      .filterSuccessfulStatusCodes()
      .map(TestModel.self)
//      .debug()
      .subscribe(onSuccess: { completion($0, nil) }, onError: { completion(nil, $0) })
      .disposed(by: disposeBag)
  }
}

enum TestAPI {
  case profile(String)
}

extension TestAPI: BaseAPI {
  var path: String {
    switch self {
    case let .profile(name):
      return "/users/\(name)"
    }
  }
}

실제 호출은 이렇게 하면 된다.

TestRepository.shared.user("susemi99") {
  if let error = $1 { print("error: \(error)") }

  guard let user = $0 else { return }
  print("user: \(user)")
}