일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- UIKit
- arkit
- swift
- iphone
- withAnimation
- 네트워크
- ios
- firebase
- combine
- avsession
- SwiftUI
- state
- dataflow
- 접근성제어
- GCD
- authentication
- CS
- view
- stateobject
- 달력
- 최적화
- toolbarvisibility
- Network
- Animation
- Concurrency
- RxSwift
- auth
- gesture
- WWDC
- Performance
- Today
- Total
XLOG
[UIKit] RxSwift 이론 + 적용 본문
1. Observable
- Observable = Observable sequence = seqquenece
- 비동기
- 이벤트를 만들고, emitting 한다.
- 관찰 가능한 함수형태의 연산자
- next 를 통해 이벤트를 방출
- completed 이벤트를 통해 종료
- error 가 발생되더라도 종료된다.
// just: 하나의 요소를 방출하는 Observable sequence 를 생성
let just = Observable<Int>.just(1)
// of: 여러 요소를 순서대로 방출하는 Observable sequence 를 생성
let of = Observable<Int>.of(1,2,3,4,5)
// from: array 를 취하며 array 내부 요소를 하나씩 방출하는 시퀀스를 생성
let from = Observable.from([1, 2, 3, 4, 5])
// create: 클로져 형식이며 다양한 값(onNext, onError...)을 생성
let create = Observable<String>.create({ observer -> Disposable in
observer.onNext("1")
observer.onNext("2")
observer.onCompleted()
observer.onNext("3")
return Disposables.create()
})
이 외에 empty(), never(), range(start: , end:)
2. Subscribe
- Observer에 대한 구독이다
- 옵저버에 담긴 이벤트들을 방출하는 것
- 이벤트 객체를 파라미터로 하는 클로져 형태이며 반환값은 Disposable

just.subscribe { event in
print(event)
}
/*
next(1)
completed
*/
let _ = create.subscribe({
print($0)
})
/*
next(1)
next(2)
completed
*/
let _ = create.subscribe({
print($0.element)
})
/*
Optional("1")
Optional("2")
nil
*/
3. Disposing
subscribe 가 Observables 의 이벤트를 emit 하는 것이라면, disposing 은 subscribe 를 취소하는 것, Disposing 을 해주지 않게 되면 메모리 이슈를 발생시킬 수 있다. 구독이 취소되지 않아 이벤트가 발생하지 않더라도 계속 변화를 감시하고 있기 때문에..
4. Subject
- Observable 과 Observer 기능을 둘 다 하는 것이 바로 Subjects!!
- 즉 Data를 Control( onNext, onError )
- PublishSubject
- 구독 후 벌어진 이벤트만 emit

let subject = PublishSubject<String>()
subject.onNext("0")
let subscirbe = subject.subscribe(onNext: {
print($0)
})
subject.on(.next("1"))
subject.onNext("2")
/*
1
2
*/
- ReplaySubject
- 구독 시점 이전에 발생한 이벤트들은 버퍼사이즈만큼 구독 시점부터 받는다.

let subject = ReplaySubject<String>.create(bufferSize: 3)
subject.onNext("0")
subject.onNext("10")
subject.onNext("20")
subject.onNext("30")
let subscirbe = subject.subscribe(onNext: {
print($0)
})
subject.on(.next("1"))
subject.onNext("2")
/*
10
20
30
1
2
*/
- BehaviorSubject
- 구독 시점 바로 이전에 발생한 event 부터 emit

let subject = BehaviorSubject(value: "start")
let subscribe1 = subject.subscribe(onNext: {
print("subscribe1: \($0)")
})
subject.onNext("0")
subject.onNext("10")
subject.onNext("20")
subject.onNext("30")
let subscirbe2 = subject.subscribe(onNext: {
print("subscribe2: \($0)")
})
subject.on(.next("1"))
subject.onNext("2")
/*
subscribe1: start
subscribe1: 0
subscribe1: 10
subscribe1: 20
subscribe1: 30
subscribe2: 30
subscribe1: 1
subscribe2: 1
subscribe1: 2
subscribe2: 2
*/
- AsyncSubject
- 구독 시점과 상관없이 Observable 이 complete 되는 시점에 가장 최근 발생한 이벤트만을 emit 한다.

