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 |