안녕하세요 코찐입니다.
이번 챕터는 Time manipulation(시간 조작)에 관한 챕터입니다.
아래의 자료를 따라서 공부하고 있습니다.
하나하나 따라하기 좋게 구성되어 있습니다.
https://www.raywenderlich.com/books/combine-asynchronous-programming-with-swift/v2.0
https://github.com/raywenderlich/comb-materials/tree/editions/2.0/06-time-based-operators/projects
플레이그라운드를 열어보면 직관적인 실습을 위해서 많은 준비를 해둔 것을 알 수 있습니다. (진짜 존경스러움...)
Shifting time
이번엔 SwiftUI 도 import 되어있습니다... 역동적인걸 보여주려고 하나봅니다
(이렇게 차근차근 설명해 나가는걸 Swift 기초 강의에도 적용해도 좋을 것 같다. 스스로 이해하는 속도에 맞춰서 따라갈 수 있는 듯)
let sourcePublisher = PassthroughSubject<Date, Never>()
let delayedPublisher = sourcePublisher.delay(for: .seconds(delayInSeconds), scheduler: DispatchQueue.main)
let subscription = Timer
.publish(every: 1.0 / valuesPerSecond, on: .main, in: .common)
.autoconnect() // value emit 전에 connect해야하는데, autoconnect라서 첫번째 subscirption에 connect
.subscribe(sourcePublisher)
1초마다 value를 생산하고, 1.5초 delay 한다.
동적으로 보면 이렇게 생겼다.
Collecting values
- 특정 기간 동안의 value를 모아줌
- ex) 특정 기간 동안의 평균을 구할 때 사용
let valuesPerSecond = 1.0
let collectTimeStride = 4
let sourcePublisher = PassthroughSubject<Date, Never>()
let collectedPublisher = sourcePublisher
.collect(.byTime(DispatchQueue.main, .seconds(collectTimeStride)))
.flatMap { dates in dates.publisher }
let subscription = Timer
.publish(every: 1.0 / valuesPerSecond, on: .main, in: .common)
.autoconnect()
.subscribe(sourcePublisher)
collect된 value들이 모여서 array로 만들어지는 것을 볼 수 있다.
그리고 여기에 예제 하나를 더 추가한다.
이번엔 limit로 maxCount를 넣어본다
let collectMaxCount = 2
let collectedPublisher2 = sourcePublisher
.collect(.byTimeOrCount(DispatchQueue.main, .seconds(collectTimeStride), collectMaxCount))
.flatMap { dates in dates.publisher }
비슷하게 해서 View를 연결하면 최대치로 설정해둔 2개만 collect되는 것을 볼 수 있다.
Holding off on events
- TextField에 타이핑 다 했을 때 다음 동작을 수행하고 싶을 때가 있음
- debounce, throttle: 이 두개가 맨날 헷갈리는데 잘 정리해보자.
- debounce: 입력 주기가 끝나면 출력
- throttle: 특정 주기안의 첫번째 or 마지막 value 출력
이 포스팅에서 잘 정리해주고 있으니, 필요하면 한 번 살펴보자.
debounce
- 입력 주기가 끝나면 마지막 값 출력
- 추가로 value가 들어오면 주기가 다시 갱신됨
let subject = PassthroughSubject<String, Never>()
let debounced = subject
.debounce(for: .seconds(1.0), scheduler: DispatchQueue.main)
.share() // 여러 subscriber가 동일한 result를 받기 위함
throttle
let throttleDelay = 1.0
let subject = PassthroughSubject<String, Never>()
let throttled = subject
.throttle(for: .seconds(throttleDelay), scheduler: DispatchQueue.main, latest: false)
.share()
- 처음에 구독한 시점에 value를 한번 바로 방출
- latest: false로 주면 특정 주기 안의 첫번째 value 출력
- 주기를 1초 간격으로 설정함
- 버튼 터치를 여러번 할 수 있는 경우에 사용하면 좋을 듯
이번에는 latest: true로 옵션을 설정해보자
그러면 특정 주기안의 마지막 value를 출력!
Timing out
특정 시간안에 이벤트가 없으면 종료
let subject = PassthroughSubject<Void, Never>()
let timedOutSubject = subject.timeout(.seconds(5), scheduler: DispatchQueue.main)
timeout 발생하면 finish 대신에 error를 던지는 경우가 더 흔한 케이스
enum TimeoutError: Error {
case timedOut
}
let subject = PassthroughSubject<Void, TimeoutError>()
let timedOutSubject = subject.timeout(
.seconds(5),
scheduler: DispatchQueue.main,
customError: { .timedOut })
Measuring time
value 사이의 시간을 계산하고 싶을 때 사용
- DispatchQueue를 사용할 경우: 나노초 단위 DispatchTimeInterval 로 리턴됨
- Runloop를 사용할 경우: 초단위로 리턴됨
let subject = PassthroughSubject<String, Never>()
let measureSubject = subject.measureInterval(using: DispatchQueue.main)
let subjectTimeline = TimelineView(title: "Emitted values")
let measureTimeline = TimelineView(title: "Measured values")
// ...중략...
let subscription1 = subject.sink {
print("+\(deltaTime)s: Subject emitted: \($0)")
}
let subscription2 = measureSubject.sink {
print("+\(deltaTime)s: Measure emitted: \(Double($0.magnitude) / 1_000_000_000.0)")
}
이번 챕터는 여기까지 입니다.
다음 챕터에서 만나요!
'iOS > Combine' 카테고리의 다른 글
[Combine 책 정리] Chapter 9: Networking (0) | 2021.01.27 |
---|---|
[Combine 책 정리] Chapter 7: Sequence Operators (0) | 2021.01.22 |
[Combine 책 정리] Chapter 5: Combining Operators (0) | 2021.01.18 |
[Combine 책 정리] Chapter 4: Filtering Operators (0) | 2021.01.13 |
[Combine 책 정리] Chatper 3: Transforming Operators (0) | 2021.01.12 |