Skip to content

Instantly share code, notes, and snippets.

@haikieu
Last active February 26, 2024 11:36
Show Gist options
  • Save haikieu/c9aa3a519835dd7ee8626498295e777a to your computer and use it in GitHub Desktop.
Save haikieu/c9aa3a519835dd7ee8626498295e777a to your computer and use it in GitHub Desktop.
Best solution to enhance timer in SwiftUI
//
// SwiftUITimer.swift
//
//
// Created by Hai Kieu on 7/13/23.
// https://medium.com/@programmingpassion/best-solution-to-enhance-timer-in-swiftui-501096572139
import SwiftUI
import Combine
extension View {
public func onTimerFired(_ handler: Binding<Cancellable?>, every: TimeInterval, repeat: Bool, onCallback callback: @escaping ((Cancellable)->Void)) -> some View {
self.modifier(TimerModifier(handler, every: every, repeat: `repeat`, onCallback: callback))
}
}
struct TimerModifier: ViewModifier {
// Declare an idle timer
@State private var timer: Timer.TimerPublisher
// A handler to help cancel a timer later
@Binding private var handler: Cancellable?
private let `repeat`: Bool
private let every: TimeInterval
private let callback: ((Cancellable)->Void)
init(_ handler: Binding<Cancellable?>, every: TimeInterval, repeat: Bool, onCallback callback: @escaping ((Cancellable)->Void) ) {
self.callback = callback
self.every = every
self.`repeat` = `repeat`
let publisher = Timer.publish(every: every, on: .main, in: .default)
_timer = .init(initialValue: publisher)
_handler = handler
}
public func body(content: Content) -> some View {
ZStack {
content
}.onAppear(perform: {
setTimer()
}).onReceive(timer) { timer in
if !`repeat` { cancelTimer() }
callback(handler)
}
}
private func setTimer() {
handler?.cancel()
timer = Timer.publish(every: every, on: .main, in: .default)
handler = timer.connect()
}
private func cancelTimer() {
handler?.cancel()
}
}
struct ConsumerDemoView: View {
@State private var timerHandler: Cancellable?
@State private var displayText: String = "Hello, world!"
@State private var count: Int = 0
var body: some View {
VStack {
Text(displayText)
.padding()
}.onTimerFired($timerHandler, every: 10, repeat: true) { timerHandler in
count += 1
displayText = "Timer fired \(count) times"
}
}
}
@wilwet
Copy link

wilwet commented Feb 26, 2024

Changed: callback(handler)
to
if let handler {
callback( handler )
}

To prevent error: Value of optional type '(any Cancellable)?' must be unwrapped to a value of type 'any Cancellable'

@wilwet
Copy link

wilwet commented Feb 26, 2024

can you add a interval modifier? Tried but the code but it don't really understand it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment