
네트워크 요청이 있는 경우 응답을 받기 까지 화면이 멈춰있으면 안되고 뭔가 일련의 작업을 수행중이라는 로딩바 호출이 프론트 개발에는 필수적이다. 이것을 좀 있어보이게 ActivityIndicator 라 부르는 것 같다(?)
프로젝트에서 RxSwift 를 사용했을때 썼던 코드가 있어 정리해둔다. (검색해보니 GitHub 오픈소스인것으로 보인다.)
import RxSwift
import RxCocoa
private struct ActivityToken<E>: ObservableConvertibleType, Disposable {
private let _source: Observable<E>
private let _dispose: Cancelable
init(source: Observable<E>, disposeAction: @escaping () -> Void) {
_source = source
_dispose = Disposables.create(with: disposeAction)
}
func dispose() {
_dispose.dispose()
}
func asObservable() -> Observable<E> {
return _source
}
}
/**
Enables monitoring of sequence computation.
If there is at least one sequence computation in progress, `true` will be sent.
When all activities complete `false` will be sent.
*/
public class ActivityIndicator: SharedSequenceConvertibleType {
public typealias Element = Bool
public typealias SharingStrategy = DriverSharingStrategy
private let _lock = NSRecursiveLock()
private let _relay = BehaviorRelay(value: 0)
private let _loading: SharedSequence<SharingStrategy, Bool>
public init() {
_loading = _relay.asDriver()
.map { $0 > 0 }
.distinctUntilChanged()
}
fileprivate func trackActivityOfObservable<Source: ObservableConvertibleType>(_ source: Source) -> Observable<Source.Element> {
return Observable.using({ () -> ActivityToken<Source.Element> in
self.increment()
return ActivityToken(source: source.asObservable(), disposeAction: self.decrement)
}) { t in
return t.asObservable()
}
}
private func increment() {
_lock.lock()
_relay.accept(_relay.value + 1)
_lock.unlock()
}
private func decrement() {
_lock.lock()
_relay.accept(_relay.value - 1)
_lock.unlock()
}
public func asSharedSequence() -> SharedSequence<SharingStrategy, Element> {
return _loading
}
}
extension ObservableConvertibleType {
public func trackActivity(_ activityIndicator: ActivityIndicator) -> Observable<Element> {
return activityIndicator.trackActivityOfObservable(self)
}
}
UIViewController 에서 ActivityIndicator를 호출할 경우 연동하여 사용하는 법도 같이 정리해둔다.
import UIKit
import RxSwift
import RxCocoa
/*
공통 UIViewController
*/
class BaseViewController: UIViewController {
var disposeBag = DisposeBag()
var activity = ActivityIndicator()
var errorTracker = ErrorTracker()
private var loadingView: LoadingView? // 로딩바가 돌아가는 UIView
var isLoading: Bool = true {
didSet {
if isLoading {
guard loadingView == nil else { return }
loadingView = LoadingView.fromNib()
if var topController = UIApplication.shared.keyWindow?.rootViewController {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
loadingView!.frame = topController.view.bounds
topController.view.addSubview(loadingView!)
topController.view.bringSubviewToFront(loadingView!)
}
} else {
loadingView?.removeFromSuperview()
loadingView = nil
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
setupRx()
}
func setupRx() {
activity.distinctUntilChanged()
.asObservable()
.bind(to: rx.isLoading)
.disposed(by: disposeBag)
}
}
BaseViewController 를 상속받은 UIViewController 에서는
아래 코드와 같이 trackActivity 할 경우 ActivityIndicator가 동작하게 된다.
apiService.exampleLogin() // RxSwift Observable 요청
.trackActivity(self.activity)
.trackError(self.errorTracker)
.subscribe(onNext: { [weak self] _ in
}).disposed(by: disposeBag)'iOS_RxSwift' 카테고리의 다른 글
| RxSwift 를 통한 UITableView 사용법에 대한 정리 (0) | 2022.02.01 |
|---|---|
| Subject, Relay 에 대한 활용 및 이해도 정리 (0) | 2022.01.24 |
| RxSwift + ReactorKit 에러 트래킹 Custom 방식에 대한 정리 (0) | 2022.01.14 |
| RxSwift ErrorTracker GitHub 코드 정리 (0) | 2022.01.11 |
| return Single<> 비동기 처리 (0) | 2022.01.11 |