우선..
곰튀김님의 RxSwift 3시간 강의연상 정말 추천합니다.
( 저도 지인분을 통해서 추천받고 들었는데 정말 대박입니다 👍 )
( 왜 RxSwift가 필요하고, 왜 쓰이는지, 왜 그토록 많은 기업에서 사용하는지 이유를 알 수 있을 거에요! )
- 이 글은 곰튀김님의 강의 영상을 보면서 정리한 글 및 추가적으로 제가 RxSwift, RxCocoa를 사용하면서 제가 필요한 내용들만 적은 글입니다.
RxSwift github
RxSwift 사용하는 이유
- RxSwift를 사용하는 이유는, 비동기적으로 발생하는 결과값을 completion 클로저로 전달하지 않고, return값으로 전달하고자 위함입니다. 그럼으로써 좀 더 코드를 동기적으로 작성할 수 있고, 코드가 더 깔끔해진다는 장점이 있습니다.
- Swift만 사용하여 completion 클로저로 전달하지 않고 return값으로 보내는 타입 생성하기 ( 곰튀김님 영상에 있습니다 )
class Observable<T> { var task: (@escaping(T) -> Void) -> Void init(_ task: @escaping(@escaping(T) -> Void) -> Void) { self.task = task } func subscribe(_ completion: @escaping(T) -> Void ) { task(completion) } }
- 기본적인 RxSwift의 사용방법
-
func loadMembers() -> Observable<[Member]> { return Observable.create { emitter in let task = URLSession.shared.dataTask(with: URL(string: MEMBER_LIST_URL)!) { data, _, error in if let error = error { emitter.onError(error) return } guard let data = data, let members = try? JSONDecoder().decode([Member].self, from: data) else { emitter.onCompleted() return } emitter.onNext(members) emitter.onCompleted() } task.resume() return Disposables.create { task.cancel() } } } loadMembers() .observeOn(MainScheduler.instance) .subscribe(onNext: { [weak self] members in self?.data = members self?.tableView.reloadData() }) .disposed(by: disposeBag)
-
클로저내에서 참조하는 객체가 메모리누수가 되지 않도록 하기 위해서는...
- subscribe내에서 self를 사용한다면?
- Observable에서 Error or Completed 를 호출한다.
- Error or Completed를 호출하면 스트림이 끊기게 되므로, 신경 쓰지 않아도 된다.
- 호출하지 않는다면, weak self로 바꾸고, DisposeBag에 담아야한다. 또는 dispose를 호출해야한다.
- 왜 weak self로 바꾸어야 하냐면, 참조 횟수가 2 이상이 되어버리니, self가 메모리에서 해제되지 않고, DisposeBag도 사라지지 않기 때문이다.
- Observable에서 Error or Completed 를 호출한다.
- subscribe내에서 weak self를 사용한다면?
- Observable에서 Error or Completed 를 호출한다.
- 호출하지 않는다면 DisposeBag에 담아야한다. 또는 dispose를 호출해야한다.
- ( 하지만 곰튀김님의 말씀으로는 너무 RxSwift에 의존하기보다는 weak self를 사용하는 것을 추천한다고 하셨다. )
RxSwift의 유용한 Operator
- Reactivex.xo 에서 상단 - docs에서 확인할 수 있습니다.
- 기본적인 문법에서 더 간략하게 사용할 수 있는 sugar api들이 많이 있습니다.
- subscribe에서 event의 분기 처리를 하는 것이 귀찮으니, 특정 event만 고려하겠다 - (onNext: {} )
- just() .subscribe(onNext: { print($0 )} )
- subscribe에서 event의 분기 처리를 하는 것이 귀찮으니, 특정 event만 고려하겠다 - (onNext: {} )
- 특정 큐에서만 subscribe 하겠다. - observeOn
// main queue just() .observeOn(MainScheduler.instance) .subscribe(onNext: { print($0 )} )
- // background queue just() .observeOn(ConcurrentDispatchQueueScheduler(qos: .default)) .subscribe(onNext: { print($0 )} )
Subject
- 기본 Observable은 만들때 부터 이미 전달할 값이 정해져있다. 그러므로 외부에서 따로 Observable에게 특정값을 전달하라고 지시할 수가 없다. 그러므로 외부에서도 값을 전달할 수 있는 Observable이면서, Subject라는 타입이 있다.
var menuObservable: BehaviorSubject<[Menu]> = BehaviorSubject<[Menu]>(value: []) // 값을 Subject에 전달하기 viewModel.menuObservable.onNext([ Menu(name: "asdf", price: Int.random(in: 100..<200), count: Int.random(in: 0..<10)), Menu(name: "asdf", price: Int.random(in: 100..<200), count: Int.random(in: 0..<10)), ])
RxCocoa
- iOS, macOS, watchOS, tvOS의 개발에서 사용되는 유용한 기능들을 제공합니다.
- UIButton를 손쉽게 addTarget을 추가할 수 있습니다.
button.rx.tap .bind { print("clicked") } .disposed(by: disposeBag)
- tableview를 dataSource를 구현하지 않고 손쉽게 사용할 수 있습니다.
-
viewModel.allMenus .bind(to: tableView.rx.items(cellIdentifier: MenuItemTableViewCell.identifier, cellType: MenuItemTableViewCell.self)) { indexPath, item, cell in cell.updateView(by: item) } .disposed(by: disposeBag)
-
- 마찬가지로 손쉽게 UILabel의 text에도 subscribe할 수 있습니다.
-
viewModel.totalSelectedCountText .bind(to: itemCountLabel.rx.text) .disposed(by: disposeBag)
-
subscribe, bind, drive 차이
셋 다 결과값을 처리하는 방법에 사용되는데,
subscribe는 가장 기본적인 결과값을 처리하는 방법입니다. onNext, onError, onCompleted 를 처리할 수 있습니다.
bind는 에러처리를 할 필요가 없는 경우에 사용됩니다. 또한 위에서 봤듯이 ( to: ) 구문으로 UIKit과 손쉽게 바인딩할 수 있습니다
- 주석에서도, 에러가 발생할 경우 디버깅 모드에서는 fatalError가 발생하고, release모드에서는 error가 출력됩니다.
drive는 내부적으로 mainThread에서만 결과값을 받아오고, 처리하는 방법입니다.
- 주석에서도 나와있듯이, drive에서는 에러처리를 할 수가 없습니다.
'iOS' 카테고리의 다른 글
커스텀 폰트 용량 줄이기 - iOS (0) | 2021.09.01 |
---|---|
UICollectionView IndexPath 리팩토링 (0) | 2021.08.28 |
CoreData와 CloudKit 연동하기 (2) | 2021.08.18 |
좌우 스크롤되는 캘린더뷰 만들기( feat: CompositionalLayout ) (0) | 2021.08.09 |
각 주제별 WWDC 참고용 (0) | 2021.07.09 |