RxSwift in 4 hours 정리(2)

2021. 3. 18. 00:03· RxSwift
목차
  1. [2교시] RxSwift 활용하기 - 쓰레드의 활용과 메모리 관리
  2. [3교시] RxSwift 활용범위 넓히기 - UI 컴포넌트와의 연동
  3. Subject
  4. RxCocoa
  5. [4교시] RxSwift 를 활용한 아키텍쳐 - 프로젝트에 MVVM 적용하기

[2교시] RxSwift 활용하기 - 쓰레드의 활용과 메모리 관리

 

1. Merge: Observable 여러개를 묶어서 하나의 Observable로 만들어줌.

2. Zip: 위 아래 데이터가 하나씩 생성되면 그것들을 쌍으로 하나 만들어서 내려줌.

3. CombineLatest: 쌍이 하나 없으면 못내려가는 zip과 달리 가장 최근에 나온 것과 쌍으로 만들어줌.

// zip: Observable들을 합쳐서 처리

let jsonObservable = downloadJson(MEMBER_LIST_URL)
let helloObservable = Observable.just("hello world")

_ = Observable.zip(jsonObservable, helloObservable) { $1 + "\n" + $0 }
    .observeOn(MainScheduler.instance)
    .subscribe(onNext: { json in
        self.editView.text = json
        self.setVisibleWithAnimation(self.activityIndicator, false)
    })

 

 

 

 

[3교시] RxSwift 활용범위 넓히기 - UI 컴포넌트와의 연동

Subject

  • 어떤 데이터를 보내야할지가 미리 정해진 스트림인 Observable과 달리 Subject는 Observable 밖에서 데이터를 주입할 수 있다.
  • Subject는 데이터를 컨트롤하여 새로운 값을 넣어줄 수도 있고, 구독할 수도 있는 양방향성을 가진다.
  • Observable.create 했을 때 받는 emitter의 역할과 비슷하나 create 안에서 쓰이는게 아니라 이미 만들어진 Observable 밖에서 사용이 가능하다.
  • Subject를 만들어놓으면 외부에서 onNext로 데이터를 넣어줄 수 있다.
  • 일반 Observable처럼 Subscribe도 가능하다.

 

 

1. AsyncSubject

 

여러개가 구독을 하더라도 데이터를 내려보내지 않고 complete되는 시점에서 가장 마지막에 있던 것들을 모두 내려주고 complete 시킨다.

 

 

2. PublishSubject

  • Subject에 누군가가 subscribe 할 수 있음.
  • 내부에서 데이터를 생성하면 그 데이터를 그대로 내려줌.
  • 다른 subscriber가 또 새롭게 Subscribe하면 그 때 Subscribe하고 있는 모든 애들한테 데이터 전달.
  • 데이터를 넣어주면 그 시점에 전달이 됨.
//lazy var menuObservable = Observable.just(menus) //menus에 대한 observable
// Observable.just면 밖에서 컨트롤 못하니 바뀔 수 있어야하니까 뭔가 바뀔 수 있는 형태로의 전환이 필요함
// PublishSubject로 만들어줌 [Menu] 타입으로


lazy var menuObservable = PublishSubject<[Menu]>()
// [Menu]를 받을 예정. [Menu]가 외부로부터 주어지면 그때마다 observable이 계속 동작할 예정.


lazy var itemsCount = menuObservable.map {
    $0.map { $0.count }.reduce(0, +)
    // menuObservable의 값이 바뀔대마다 count의 총합으로 바꿔서 밑으로 내려보내주는 observable.
}

lazy var totalPrice = menuObservable.map {
        $0.map { $0.price * $0.count }.reduce(0, +)
        
// menuObservable(menus)의 값이 바뀌면 그 값이 바뀔때마다 다시 계산되어서 총합이 내려옴.
// menuObservable의 값이 바뀔때마다 가격의 총합으로 계산하여 밑으로 내려보내주는 observable.

init() {
  let menus: [Menu] = [
      Menu(id: 0, name: "튀김1", price: 1000, count: 0),
      Menu(id: 0, name: "튀김1", price: 1000, count: 0),
      Menu(id: 0, name: "튀김1", price: 1000, count: 0),
      Menu(id: 0, name: "튀김1", price: 1000, count: 0),
   	]
    
   menuObservable.onNext(menus)
}
// - MenuListViewModel -

var totalPrice: PublishSubject<Int> = PublishSubject()

// - MenuViewController -

//totalPrice는 Observable인데 map으로 currencyKR로 바꾼 다음 susbcribe onNext로 가져와서 self.totalprice의 텍스트에 집어넣음.
viewModel.totalPrice
    .scan(0, accumulator: +) //0부터 시작해서 새로운 값이 들어오면 기존의 값이랑 더하겠다는 의미
    .map { $0.currencyKR() }
    .observeOn(MainScheduler.instance)
    .bind(to: totalPrice.rx.text )
    //맨 처음 subscribe에 의해 값이 바뀔때마다 onNext로 전달해서 값이 계속 바뀜. updateUI()를 계속 호출할 필요가 없음.
    .disposed(by: disposeBag)

 

 

 

3. BehaviorSubject

  • BehaviorSubject는 기본값을 하나 가지고 있음.
  • 누군가 Subscribe 하자마자 기본값 데이터를 하나 내려주고 시작한다. (아직 데이터가 생성되지 않았을 때)
  • 데이터가 생성될 때마다 계속 내려보내줌.
  • 누군가 새롭게 중간에서 subscribe 하면 가장 최근에 발생했던 그 값을 미리 내려주고 나서 그 다음부터 발생하는 데이터를 똑같이 모든 구독하는 애들한테 전달해줌.
lazy var menuObservable = BehaviorSubject<[Menu]>(value: [])

// BehaviorSubject는 초기값을 가진다.
// viewmodel이 생성되는 시점에 onNext(menus)로 넘어오는 menus 어레이가 넣어진다.
// 나중에 tableview가 subscribe했을 때 가장 최근에 들어온 menus 어레이를 그대로 받아옴.

ex) menuObservable 같은 subject는 UI와 연결되어있음. 에러가 난다고 해서 스트림이 끊어지면 UI에서도 다 끊어지기 때문에 끊어지면 안됨.

 

 

4. ReplaySubject

  • Subscribe 했을때 데이터를 순서대로 내려줌. (여기까지 PublishSubject와 비슷함)
  • 두번째로 subscribe하는 애가 있으면 여태까지 발생했던 모든 데이터를 한꺼번에 다 내려줌.
  • 그 이후에 발생하는 것은 같이 받을 수 있음.
  • 여태까지 전달받았던 것을 한꺼번에 받고 시작.

 

RxCocoa

  • RxSwift의 요소들을 UIKit에 extension한 형태
  • UILabel에 .rx라는 것을 제공해줌. Binder 타입으로 rx.text라는 것을 가질 수 있음. (bind하면 Subscribe하지 않고 처리 가능)
  •  UI 작업의 특징
    • 항상 메인 스레드에서 돌아야함. (observeOn 사용)
    • UI 처리 도중 에러가 발생하면 작업이 끝나게 되고 해당 스트림은 재사용할 수 없다.
    • 에러가 나더라도 스트림이 끊어지지 않도록 처리해야 함. ex) .catchErrorJustReturn("")
// UI에 대해서는 아래 두 줄의 작업이 항상 필요함.

.catchErrorJustReturn("")
.observeOn(MainScheduler.instance)


viewModel.itemsCount
    .map { "\($0)" }
    .catchErrorJustReturn("") //에러가 나면 그냥 빈 문자열로 내림 (에러가 났을 경우, UI Stream 끊어지는 것을 방지하기 위해서)
    .observeOn(MainScheduler.instance)
    .bind(to: itemCountLabel.rx.text) // bind를 하면 순환참조 없이 사용 가능. 즉, 순환참조 없이 들어온 데이터 값을 그대로 전달해줌.
	.disposed(by: disposeBag)




// 그러나 항상 이렇게 처리하기 귀찮음. 그래서 나온게 -> Driver

 

 

  •  Observable / Driver : UI용으로는 driver로 제공
    • Driver는 항상 메인 스레드에서 작동함. (observeOn 사용하지 않아도 됨.)
    • 에러가 나는 경우에 대해 onErrorJustReturn으로 바꿔서 처리. ex) .asDriver(onErrorJustReturn: "")
// subscribe나 bind가 아닌 driver에 대해서는 drive를 사용.
// driver로 바뀌면 이 것은 에러가 나는 경우에 대해서 onErrorJustReturn로 바꿔서 처리.

viewModel.itemsCount
      .map { "\($0)" }
      .asDriver(onErrorJustReturn: "")
      .drive(itemCountLabel.rx.text)
      .disposed(by: disposeBag)

 

 

  •  Subject / Relay: UI용으로는 Relay로 제공
    • BehaviorRelay
      • 스트림이 끊어지지 않는 Subject
      • BehaviorRelay는 Subject와 같지만 에러가 나도 끊어지지 않는다. (에러 무시)
      • next, error, complete도 호출되지 않음. (complete되거나 error가 나오지 않기 때문에)
      • 오로지 받을 수 밖에 없기 때문에 onNext() 대신 accept()를 사용한다.
