ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • ReactorKit 에서 API 활용하기
    Swift/RxSwift 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()
                
        }
    }

    'Swift > RxSwift' 카테고리의 다른 글

    ReactorKit 으로 로그인 페이지 만들기  (0) 2022.10.14
    RxAlamofire  (0) 2022.10.03
    RxSwift Observable, Subjects and Relays  (0) 2022.10.01
    UITableView (with RxSwift)  (2) 2022.09.30
    Login Validation (simple..) RxSwift 구현  (0) 2022.09.29
Designed by Tistory.