はじめに
API通信でおなじみのMoyaですがRxMoyaも備わっているのでRxSwiftと絡めた実装をやってみます。
シンプルに、Qiitaで「iOS」というタグでAPIを叩きます。
ソースコード
まずはAPI
API
QiitaAPI.swift
import Foundation
import Moya
enum QiitaError: Error {
case error
}
enum QiitaAPI {
case user
}
extension QiitaAPI: TargetType {
var baseURL: URL {
guard let url = URL(string: "https://qiita.com/api/v2/") else {
fatalError("base URL error")
}
return url
}
var path: String {
switch self {
case .user:
return "tags/\(QiitaSearchTag.tag)/items"
}
}
var method: Moya.Method {
return Moya.Method.get
}
var sampleData: Data {
return Data()
}
var task: Task {
return .requestPlain
}
var headers: [String: String]? {
return nil
}
}
QiitaSearchTag.tagを作成して外部で定数化しました。
Model
QiitaModel.swift
import Foundation
struct QiitaModel: Decodable {
var title: String?
var user: QiitaUser
let url: String?
}
struct QiitaUser: Decodable {
var profileImageUrl: String?
enum CodingKeys: String, CodingKey {
case profileImageUrl = "profile_image_url"
}
}
ViewModel
QiitaViewModel.swift
import Foundation
import Moya
import RxCocoa
import RxMoya
import RxSwift
final class QiitaViewModel {
var models = BehaviorRelay<[QiitaModel]>(value: [])
private var disposeBag = DisposeBag()
let provider = MoyaProvider<QiitaAPI>()
func requestDataSource() {
provider.rx.request(.user)
.filterSuccessfulStatusCodes()
.map([QiitaModel].self)
.subscribe(onSuccess: { (response) in
self.models.accept(response)
}) { (error) in
print(error)
}
.disposed(by: disposeBag)
}
}
View(ViewController)
QiitaClientViewController
import RxSwift
import UIKit
final class QiitaClientViewController: UIViewController {
#//tableViewのセットアップは省略
private let viewModel = QiitaViewModel()
private var disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
bind()
viewModel.requestDataSource()
}
private func bind() {
viewModel.models
.bind(to: tableView.rx.items(cellIdentifier: tableViewCell, cellType: TableViewCell.self)) { _, element, cell in cell.configure(model: element) }.disposed(by: disposeBag)
tableView.rx.modelSelected(QiitaModel.self)
.subscribe(onNext: { response in
guard let url = response.url,
let _url = URL(string: url) else {
return
}
UIApplication.shared.open(_url, options: [:], completionHandler: nil)
})
.disposed(by: disposeBag)
}
}
tableView箇所の記述が劇的にスッキリしましたね。
使い方は徐々になれるしかないと思いました。
間違っている箇所やより良い書き方があればご指摘お願いします。