Created
March 13, 2025 09:21
-
-
Save dreymonde/7ed2c6563bdba57e4dcf9309e7737196 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import SwiftUI | |
| import Combine | |
| // MARK: - onTimer | |
| extension View { | |
| /// Adds an action to perform at specified intervals. | |
| /// | |
| /// - Parameters: | |
| /// - interval: The time interval on which to publish events. For example, a value of `0.5` publishes an event approximately every half-second. | |
| /// - tolerance: The allowed timing variance when emitting events. Defaults to `nil`, which allows any variance. | |
| /// - cancelTrigger: A value to monitor for changes to determine when to cancel `Timer` | |
| /// - perform: A closure that is executed with the current date each time the timer fires. | |
| func onTimer( | |
| every interval: TimeInterval, | |
| tolerance: TimeInterval? = nil, | |
| cancelTrigger: AnyHashable? = nil, | |
| perform: @escaping (_ date: Date) -> Void | |
| ) -> some View { | |
| self.modifier( | |
| TimerModifier( | |
| every: interval, | |
| tolerance: tolerance, | |
| cancelTrigger: cancelTrigger, | |
| perform: { date in perform(date) } | |
| ) | |
| ) | |
| } | |
| /// Adds an action to perform at specified intervals. | |
| /// | |
| /// - Parameters: | |
| /// - interval: The time interval on which to publish events. For example, a value of `0.5` publishes an event approximately every half-second. | |
| /// - tolerance: The allowed timing variance when emitting events. Defaults to `nil`, which allows any variance. | |
| /// - cancelTrigger: A value to monitor for changes to determine when to cancel `Timer` | |
| /// - perform: A closure that is executed each time the timer fires. | |
| func onTimer( | |
| every interval: TimeInterval, | |
| tolerance: TimeInterval? = nil, | |
| cancelTrigger: AnyHashable? = nil, | |
| perform: @escaping () -> Void | |
| ) -> some View { | |
| self.modifier( | |
| TimerModifier( | |
| every: interval, | |
| tolerance: tolerance, | |
| cancelTrigger: cancelTrigger, | |
| perform: { _ in perform() } | |
| ) | |
| ) | |
| } | |
| } | |
| // MARK: - TimerModifier | |
| /// Use via `.onTimer` modifiers | |
| struct TimerModifier: ViewModifier { | |
| @State private var timer: Publishers.Autoconnect<Timer.TimerPublisher> | |
| let cancelTrigger: AnyHashable? | |
| private let perform: (Date) -> Void | |
| init( | |
| every interval: TimeInterval, | |
| tolerance: TimeInterval?, | |
| cancelTrigger: AnyHashable?, | |
| perform: @escaping (Date) -> Void | |
| ) { | |
| self.timer = Timer.publish(every: interval, tolerance: tolerance, on: .main, in: .common) | |
| .autoconnect() | |
| self.cancelTrigger = cancelTrigger | |
| self.perform = perform | |
| } | |
| func body(content: Content) -> some View { | |
| content | |
| .onReceive(timer) { date in | |
| self.perform(date) | |
| } | |
| .onChange(of: cancelTrigger) { | |
| self.timer.upstream.connect().cancel() | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I really appreciate how your article dives into the concept of custom ViewModifiers and showcases the composability of SwiftUI. For the specific use case of updating a view on a schedule, though, I’d recommend considering TimelineView, which is designed for exactly this scenario.