// MARK: - MenuListViewModel - 

lazy var menuObservable = BehaviorRelay<[Menu]>(value: [])

// MARK: - MenuViewController -

func changeCount(item: Menu, increase: Int) {
    _ = menuObservable
        .map { menus in
            menus.map { m in
                if (m.id == item.id) {
                    return Menu(id: m.id,
                                name: m.name,
                                price: m.price,
                                count: max(m.count + increase, 0)) // 카운트 '-'로 못내려가게 처리
                } else {
                    return Menu(id: m.id,
                                name: m.name,
                                price: m.price,
                                count: m.count)
                }
            } //새로운 Menu가 만들어져서 리턴됨
        }
        .take(1) //한번만 수행하고 observable을 끝낼 것이다라는 의미. disposable 처리 할 필요 없어짐.
        .subscribe(onNext: {
            self.menuObservable.accept($0)
        })
}

 

 

 

 

 

[4교시] RxSwift 를 활용한 아키텍쳐 - 프로젝트에 MVVM 적용하기

  1. MVC
    • Input을 Controller가 IBAction의 형태로 받는다. 결과물에 대한 출력도 Controller에서 한다.
    • UIKit에 종속되는 View, Controller와 달리 Model은 특정 플랫폼에 종속되지 않으므로 테스트가 가능하다.
  2. MVP
    • Controller의 역할을 제한하고자 ViewController를 View쪽으로 넘기고, ViewController가 가지고 있떤 로직 부분만 따로 떼어 Presenter로 뺀다. (화면에 그려져야 될 요소에 대한 로직을 Presenter로 옮김.)
    • View에 Input이 들어오면 Presenter에게 물어본다. Presenter는 어떠한 처리를 진행해서 이러한 내용들(눈에 보여져야될 모든 요소들)을 그리라고 View에게 시킴.
    • View와 Presenter는 1:1 관계이다
    • 모든 판단과 로직은 View가 하지 않고 Presenter가 함.
  3. MVVM
    1. MVP 패턴에서 View와 Presenter의 쌍이 여러개 만들어져야 하는 문제점 발생. 그냥 Presenter를 공통으로 하나 쓰이면 되는거 아닌가 하는 아이디어가 나옴.
    2. ViewModel이 화면에 무엇을 그리라고 지시하지 않음. View -> ViewModel 단방향 형태
    3. View가 ViewModel을 바라보면서 그 값이 바뀌면 View 스스로 갱신한다.

 



반응형

'RxSwift' 카테고리의 다른 글

RxSwift in 4 hours 정리(1)  (0) 2021.03.01
  1. [2교시] RxSwift 활용하기 - 쓰레드의 활용과 메모리 관리
  2. [3교시] RxSwift 활용범위 넓히기 - UI 컴포넌트와의 연동
  3. Subject
  4. RxCocoa
  5. [4교시] RxSwift 를 활용한 아키텍쳐 - 프로젝트에 MVVM 적용하기
'RxSwift' 카테고리의 다른 글
  • RxSwift in 4 hours 정리(1)
빵판 AKA 브레드보드
빵판 AKA 브레드보드
반응형
빵판 AKA 브레드보드
BreadBoard's devlog
빵판 AKA 브레드보드
전체
오늘
어제
  • 분류 전체보기 (49)
    • iOS (8)
    • Swift (1)
    • RxSwift (2)
    • Algorithm (29)
      • Swift (7)
      • Python (9)
      • Java (12)
    • Error 정리 (3)
    • ETC (2)
    • CS (0)
    • Spring (1)
      • 일반 (0)
      • Spring Security (1)
    • Infra (0)
      • CI CD (0)
    • Project (2)
      • Vinyler (1)
      • BinBean (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 알고리즘
  • DFS
  • Apple Business Manager
  • github
  • 편입
  • RxSwift
  • uicollectionview
  • 백준
  • AWS
  • Dijkstra
  • framework
  • CocoaPods
  • 다익스트라
  • BFS
  • 최단경로
  • TestFlight
  • 컴퓨터공학과
  • IOS
  • SwiftPackageManager
  • S3
  • Apple Enterprise Program
  • 코딩테스트
  • xcode
  • 개발자
  • AutoLayout
  • greedy알고리즘
  • SPM

최근 댓글

최근 글

hELLO · Designed By 정상우.v4.2.1
빵판 AKA 브레드보드
RxSwift in 4 hours 정리(2)
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.