티스토리 뷰

iOS

[Combine] Chapter11 : Timers

Peppo 2023. 1. 8. 01:03
728x90

Timers

 

타이머를 구현하려면 아래와 같은 방법이 있습니다.

  • RunLoop
  • Timer class
  • DispatchQueue

RunLoop

NOTE 

Apple 공식문서에서 RunLoop 클래스는 thread safe 하지 않아, 현재 thread에서만 RunLoop 메서드를 호출해야 한다고 합니다.

아래는 RunLoop를 활용해 1초마다 print를 하는 timer를 만든 예제 입니다.

import Foundation

let runLoop = RunLoop.main

let subscription = runLoop.schedule(
    after: runLoop.now,
    interval: .seconds(1),
    tolerance: .milliseconds(100)) {
        print("Timer fired")
    }​


위 코드는 1초마다 "Timer fired" 가 프린트 되며,
아래처럼 Cancellable을 이용해 타이머를 취소시킬 수도 있습니다.

import Foundation

let runLoop = RunLoop.main

let subscription = runLoop.schedule(
    after: runLoop.now,
    interval: .seconds(1),
    tolerance: .milliseconds(100)) {
        print("Timer fired")
    }
// 추가
runLoop.schedule(after: .init(Date(timeIntervalSinceNow: 3.0))) {
    subscription.cancel()
}​

 



 

 

 

Timer

timer publisher는 아래와 같이 만들 수 있습니다.

let main = Timer.publish(every: 1.0, on: .main, in: .common)

두 가지 매개변수 (on, in) 역할

On: 어떤 RunLoop에서 타이머를 사용할지, (위 예제에선 main thread)
In: 어떤 방식으로 타이머 Runloop를 실행할 건지, (여기선 기본값 common)

NOTE
Runloop
의 동작방식을 모른다면 그냥 기본값을 사용하길 권장 합니다.

아래처럼 'on 파라미터'에 current를 사용해,  모든 thread에 대해 RunLoop를 얻을 수 있다는데.. 
안정성을 위해선 RunLoop.main을 사용하라는 말이 있음.. 

let current = Timer.publish(every: 1.0, on: .current, in: .common)​


필수!
타이머가 반환하는 Publisher는 ConnectablePublisher 인데,
이 Publisher를 구독하여 실행하려면 connect() 메서드를 호출해야합니다.
첫 subscriber가 구독할때, 자동으로 연결시켜주는 자매품 autoconnect() 메서드도 있어요!​

따라서, 구독 시 타이머를 시작할 publisher를 만드는 방법은 아래처럼 작성하면 됩니다.

let publisher = Timer
    .publish(every: 1.0, on: .main, in: .common)
    .autoconnect()​

아래 Timer는 현재 날짜를 반복적으로 방출하고, Publisher.Output 타입은 Date 입니다.
scan 연산자를 사용해 증가값을 내보내는 타이머를 만듭니다.

let subscription = Timer
    .publish(every: 1.0, on: .main, in: .common)
    .autoconnect()
    // 이전 값에 +1을 함 (reduce는 최종값만 나타내지만, scan은 지속적으로 값을 보여줌)
    .scan(0) { counter, _ in
        counter + 1
    }
    .sink { counter in
        print("Counter is \(counter)")
    }
    
// Counter is 1
// Counter is 2
// Counter is 3
// Counter is 4
// .....


 

 

 

 

DispatchQueue

DispatchQueue를 사용하여 타이머를 생성할 수 있습니다.

아래는 DispatchQueue를 이용해 1초마다 이벤트를 방출하는 코드입니다.
import Foundation
import Combine

let queue = DispatchQueue.main

// 1
let source = PassthroughSubject<Int, Never>()

// 2
var counter = 0

// 3
let cancellable = queue.schedule(
    after: queue.now,
    interval: .seconds(1)
) {
    source.send(counter)
    counter += 1
}

// 4
let subscription = source.sink {
    print("Timer emitted \($0)")
}​
1. 타이머 값을 보낼 Subject를 만듭니다. 
2. 타이머가 실행 될때마다 카운터를 증가시킵니다.
3. 1초마다 queue에 action을 (counter + 1, source에 counter 등록) 저장해놓습니다.
4. 타이머 값을 얻기 위해 subject를 구독합니다. 


 

 

728x90

'iOS' 카테고리의 다른 글

[Combine] Chapter13 : Resource Management  (0) 2023.01.19
[Combine] Chapter12 : Key-Value Observing (KVO)  (0) 2023.01.15
[Combine] Chapter10 : Debugging  (0) 2023.01.03
UIView vs CALayer  (0) 2022.12.28
[Combine] Chapter6 : Time Manipulation Operators  (0) 2022.11.24