let subject = AsyncSubject<String>()
let subscribe1 = subject.subscribe(onNext: {
print("subscribe1: \($0)")
})
subject.onNext("0")
subject.onNext("10")
subject.onNext("20")
subject.onNext("30")
let subscirbe2 = subject.subscribe(onNext: {
print("subscribe2: \($0)")
})
subject.on(.next("1"))
subject.onNext("2")
subject.onCompleted()
/*
subscribe1: 2
subscribe2: 2
*/
5. Observable vs Subject
위에서 Subject 는 Observable, Observer 의 두가지 기능을 가지고 있다고 설명을 했지만 정확한 차이를 모르겠어서 찾아봤다.

Observable 은 위에서도 설명을 적었지만 함수이다. 따로 state 가 존재하지 않고, 그렇기에 매번 실행이 된다. 즉 각 View 에 데이터를 적용을 하기 위해선 Subject를 사용해야 한다. 같은 값을 바라보고 있어야 하기 때문이다.
6. Observable 의 Life Cycle
- Subscribed
- Next
- Completed / Error
- Disposed
6. 간단히 프로젝트에 적용
사용 프로젝트 : https://github.com/profit0124/TodoListWithUIKit/tree/feature/rxSwift
GitHub - profit0124/TodoListWithUIKit
Contribute to profit0124/TodoListWithUIKit development by creating an account on GitHub.
github.com
이전에 DiffableDataSource, CoreData 를 사용했던 TodoList 에 rxSwift를 간단하게 적용해봤다.
간단히 RxSwift의 원리를 생각해보면 Observable 을 Subscribe 하여, 해당 이벤트를 방출하면 이벤트를 받아서 자동으로 처리를 해주는 것이다. 그렇기에 기존에 ViewModel 에서 사용했던 [Todo] 를 Subject로 변경하여 값이 바뀔때마다, snapshot을 변경하여 datasource에 적용해주도록 변경하였다.
import UIKit
import RxSwift
import CoreData
final class TodosViewModel {
var todos: BehaviorSubject<[Todo]> = BehaviorSubject<[Todo]>(value: [])
private let disposeBag = DisposeBag()
var dataSource: UICollectionViewDiffableDataSource<CollectionViewSection, Todo>!
init(_ collectionView: UICollectionView) {
self.dataSource = .init(collectionView: collectionView) { (collectionView, indexPath, todo) -> UICollectionViewCell? in
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TodoCellViewCollectionViewCell.id, for: indexPath) as? TodoCellViewCollectionViewCell else { return UICollectionViewCell() }
cell.configuration(todo: todo, delegate: self)
return cell
}
bind()
fetchTodos()
}
func bind() {
todos.asObservable()
.subscribe(onNext: { [weak self] items in
guard let self = self else { return }
var snapShot = NSDiffableDataSourceSnapshot<CollectionViewSection, Todo>()
snapShot.appendSections([.main])
snapShot.appendItems(items.filter({ !$0.isCompleted }))
self.dataSource.apply(snapShot, animatingDifferences: true)
})
.disposed(by: disposeBag)
}
func fetchTodos() {
let request = Todo.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(keyPath: \Todo.deadline, ascending: false)]
do {
let todoItems = try PersistenceManager.shared.container.viewContext.fetch(request)
self.todos.onNext(todoItems)
} catch {
print(error)
}
}
func addTodo(_ todo: Todo) {
do {
var values = try todos.value()
values.append(todo)
self.todos.onNext(values)
} catch {
print(error)
}
}
...
}
이렇게 해주게 되면 todos의 이벤트를 방출하기만 하면 todos 의 변화가 있을 때마다 자동으로 snapShot을 업데이트 하여 CollectionView의 UI를 변경할 수 있다.
물론 DiffableDataSource가 아닌 RxDataSource를 사용할 수도 있다.
'Swift > UIKit' 카테고리의 다른 글
[UIKit] DiffableDataSource (2) | 2024.06.08 |
---|---|
[UIKit] Autolayout 이해하기 (4) | 2023.03.09 |
[UIKit] Layer 는 무엇일까? 왜 사용할까? (0) | 2023.03.01 |
원티드 프리온보딩 챌린지 iOS 2차과정 사전 과제 (2) | 2023.02.26 |
[UIKit] UIImage 효율적으로 사용하기 - 1 (0) | 2023.02.01 |