본문 바로가기

IOS

Swift - ReactorKit2

https://moonggi-dev-story.tistory.com/48 글에 이어서 ReactorKit 에 대해 적도록 하겠습니다.

간단하게 Alamofire, SwiftyJSON 를 이용하여 통신을 진행 하도록 하겠습니다.

 

먼저 결과를 보여드리자면......

1. 초기에 로딩전이라는 메세지를 띄운다

2. 통신 버튼 누를시 -> 로딩중 으로 바꾼뒤 통신이 완료 되면 결과 메세지와 함께 로딩 메세지도 변경한다.


1. View (ViewController)

 

import UIKit
import RxSwift
import RxCocoa
import RxGesture
import ReactorKit

class ViewController: UIViewController {

    let reactor = ViewReactor()
    let disposeBag = DisposeBag()
    
    @IBOutlet weak var resultTextView: UITextView!
    @IBOutlet weak var httpButton: UIButton!
    @IBOutlet weak var loadingText: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        bind(reactor: reactor)
    }
    
    func bind(reactor : ViewReactor) {
        
        httpButton.rx.tap
            .map{ _ in  ViewReactor.Action.http }
            .bind(to: reactor.action)
            .disposed(by: disposeBag)
        
        reactor.state.map{ $0.isLoading }
            .distinctUntilChanged()
            .subscribe(onNext : {
                [self] isLoading in
                print(isLoading)
                loadingText.text = isLoading ? "로딩중" : "로딩전"
            }).disposed(by: disposeBag)
        
        reactor.state.map{ $0.text }
            .distinctUntilChanged()
            .subscribe(onNext : {
                value in
                self.resultTextView.text = "\(value)"
            }).disposed(by: disposeBag)
    }
}

 


2. HttpUtil

import UIKit
import RxSwift
import Alamofire
import SwiftyJSON

enum ConnectError:Error {
    case APIException
}

class HttpUtil
{
    
    static func setConnect(_ url:String, _ parameter:Parameters) -> Observable<DataResponse<Data, AFError>> {
        
        return Observable.create() { emitter in
            
            let headers: HTTPHeaders = [
                "Accept": "application/json"
            ]
            
            AF.request(url,
                            method: .get,
                             parameters: parameter,
                             encoding: URLEncoding.default,
                             headers: headers
            )
            
                .validate(statusCode:200..<300)
                .responseData(completionHandler: {
                    (response) in
                    
                    switch response.result {
                        case .failure(_):
                            emitter.onError(ConnectError.APIException)
                        case .success(_):
                            emitter.onNext(response)
                            emitter.onCompleted()
                    }
                })
            
            return Disposables.create()
        }
    }
}

3. Reactor (ViewReactor)

import Foundation
import RxSwift
import RxCocoa
import RxGesture
import ReactorKit
import Alamofire
import SwiftyJSON

class ViewReactor: Reactor {
    
    let initialState = State()
    
    enum Action {
        case http
    }
    
    struct State {
        var isLoading : Bool = false
        var text : String = ""
    }
    
    enum Mutation {
        case setLoading(Bool)
        case setResult(String)
    }
    
    func mutate(action: Action) -> Observable<Mutation> {
        switch action {
            case .http:
                
                return Observable.concat([
                    Observable.just(Mutation.setLoading(true)),
                    setHttp().map{Mutation.setResult($0)},
                    Observable.just(Mutation.setLoading(false))
                ])
        }
    }
    
    func reduce(state: State, mutation: Mutation) -> State {
        var state = state
        switch mutation {
            case let .setLoading(val):
                state.isLoading = val
            case let.setResult(val):
                state.text = val
        }
        return state
    }
    
    func setHttp() -> Observable<String> {
        
        return Observable.create() {
            emitter in
            
            let parameter : Parameters = [:]
            let url : String = "https://jsonplaceholder.typicode.com/posts"
            var result : String = ""
            
            HttpUtil.setConnect(url, parameter)
                .subscribe(on: ConcurrentDispatchQueueScheduler.init(qos: .default))
                .subscribe {
                    event in
                    
                    switch event {
                    case .next(let json):
                        
                        if let json = try? JSON(data: json.data!) {
                            
                            if let siArray = json.array {
                                
                                emitter.onNext("body : \(siArray[0]["body"].stringValue)\nid : \(siArray[0]["id"].stringValue)\nuserId : \(siArray[0]["userId"].stringValue)\ntitle : \(siArray[0]["title"].stringValue)")
                                emitter.onCompleted()
                            }
                        }
                    
                        break
                    case .completed:
                        result = "completed"
                        break
                    case .error:
                        result = "error"
                        break
                    }
                }
            
            return Disposables.create()
        }
        
    }
}

Concat를 사용할때 Http 통신을 Observerble 형태로 만들어서 해당 Observerble를 끝내지 않고 진행하게 만들경우

통신하는 시간보다 setLoading(false) 가 먼저 불릴수 있었습니다. 

간단하게 Http 통신하는 프로세스 만들기 위해 참고 하시면 좋을꺼 같습니다.

 

ReactorKit 를 사용하면서 지속적인 이슈 사항 있을시에 올리도록 노력 하겠습니다.

 

 

 

 

 

'IOS' 카테고리의 다른 글

Swift - UICollectionView + Paging  (0) 2021.07.13
Swift - SceneDelegate  (0) 2021.07.10
Swift - ReactorKit  (0) 2021.06.09
Swift - Photos + CollectionView + Drag and Drops  (0) 2021.05.30
Swift - Photos + CollectionView + Gesture Multiple Select  (0) 2021.03.07