Swift/RxSwift
ReactorKit 에서 API 활용하기
iosswift
2022. 10. 15. 10:13
// 출처: https://nsios.tistory.com/141
(출처 코드를 일부 수정했습니다. )
중요한 Code
1. Controller
class APIPracticeController: UIViewController, View {
var disposeBag = DisposeBag()
func bind(reactor: MainReactor) {
loadButton.rx.tap
.map({ Reactor.Action.touchButton(index: 0) })
.bind(to: reactor.action)
.disposed(by: disposeBag)
reactor.state
.map({$0.image})
.subscribe(onNext: {[weak self] img in
guard let img = img, let self = self else { return }
DispatchQueue.main.async {
self.centerImageView.image = img
}
})
.disposed(by: disposeBag)
}
}
2. Reactor, Network
class MainReactor: Reactor {
let network = Network()
private let URLs = ["https://avatars.githubusercontent.com/u/62657991?v=4"]
enum Action {
case touchButton(index: Int)
}
enum Mutation {
case setImage(image: UIImage?)
}
struct State {
var image: UIImage?
}
let initialState: State
init() {
self.initialState = State()
}
func mutate(action: Action) -> Observable<Mutation> {
switch action {
case .touchButton(let index):
return Observable.just(URLs[index])
.flatMap({ self.network.load(url: $0) })
.map({ Mutation.setImage(image: $0) })
}
}
func reduce(state: State, mutation: Mutation) -> State {
var state = state
switch mutation {
case .setImage(let image):
state.image = image
}
return state
}
}
class Network {
func load(url: String) -> Single<UIImage?> {
let request = URLRequest(url: URL(string: url)!)
return URLSession.shared.rx.response(request: request)
.map({ UIImage(data: $0.data) })
.asSingle()
}
}
API Call 이용할 때 작동 순서!
1. 버튼을 누른다 -> Reactor 의 Action 에 전달.
2. func mutate(action: Action) 을 통해 return Mutation.setImage
case .touchButton(let index):
return Observable.just(URLs[index])
.flatMap({ self.network.load(url: $0) })
.map({ Mutation.setImage(image: $0) })
}
3. 2 과정에서, networkCall 을 통해 URL -> Image 로 변환.
이때, flatMap 안에서 url -> Observable<UIImage> 로 변환해준다.
switch mutation {
case .setImage(let image):
state.image = image
}
4. 그 후에는 func mutate 에서 위와 같이 state 의 image property 를 설정, 이어 ViewController 에 전달.
전체 코드
import Foundation
import RxSwift
import ReactorKit
import UIKit
import RxCocoa
class APIPracticeController: UIViewController, View {
var disposeBag = DisposeBag()
let loadButton: UIButton = {
let button = UIButton()
button.setTitle("load Image", for: .normal)
button.setTitleColor(.systemBlue, for: .normal)
return button
}()
init(reactor: MainReactor) {
super.init(nibName: nil, bundle: nil)
self.reactor = reactor
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
lazy var centerImageView: UIImageView = {
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 350, height: 350))
imageView.image = UIImage()
imageView.center = view.center
imageView.backgroundColor = .magenta
return imageView
}()
override func viewDidLoad() {
super.viewDidLoad()
makeUI()
}
func makeUI() {
view.addSubview(centerImageView)
view.addSubview(loadButton)
loadButton.translatesAutoresizingMaskIntoConstraints = false
loadButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
loadButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
}
func bind(reactor: MainReactor) {
loadButton.rx.tap
.map({ Reactor.Action.touchButton(index: 0) })
.bind(to: reactor.action)
.disposed(by: disposeBag)
// reactor.state
// .map({$0.image})
// .bind(to: centerImageView.rx.image)
//// .bind(to: centerImageView.imag)
// .disposed(by: disposeBag)
reactor.state
.map({$0.image})
.subscribe(onNext: {[weak self] img in
if let img = img {
DispatchQueue.main.async {
self?.centerImageView.image = img
}
} else {
print("img is nil")
}
})
.disposed(by: disposeBag)
}
}
import Foundation
import RxSwift
import ReactorKit
class MainReactor: Reactor {
let network = Network()
private let URLs = ["https://avatars.githubusercontent.com/u/62657991?v=4"]
enum Action {
case touchButton(index: Int)
}
enum Mutation {
case setImage(image: UIImage?)
}
struct State {
var image: UIImage?
}
let initialState: State
init() {
self.initialState = State()
}
func mutate(action: Action) -> Observable<Mutation> {
switch action {
case .touchButton(let index):
return Observable.just(URLs[index])
.flatMap({ self.network.load(url: $0) })
.map { img -> UIImage? in
return img
}
.map({ Mutation.setImage(image: $0) })
}
}
func reduce(state: State, mutation: Mutation) -> State {
var state = state
switch mutation {
case .setImage(let image):
state.image = image
}
return state
}
}
class Network {
func load(url: String) -> Single<UIImage?> {
let request = URLRequest(url: URL(string: url)!)
print("request: \(request)")
return URLSession.shared.rx.response(request: request)
.map({ UIImage(data: $0.data) })
.asSingle()
}
}