티스토리 뷰
Shifting time
publisher에서 방출하는 이벤트를 지연시키는 연산자를 알아보겠습니다.
delay(for:tolerance:scheduler:options)
upstream 에서 값을 방출할 때마다 잠시 동안 지연시켜 다음 지정한 스케줄러에서 방출합니다.
import Combine import SwiftUI import PlaygroundSupport // 1 let valuesPerSecond = 1.0 let delayInSeconds = 1.5 // 2 let sourcePublisher = PassthroughSubject<Date, Never>() // 3 let delayedPublisher = sourcePublisher.delay(for: .seconds(delayInSeconds), scheduler: DispatchQueue.main) // 4 let subscription = Timer .publish(every: 1.0 / valuesPerSecond, on: .main, in: .common) .autoconnect() .subscribe(sourcePublisher) // 5 let sourceTimeline = TimelineView(title: "Emitted values (\(valuesPerSecond) per sec.):") // 6 let delayedTimeline = TimelineView(title: "Delayed values (with a \(delayInSeconds)s delay):") let view = VStack(spacing: 50) { sourceTimeline delayedTimeline } // 7 PlaygroundPage.current.liveView = UIHostingController(rootView: view.frame(width: 375, height: 600)) sourcePublisher.displayEvents(in: sourceTimeline) delayedPublisher.displayEvents(in: delayedTimeline)
1. 매 1초마다 값을 방출하는 publisher를 만들고, 1.5초 후 방출하는 걸 만들고 비교
2. 날짜를 받는 Subject를 만들고
3. delay 연산자를 이용해 sourcePublisher를 1.5초 지연시킵니다.
4. 초당 1개의 값을 방출하는 타이머를 만들고, autoconnect()로 바로 시작, 구독을 합니다.
5 ~ 7. 대충 View 만들기
Collecting values
collect()
지정된 시간마다 값을 방출합니다.
import Combine import SwiftUI import PlaygroundSupport let valuesPerSecond = 1.0 let collectTimeStride = 4 let sourcePublisher = PassthroughSubject<Date, Never>() let collectedPublisher = sourcePublisher .collect(.byTime(DispatchQueue.main, .seconds(collectTimeStride))) let subscription = Timer .publish(every: 1.0 / valuesPerSecond, on: .main, in: .common) .autoconnect() .subscribe(sourcePublisher) let sourceTimeline = TimelineView(title: "Emitted values:") let collectedTimeline = TimelineView(title: "Collected values (every \(collectTimeStride)s):") let view = VStack(spacing: 40) { sourceTimeline collectedTimeline } PlaygroundPage.current.liveView = UIHostingController(rootView: view.frame(width: 375, height: 600)) sourcePublisher.displayEvents(in: sourceTimeline) collectedPublisher.displayEvents(in: collectedTimeline)
1. 4초마다 값을 구독하기 위해 collect() 연산자를 사용합니다.
+ 추가로 flatMap을 이용해서 방출됐던 값들을 마지막 4초마다 보여줄 수 있습니다.let collectedPublisher = sourcePublisher .collect(.byTime(DispatchQueue.main, .seconds(collectTimeStride))) .flatMap { dates in dates.publisher } // +추가
Collecting values(2)
collect(_:options:)
최대치로 설정해둔 갯수만 collect 합니다.
import Combine import SwiftUI import PlaygroundSupport let valuesPerSecond = 1.0 let collectTimeStride = 4 // 추가 let collectMaxCount = 2 let sourcePublisher = PassthroughSubject<Date, Never>() let collectedPublisher = sourcePublisher .collect(.byTime(DispatchQueue.main, .seconds(collectTimeStride))) .flatMap { dates in dates.publisher } // 추가 let collectedPublisher2 = sourcePublisher .collect(.byTimeOrCount(DispatchQueue.main, .seconds(collectTimeStride), collectMaxCount)) // 최대치 설정 .flatMap { dates in dates.publisher} let subscription = Timer .publish(every: 1.0 / valuesPerSecond, on: .main, in: .common) .autoconnect() .subscribe(sourcePublisher) let sourceTimeline = TimelineView(title: "Emitted values:") let collectedTimeline = TimelineView(title: "Collected values (every \(collectTimeStride)s):") // 추가 let collectedTimeline2 = TimelineView(title: "Collected values (at most \(collectMaxCount) every \(collectTimeStride)s):") let view = VStack(spacing: 40) { sourceTimeline collectedTimeline // 추가 collectedTimeline2 } PlaygroundPage.current.liveView = UIHostingController(rootView: view.frame(width: 375, height: 600)) sourcePublisher.displayEvents(in: sourceTimeline) collectedPublisher.displayEvents(in: collectedTimeline) // 추가 collectedPublisher2.displayEvents(in: collectedTimeline2)
최대치로 설정해둔 2개의 값만 collect 합니다.
Holding off on events
debounce(for:scheduler:)
subject에서 방출되는 값중 , for에 지정된 시간동안 입력되었던 '마지막' 값들을 한꺼번에 방출합니다.
import Combine import SwiftUI import PlaygroundSupport let subject = PassthroughSubject<String, Never>() // 1 let debounced = subject .debounce(for: .seconds(1.0), scheduler: DispatchQueue.main) // 2 .share() let subjectTimeline = TimelineView(title: "Emitted values") let debouncedTimeline = TimelineView(title: "Debounced values") let view = VStack(spacing: 100) { subjectTimeline debouncedTimeline } PlaygroundPage.current.liveView = UIHostingController(rootView: view.frame(width: 375, height: 600)) subject.displayEvents(in: subjectTimeline) debounced.displayEvents(in: debouncedTimeline) let subscription1 = subject .sink { string in print("+\(deltaTime)s: Subject emitted: \(string)") } let subscription2 = debounced .sink { string in print("+\(deltaTime)s: Debounced emitted: \(string)") } subject.feed(with: typingHelloWorld)
1. debounce를 사용해서 for에 지정된 시간동안 입력됐던 값들을 방출합니다.
2. 여러 번 subscribe할때 동일한 결과값을 보장하기 위해 share() 를 사용합니다.
Throttle(for:scheduler:latest:)
subject에 방출되었던 값중, for에 지정된 시간동안 입력된 가장 '첫번째' 또는 '최근(latest)' 값을 방출합니다.
첫번째 값 방출시 (latest: false)
import Combine import SwiftUI import PlaygroundSupport let throttleDelay = 1.0 let subject = PassthroughSubject<String, Never>() let throttled = subject .throttle(for: .seconds(throttleDelay), scheduler: DispatchQueue.main, latest: false) .share() let subjectTimeline = TimelineView(title: "Emitted values") let throttledTimeline = TimelineView(title: "Throttled values") let view = VStack(spacing: 100) { subjectTimeline throttledTimeline } PlaygroundPage.current.liveView = UIHostingController(rootView: view.frame(width: 375, height: 600)) subject.displayEvents(in: subjectTimeline) throttled.displayEvents(in: throttledTimeline) let subscription1 = subject .sink { string in print("+\(deltaTime)s: Subject emitted: \(string)") } let subscription2 = throttled .sink { string in print("+\(deltaTime)s: Throttled emitted: \(string)") } subject.feed(with: typingHelloWorld)
최근 값 방출시 (latest: true)let throttled = subject .throttle(for: .seconds(throttleDelay), scheduler: DispatchQueue.main, latest: true) .share()
Timing out
timeout(_:scheduler:options:customError:)
지정된 시간동안 이벤트 방출이없으면 complete 시킵니다.
(네트워크 응답없을시 error처리로 사용해도 될듯)
import Combine import SwiftUI import PlaygroundSupport let subject = PassthroughSubject<Void, Never>() // 1 let timedOutSubject = subject.timeout(.seconds(5), scheduler: DispatchQueue.main) let timeline = TimelineView(title: "Button taps") let view = VStack(spacing: 100) { Button(action: { subject.send() }) { Text("Press me within 5 seconds") } timeline } PlaygroundPage.current.liveView = UIHostingController(rootView: view.frame(width: 375, height: 600)) timedOutSubject.displayEvents(in: timeline)
1. 5초동안 이벤트 방출이 없으면 time-out (complete 처리)
지정된 시간동안 이벤트 방출이 없으면 '에러처리'import Combine import SwiftUI import PlaygroundSupport // 1 enum TimeoutError: Error { case timeout } let subject = PassthroughSubject<Void, TimeoutError>() // 2 let timedOutSubject = subject.timeout(.seconds(5), scheduler: DispatchQueue.main, customError: { .timeout }) // 3 let timeline = TimelineView(title: "Button taps") let view = VStack(spacing: 100) { Button(action: { subject.send() }) { Text("Press me within 5 seconds") } timeline } PlaygroundPage.current.liveView = UIHostingController(rootView: view.frame(width: 375, height: 600)) timedOutSubject.displayEvents(in: timeline)
1. CustomError 추가 (enum)
2. TimeoutError 리턴을 하는 상수 subject
3. timedOutSubject에서 에러가 발생할시 TimeoutError의 어떤 case를 리턴할지 정함.
Measuring time
measureInterval(using:)
value 사이의 시간을 계산하고 싶을 때 사용
- DispatchQueue를 사용할 경우: 나노초 단위 DispatchTimelnterval로 리턴됨
- Runloop를 사용할 경우: 초단위로 리턴됨
import Combine import SwiftUI import PlaygroundSupport 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 view = VStack(spacing: 100) { subjectTimeline measureTimeline } PlaygroundPage.current.liveView = UIHostingController(rootView: view.frame(width: 375, height: 600)) subject.displayEvents(in: subjectTimeline) measureSubject.displayEvents(in: measureTimeline) 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)") } let measureSubject2 = subject.measureInterval(using: RunLoop.main) let subscription3 = measureSubject2.sink { print("+\(deltaTime)s: Measure2 emitted: \($0)") } subject.feed(with: typingHelloWorld) let measureSubject2 = subject.measureInterval(using: RunLoop.main) let subscription3 = measureSubject2.sink { print("+\(deltaTime)s: Measure2 emitted: \($0)") }
다른건 유용하게 사용할 수 있을것 같은데 measureInterval(using:)의 경우는 아직 어떤상황에서 사용해야할지 잘 모르겠네요..
추후 더 겪어보고 사용하게 된다면 해당글 업데이트 하겠습니다 :)
'iOS' 카테고리의 다른 글
[Combine] Chapter10 : Debugging (0) | 2023.01.03 |
---|---|
UIView vs CALayer (0) | 2022.12.28 |
[Swift 알고리즘] - 문자열 내림차순으로 배치하기 (Programmers) (0) | 2022.11.15 |
[Combine] Chapter5: Combining Operators (0) | 2022.11.10 |
[Swift 알고리즘] - 자연수 뒤집어 배열로 만들기 (Programmers) (0) | 2022.11.05 |
- Total
- Today
- Yesterday
- swift reduce
- Swift 알고리즘
- Swift joined
- Swift
- swift (programmers)
- Swift final
- Swift Leetcode
- Swift 프로그래머스
- RIBs tutorial
- Swift joined()
- Swift inout
- Swift 프로퍼티
- CS 네트워크
- Swift Error Handling
- swift property
- swift 고차함수
- iOS error
- Swift init
- Class
- Swift RIBs
- 2023년 회고
- Combine: Asynchronous Programming with Swift
- swift protocol
- ios
- RTCCameraVideoCapturer
- removeLast()
- Swift 내림차순
- swift programmers
- 원티드 프리온보딩
- Swift ModernRIBs
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |