From ef29db6d829cf43dd9fae5faf391d3c82ccdd06f Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Fri, 22 Nov 2024 18:59:53 +0100
Subject: [PATCH 01/45] 1
---
.../Internal/Helpers/MTimerCallbacks.swift | 14 ++
.../Helpers/MTimerConfigurationManager.swift | 58 +++++
.../Internal/Helpers/MTimerContainer.swift | 22 ++
.../Internal/Helpers/MTimerStateManager.swift | 25 +++
.../Internal/Helpers/MTimerValidator.swift | 22 ++
Sources/Internal/MTimer.swift | 200 ++++++++++--------
.../Protocols/FactoryInitializable.swift | 20 ++
Sources/Public/MTimerID.swift | 12 ++
Sources/Public/MTimerStatus.swift | 8 +
Sources/Public/Public+MTime.swift | 2 +-
Sources/Public/Public+MTimer.swift | 65 +++---
Tests/MTimeTests.swift | 8 +-
Tests/MTimerTests.swift | 95 ++++++---
13 files changed, 387 insertions(+), 164 deletions(-)
create mode 100644 Sources/Internal/Helpers/MTimerCallbacks.swift
create mode 100644 Sources/Internal/Helpers/MTimerConfigurationManager.swift
create mode 100644 Sources/Internal/Helpers/MTimerContainer.swift
create mode 100644 Sources/Internal/Helpers/MTimerStateManager.swift
create mode 100644 Sources/Internal/Helpers/MTimerValidator.swift
create mode 100644 Sources/Internal/Protocols/FactoryInitializable.swift
create mode 100644 Sources/Public/MTimerID.swift
create mode 100644 Sources/Public/MTimerStatus.swift
diff --git a/Sources/Internal/Helpers/MTimerCallbacks.swift b/Sources/Internal/Helpers/MTimerCallbacks.swift
new file mode 100644
index 0000000..555e556
--- /dev/null
+++ b/Sources/Internal/Helpers/MTimerCallbacks.swift
@@ -0,0 +1,14 @@
+//
+// MTimerCallbacks.swift
+// MijickTimer
+//
+// Created by Alina Petrovska on 11.11.2024.
+//
+
+import SwiftUI
+
+class MTimerCallbacks {
+ var onRunningTimeChange: ((MTime) -> ())?
+ var onTimerActivityChange: ((MTimerStatus) -> ())?
+ var onTimerProgressChange: ((Double) -> ())?
+}
diff --git a/Sources/Internal/Helpers/MTimerConfigurationManager.swift b/Sources/Internal/Helpers/MTimerConfigurationManager.swift
new file mode 100644
index 0000000..f08a065
--- /dev/null
+++ b/Sources/Internal/Helpers/MTimerConfigurationManager.swift
@@ -0,0 +1,58 @@
+//
+// MTimerConfigurationManager.swift
+// MijickTimer
+//
+// Created by Alina Petrovska on 11.11.2024.
+//
+
+import SwiftUI
+
+class MTimerConfigurationManager {
+ var initialTime: (start: TimeInterval, end: TimeInterval) = (0, 1)
+ var publisherTime: TimeInterval = 0
+ var publisherTimeTolerance: TimeInterval = 0.4
+ var runningTime: TimeInterval = 0
+}
+
+extension MTimerConfigurationManager {
+ func assignInitialStartValues(_ startTime: TimeInterval, _ endTime: TimeInterval) {
+ initialTime = (startTime, endTime)
+ runningTime = startTime
+ }
+ func assignInitialPublisherValues(_ time: TimeInterval, _ tolerance: TimeInterval) {
+ publisherTime = time
+ publisherTimeTolerance = tolerance
+ }
+ func resetRunningTime() {
+ runningTime = initialTime.start
+ }
+ func skipRunningTime() {
+ runningTime = initialTime.end
+ }
+ func getPublisherTime() -> TimeInterval {
+ publisherTime == 0 ? max(initialTime.start, initialTime.end) : publisherTime
+ }
+ func calculateNewRunningTime(_ timeChange: Any?) {
+ let timeChange = timeChange as? TimeInterval ?? publisherTime
+ let newRunningTime = runningTime + timeChange * timeIncrementMultiplier
+ runningTime = timeIncrementMultiplier == -1
+ ? max(newRunningTime, initialTime.end)
+ : min(newRunningTime, initialTime.end)
+ }
+ func calculateTimerProgress() -> Double {
+ let timerTotalTime = max(initialTime.start, initialTime.end) - min(initialTime.start, initialTime.end)
+ let timerRunningTime = abs(runningTime - initialTime.start)
+ return timerRunningTime / timerTotalTime
+ }
+ func reset() {
+ initialTime = (0, 1)
+ publisherTime = 0
+ publisherTimeTolerance = 0.4
+ runningTime = 0
+ }
+}
+
+extension MTimerConfigurationManager {
+ var canTimerBeStarted: Bool { runningTime != initialTime.end }
+ var timeIncrementMultiplier: Double { initialTime.start > initialTime.end ? -1 : 1 }
+}
diff --git a/Sources/Internal/Helpers/MTimerContainer.swift b/Sources/Internal/Helpers/MTimerContainer.swift
new file mode 100644
index 0000000..1d103fb
--- /dev/null
+++ b/Sources/Internal/Helpers/MTimerContainer.swift
@@ -0,0 +1,22 @@
+//
+// MTimerContainer.swift
+// MijickTimer
+//
+// Created by Alina Petrovska on 11.11.2024.
+//
+
+@MainActor class MTimerContainer {
+ private var timers: [MTimer] = []
+ static let shared = MTimerContainer()
+
+ private init() { }
+}
+
+extension MTimerContainer {
+ func getTimer(_ id: MTimerID) -> MTimer? { timers.first(where: { $0.id == id }) }
+ func register(_ timer: MTimer) { if getTimer(timer.id) == nil { timers.append(timer) }}
+}
+
+extension MTimerContainer {
+ func resetAll() { timers.forEach { $0.reset() }}
+}
diff --git a/Sources/Internal/Helpers/MTimerStateManager.swift b/Sources/Internal/Helpers/MTimerStateManager.swift
new file mode 100644
index 0000000..491f4f8
--- /dev/null
+++ b/Sources/Internal/Helpers/MTimerStateManager.swift
@@ -0,0 +1,25 @@
+//
+// MTimerStateManager.swift
+// MijickTimer
+//
+// Created by Alina Petrovska on 11.11.2024.
+//
+
+import SwiftUI
+
+class MTimerStateManager {
+ var internalTimer: Timer?
+ var backgroundTransitionDate: Date? = nil
+
+ deinit { internalTimer?.invalidate() }
+}
+
+extension MTimerStateManager {
+ func didEnterBackground() {
+ internalTimer?.invalidate()
+ backgroundTransitionDate = .init()
+ }
+ func willEnterForeground() {
+ backgroundTransitionDate = nil
+ }
+}
diff --git a/Sources/Internal/Helpers/MTimerValidator.swift b/Sources/Internal/Helpers/MTimerValidator.swift
new file mode 100644
index 0000000..cceb809
--- /dev/null
+++ b/Sources/Internal/Helpers/MTimerValidator.swift
@@ -0,0 +1,22 @@
+//
+// MTimerValidator.swift
+// MijickTimer
+//
+// Created by Alina Petrovska on 11.11.2024.
+//
+
+import Foundation
+
+class MTimerValidator {
+ func checkRequirementsForInitializingTimer(_ publisherTime: TimeInterval) throws {
+ if publisherTime < 0.001 { throw MTimer.Error.publisherTimeCannotBeLessThanOneMillisecond }
+ }
+ func checkRequirementsForStartingTimer(_ startTime: TimeInterval, _ endTime: TimeInterval, _ state: MTimerStateManager, _ status: MTimerStatus) throws {
+ if startTime < 0 || endTime < 0 { throw MTimer.Error.timeCannotBeLessThanZero }
+ if startTime == endTime { throw MTimer.Error.startTimeCannotBeTheSameAsEndTime }
+ if status == .inProgress && state.backgroundTransitionDate == nil { throw MTimer.Error.timerIsAlreadyRunning }
+ }
+ func checkRequirementsForResumingTimer(_ callbacks: MTimerCallbacks) throws {
+ if callbacks.onRunningTimeChange == nil { throw MTimer.Error.cannotResumeNotInitialisedTimer }
+ }
+}
diff --git a/Sources/Internal/MTimer.swift b/Sources/Internal/MTimer.swift
index ae43ad9..46d9479 100644
--- a/Sources/Internal/MTimer.swift
+++ b/Sources/Internal/MTimer.swift
@@ -1,166 +1,180 @@
//
-// MTimer.swift of Timer
+// MTimer.swift
+// MijickTimer
//
-// Created by Tomasz Kurylik
-// - Twitter: https://twitter.com/tkurylik
-// - Mail: tomasz.kurylik@mijick.com
-// - GitHub: https://github.com/FulcrumOne
+// Created by Alina Petrovska on 11.11.2024.
//
-// Copyright ©2023 Mijick. Licensed under MIT License.
-
import SwiftUI
-public final class MTimer {
- static let shared: MTimer = .init()
-
- // Current State
- var internalTimer: Timer?
- var isTimerRunning: Bool = false
- var runningTime: TimeInterval = 0
- var backgroundTransitionDate: Date? = nil
-
- // Configuration
- var initialTime: (start: TimeInterval, end: TimeInterval) = (0, 1)
- var publisherTime: TimeInterval = 0
- var publisherTimeTolerance: TimeInterval = 0.4
- var onRunningTimeChange: ((MTime) -> ())!
- var onTimerActivityChange: ((Bool) -> ())?
- var onTimerProgressChange: ((Double) -> ())?
-
- deinit { internalTimer?.invalidate() }
+public final class MTimer: ObservableObject, FactoryInitializable {
+ private let state = MTimerStateManager()
+ private let configuration = MTimerConfigurationManager()
+ private let validator = MTimerValidator()
+
+ let callbacks = MTimerCallbacks()
+ let id: MTimerID
+
+ @Published public private(set) var timerTime: MTime = .init()
+ @Published public private(set) var timerState: MTimerStatus = .notStarted
+ @Published public private(set) var timerProgress: Double = 0
+
+ init(identifier: MTimerID) { self.id = identifier }
}
-
// MARK: - Initialising Timer
extension MTimer {
- func checkRequirementsForInitialisingTimer(_ publisherTime: TimeInterval) throws {
- if publisherTime < 0.001 { throw Error.publisherTimeCannotBeLessThanOneMillisecond }
+ func checkRequirementsForInitializingTimer(_ publisherTime: TimeInterval) throws {
+ try validator.checkRequirementsForInitializingTimer(publisherTime)
}
func assignInitialPublisherValues(_ time: TimeInterval, _ tolerance: TimeInterval, _ completion: @escaping (MTime) -> ()) {
- publisherTime = time
- publisherTimeTolerance = tolerance
- onRunningTimeChange = completion
+ configuration.assignInitialPublisherValues(time, tolerance)
+ callbacks.onRunningTimeChange = completion
}
}
// MARK: - Starting Timer
extension MTimer {
func checkRequirementsForStartingTimer(_ startTime: TimeInterval, _ endTime: TimeInterval) throws {
- if startTime < 0 || endTime < 0 { throw Error.timeCannotBeLessThanZero }
- if startTime == endTime { throw Error.startTimeCannotBeTheSameAsEndTime }
-
- if isTimerRunning && backgroundTransitionDate == nil { throw Error.timerIsAlreadyRunning }
+ try validator.checkRequirementsForStartingTimer(startTime, endTime, state, timerState)
}
func assignInitialStartValues(_ startTime: TimeInterval, _ endTime: TimeInterval) {
- initialTime = (startTime, endTime)
- runningTime = startTime
+ configuration.assignInitialStartValues(startTime, endTime)
+ resetRunningTime()
+ resetTimerPublishers()
+ }
+ func startTimer() {
+ handleTimer(status: .inProgress)
}
- func startTimer() { handleTimer(start: true) }
}
// MARK: - Resuming Timer
extension MTimer {
func checkRequirementsForResumingTimer() throws {
- if onRunningTimeChange == nil { throw Error.cannotResumeNotInitialisedTimer }
+ try validator.checkRequirementsForResumingTimer(callbacks)
}
}
-// MARK: - Stopping Timer
+// MARK: - Timer State Control
extension MTimer {
- func stopTimer() { handleTimer(start: false) }
+ func pauseTimer() { handleTimer(status: .paused) }
+ func cancelTimer() { handleTimer(status: .cancelled) }
+ func finishTimer() { handleTimer(status: .finished) }
}
-// MARK: - Resetting Timer
+// MARK: - Reset Timer
extension MTimer {
- func resetRunningTime() { runningTime = initialTime.start }
+ func resetTimer() {
+ configuration.reset()
+ updateInternalTimer(false)
+ timerState = .notStarted
+ updateObservers(false)
+ resetTimerPublishers()
+ publishTimerStatus()
+ }
}
+// MARK: - Running Time Updates
+extension MTimer {
+ func resetRunningTime() { configuration.resetRunningTime() }
+ func skipRunningTime() { configuration.skipRunningTime() }
+}
// MARK: - Handling Timer
private extension MTimer {
- func handleTimer(start: Bool) { if !start || canTimerBeStarted {
- isTimerRunning = start
- updateInternalTimer(start)
- updateObservers(start)
+ func handleTimer(status: MTimerStatus) { if status != .inProgress || configuration.canTimerBeStarted {
+ timerState = status
+ updateInternalTimer(isTimerRunning)
+ updateObservers(isTimerRunning)
publishTimerStatus()
}}
}
private extension MTimer {
- func updateInternalTimer(_ start: Bool) { DispatchQueue.main.async { [self] in switch start {
- case true: updateInternalTimerStart()
- case false: updateInternalTimerStop()
- }}}
- func updateObservers(_ start: Bool) { switch start {
- case true: addObservers()
- case false: removeObservers()
+ func updateInternalTimer(_ start: Bool) {
+ switch start {
+ case true: updateInternalTimerStart()
+ case false: updateInternalTimerStop()
}}
+ func updateObservers(_ start: Bool) {
+ switch start {
+ case true: addObservers()
+ case false: removeObservers()
+ }
+ }
}
private extension MTimer {
func updateInternalTimerStart() {
- internalTimer = .scheduledTimer(withTimeInterval: publisherTime, repeats: true, block: handleTimeChange)
- internalTimer?.tolerance = publisherTimeTolerance
+ let publisherTime = configuration.getPublisherTime()
+ state.internalTimer = .scheduledTimer(timeInterval: publisherTime,
+ target: self,
+ selector: #selector(handleTimeChange),
+ userInfo: nil,
+ repeats: true)
+ state.internalTimer?.tolerance = configuration.publisherTimeTolerance
updateInternalTimerStartAddToRunLoop()
}
- func updateInternalTimerStop() { internalTimer?.invalidate() }
+ func updateInternalTimerStop() {
+ state.internalTimer?.invalidate()
+ }
}
+
private extension MTimer {
/// **CONTEXT**: On macOS, when the mouse is down in a menu item or other tracking loop, the timer will not start.
/// **DECISION**: Adding a timer the RunLoop seems to fix the issue issue.
func updateInternalTimerStartAddToRunLoop() {
#if os(macOS)
- if let internalTimer { RunLoop.main.add(internalTimer, forMode: .common) }
+ guard let internalTimer = state.internalTimer else { return }
+ RunLoop.main.add(internalTimer, forMode: .common)
#endif
}
}
// MARK: - Handling Time Change
private extension MTimer {
- func handleTimeChange(_ timeChange: Any? = nil) {
- runningTime = calculateNewRunningTime(timeChange as? TimeInterval ?? publisherTime)
+ @objc func handleTimeChange(_ timeChange: Any) {
+ configuration.calculateNewRunningTime(timeChange)
stopTimerIfNecessary()
publishRunningTimeChange()
}
}
private extension MTimer {
- func calculateNewRunningTime(_ timeChange: TimeInterval) -> TimeInterval {
- let newRunningTime = runningTime + timeChange * timeIncrementMultiplier
- return timeIncrementMultiplier == -1 ? max(newRunningTime, initialTime.end) : min(newRunningTime, initialTime.end)
- }
- func stopTimerIfNecessary() { if !canTimerBeStarted {
- stopTimer()
+ func stopTimerIfNecessary() { if !configuration.canTimerBeStarted {
+ finishTimer()
}}
}
// MARK: - Handling Background Mode
private extension MTimer {
func addObservers() {
- NotificationCenter.addAppStateNotifications(self, onDidEnterBackground: #selector(didEnterBackgroundNotification), onWillEnterForeground: #selector(willEnterForegroundNotification))
+ NotificationCenter
+ .addAppStateNotifications(self,
+ onDidEnterBackground: #selector(didEnterBackgroundNotification),
+ onWillEnterForeground: #selector(willEnterForegroundNotification))
}
func removeObservers() {
NotificationCenter.removeAppStateChangedNotifications(self)
}
}
private extension MTimer {
- @objc func didEnterBackgroundNotification() {
- internalTimer?.invalidate()
- backgroundTransitionDate = .init()
- }
@objc func willEnterForegroundNotification() {
handleReturnFromBackgroundWhenTimerIsRunning()
- backgroundTransitionDate = nil
+ state.willEnterForeground()
+ }
+ @objc func didEnterBackgroundNotification() {
+ state.didEnterBackground()
}
}
private extension MTimer {
- func handleReturnFromBackgroundWhenTimerIsRunning() { if let backgroundTransitionDate, isTimerRunning {
+ func handleReturnFromBackgroundWhenTimerIsRunning() {
+ guard let backgroundTransitionDate = state.backgroundTransitionDate, isTimerRunning else { return }
let timeChange = Date().timeIntervalSince(backgroundTransitionDate)
-
+
handleTimeChange(timeChange)
resumeTimerAfterReturningFromBackground()
- }}
+ }
}
private extension MTimer {
- func resumeTimerAfterReturningFromBackground() { if canTimerBeStarted {
+ func resumeTimerAfterReturningFromBackground() { if configuration.canTimerBeStarted {
updateInternalTimer(true)
}}
}
@@ -171,26 +185,30 @@ private extension MTimer {
publishTimerStatusChange()
publishRunningTimeChange()
}
+ func resetTimerPublishers() {
+ guard isNeededReset else { return }
+ timerState = .notStarted
+ timerProgress = 0
+ timerTime = .init(timeInterval: configuration.initialTime.start)
+ }
}
+
private extension MTimer {
- func publishTimerStatusChange() { DispatchQueue.main.async { [self] in
- onTimerActivityChange?(isTimerRunning)
+ func publishTimerStatusChange() { DispatchQueue.main.async(qos: .userInteractive) { [weak self] in
+ guard let self else { return }
+ callbacks.onTimerActivityChange?(timerState)
}}
- func publishRunningTimeChange() { DispatchQueue.main.async { [self] in
- onRunningTimeChange?(.init(timeInterval: runningTime))
- onTimerProgressChange?(calculateTimerProgress())
+ func publishRunningTimeChange() { DispatchQueue.main.async(qos: .userInteractive) { [weak self] in
+ guard let self else { return }
+ callbacks.onRunningTimeChange?(.init(timeInterval: configuration.runningTime))
+ callbacks.onTimerProgressChange?(configuration.calculateTimerProgress())
+ timerTime = .init(timeInterval: configuration.runningTime)
+ timerProgress = configuration.calculateTimerProgress()
}}
}
-private extension MTimer {
- func calculateTimerProgress() -> Double {
- let timerTotalTime = max(initialTime.start, initialTime.end) - min(initialTime.start, initialTime.end)
- let timerRunningTime = abs(runningTime - initialTime.start)
- return timerRunningTime / timerTotalTime
- }
-}
-// MARK: - Others
+// MARK: - Helpers
private extension MTimer {
- var canTimerBeStarted: Bool { runningTime != initialTime.end }
- var timeIncrementMultiplier: Double { initialTime.start > initialTime.end ? -1 : 1 }
+ var isTimerRunning: Bool { timerState == .inProgress }
+ var isNeededReset: Bool { timerState == .finished || timerState == .cancelled || timerState == .notStarted }
}
diff --git a/Sources/Internal/Protocols/FactoryInitializable.swift b/Sources/Internal/Protocols/FactoryInitializable.swift
new file mode 100644
index 0000000..9bd68c1
--- /dev/null
+++ b/Sources/Internal/Protocols/FactoryInitializable.swift
@@ -0,0 +1,20 @@
+//
+// FactoryInitializable.swift
+// MijickTimer
+//
+// Created by Alina Petrovska on 15.11.2024.
+//
+
+import SwiftUI
+
+@MainActor public protocol FactoryInitializable {
+ init(_ id: MTimerID)
+}
+
+extension FactoryInitializable where Self: MTimer {
+ public init(_ id: MTimerID) {
+ let timer = MTimerContainer.shared.getTimer(id) ?? MTimer(identifier: id)
+ MTimerContainer.shared.register(timer)
+ self = timer as! Self
+ }
+}
diff --git a/Sources/Public/MTimerID.swift b/Sources/Public/MTimerID.swift
new file mode 100644
index 0000000..1737292
--- /dev/null
+++ b/Sources/Public/MTimerID.swift
@@ -0,0 +1,12 @@
+//
+// MTimerID.swift
+// MijickTimer
+//
+// Created by Alina Petrovska on 11.11.2024.
+//
+
+public struct MTimerID: Equatable {
+ public let rawValue: String
+
+ public init(rawValue: String) { self.rawValue = rawValue }
+}
diff --git a/Sources/Public/MTimerStatus.swift b/Sources/Public/MTimerStatus.swift
new file mode 100644
index 0000000..6ee6e0c
--- /dev/null
+++ b/Sources/Public/MTimerStatus.swift
@@ -0,0 +1,8 @@
+//
+// MTimerStatus.swift
+// MijickTimer
+//
+// Created by Alina Petrovska on 11.11.2024.
+//
+
+public enum MTimerStatus { case notStarted, inProgress, cancelled, paused, finished }
diff --git a/Sources/Public/Public+MTime.swift b/Sources/Public/Public+MTime.swift
index 17056ec..8f8bc27 100644
--- a/Sources/Public/Public+MTime.swift
+++ b/Sources/Public/Public+MTime.swift
@@ -23,7 +23,7 @@ extension MTime {
self.init(timeInterval: timeInterval)
}
public init(timeInterval: TimeInterval) {
- let millisecondsInt = Int(timeInterval * 1000)
+ let millisecondsInt = timeInterval == .infinity ? Int(Self.max.toTimeInterval() * 1000) : Int(timeInterval * 1000)
let hoursDiv = 1000 * 60 * 60
let minutesDiv = 1000 * 60
diff --git a/Sources/Public/Public+MTimer.swift b/Sources/Public/Public+MTimer.swift
index 8afea43..1f9715d 100644
--- a/Sources/Public/Public+MTimer.swift
+++ b/Sources/Public/Public+MTimer.swift
@@ -11,24 +11,8 @@
import SwiftUI
-// MARK: - Creating New Instance Of Timer
-extension MTimer {
- /// Allows to create multiple instances of a timer.
- public static func createNewInstance() -> MTimer { .init() }
-}
-
// MARK: - Initialising Timer
extension MTimer {
- /// Prepares the timer to start.
- /// WARNING: Use the start() method to start the timer.
- public static func publish(every time: TimeInterval, tolerance: TimeInterval = 0.4, _ completion: @escaping (_ currentTime: MTime) -> ()) throws -> MTimer {
- try shared.publish(every: time, tolerance: tolerance, completion)
- }
- /// Prepares the timer to start.
- /// WARNING: Use the start() method to start the timer.
- public static func publish(every time: TimeInterval, tolerance: TimeInterval = 0.4, currentTime: Binding) throws -> MTimer {
- try shared.publish(every: time, tolerance: tolerance) { currentTime.wrappedValue = $0 }
- }
/// Prepares the timer to start.
/// WARNING: Use the start() method to start the timer.
public func publish(every time: TimeInterval, tolerance: TimeInterval = 0.4, currentTime: Binding) throws -> MTimer {
@@ -37,7 +21,7 @@ extension MTimer {
/// Prepares the timer to start.
/// WARNING: Use the start() method to start the timer.
public func publish(every time: TimeInterval, tolerance: TimeInterval = 0.4, _ completion: @escaping (_ currentTime: MTime) -> ()) throws -> MTimer {
- try checkRequirementsForInitialisingTimer(time)
+ try checkRequirementsForInitializingTimer(time)
assignInitialPublisherValues(time, tolerance, completion)
return self
}
@@ -63,22 +47,14 @@ extension MTimer {
// MARK: - Stopping Timer
extension MTimer {
- /// Stops the timer.
- public static func stop() {
- shared.stop()
- }
- /// Stops the timer.
- public func stop() {
- stopTimer()
+ /// Pause the timer.
+ public func pause() {
+ pauseTimer()
}
}
// MARK: - Resuming Timer
extension MTimer {
- /// Resumes the stopped timer.
- public static func resume() throws {
- try shared.resume()
- }
/// Resumes the stopped timer.
public func resume() throws {
try checkRequirementsForResumingTimer()
@@ -86,29 +62,42 @@ extension MTimer {
}
}
-// MARK: - Resetting Timer
+// MARK: - Aborting Timer
extension MTimer {
/// Stops the timer and resets its current time to the initial value.
- public static func reset() {
- shared.reset()
+ public func cancel() {
+ resetRunningTime()
+ cancelTimer()
}
- /// Stops the timer and resets its current time to the initial value.
+}
+
+// MARK: - Aborting Timer
+extension MTimer {
+ /// Stops the timer and resets all timer states to default
public func reset() {
- resetRunningTime()
- stopTimer()
+ resetTimer()
+ }
+}
+
+// MARK: - Skip Timer
+extension MTimer {
+ /// Stops the timer and skips it's condition to the final state.
+ public func skip() {
+ skipRunningTime()
+ finishTimer()
}
}
// MARK: - Publishing Timer Activity Status
extension MTimer {
/// Publishes the timer activity changes.
- public func onTimerActivityChange(_ action: @escaping (_ isRunning: Bool) -> ()) -> MTimer {
- onTimerActivityChange = action
+ public func onTimerActivityChange(_ action: @escaping (_ isRunning: MTimerStatus) -> ()) -> MTimer {
+ callbacks.onTimerActivityChange = action
return self
}
/// Publishes the timer activity changes.
public func bindTimerStatus(isTimerRunning: Binding) -> MTimer {
- onTimerActivityChange { isTimerRunning.wrappedValue = $0 }
+ onTimerActivityChange { isTimerRunning.wrappedValue = $0 == .inProgress }
}
}
@@ -116,7 +105,7 @@ extension MTimer {
extension MTimer {
/// Publishes the timer progress changes.
public func onTimerProgressChange(_ action: @escaping (_ progress: Double) -> ()) -> MTimer {
- onTimerProgressChange = action
+ callbacks.onTimerProgressChange = action
return self
}
/// Publishes the timer progress changes.
diff --git a/Tests/MTimeTests.swift b/Tests/MTimeTests.swift
index 1640fa9..cf1673d 100644
--- a/Tests/MTimeTests.swift
+++ b/Tests/MTimeTests.swift
@@ -17,7 +17,7 @@ final class MTimeTests: XCTestCase {}
// MARK: - Initialisation from TimeInterval
extension MTimeTests {
func testTimeInitialisesCorrectly_1second() {
- let time = MTime(1)
+ let time = MTime(timeInterval: 1)
XCTAssertEqual(time.hours, 0)
XCTAssertEqual(time.minutes, 0)
@@ -25,7 +25,7 @@ extension MTimeTests {
XCTAssertEqual(time.milliseconds, 0)
}
func testTimeInitialisesCorrectly_59seconds120milliseconds() {
- let time = MTime(59.12)
+ let time = MTime(timeInterval: 59.12)
XCTAssertEqual(time.hours, 0)
XCTAssertEqual(time.minutes, 0)
@@ -33,7 +33,7 @@ extension MTimeTests {
XCTAssertEqual(time.milliseconds, 120)
}
func testTimeInitialisesCorrectly_21minutes37seconds() {
- let time = MTime(1297)
+ let time = MTime(timeInterval: 1297)
XCTAssertEqual(time.hours, 0)
XCTAssertEqual(time.minutes, 21)
@@ -41,7 +41,7 @@ extension MTimeTests {
XCTAssertEqual(time.milliseconds, 0)
}
func testTimeInitialisesCorrectly_1hour39minutes17seconds140milliseconds() {
- let time = MTime(5957.14)
+ let time = MTime(timeInterval: 5957.14)
XCTAssertEqual(time.hours, 1)
XCTAssertEqual(time.minutes, 39)
diff --git a/Tests/MTimerTests.swift b/Tests/MTimerTests.swift
index 7910fef..ad5b360 100644
--- a/Tests/MTimerTests.swift
+++ b/Tests/MTimerTests.swift
@@ -12,10 +12,12 @@
import XCTest
@testable import MijickTimer
-final class MTimerTests: XCTestCase {
+@MainActor final class MTimerTests: XCTestCase {
var currentTime: TimeInterval = 0
- override func setUp() { MTimer.stop() }
+ override func setUp() async throws {
+ MTimerContainer.shared.resetAll()
+ }
}
// MARK: - Basics
@@ -23,55 +25,71 @@ extension MTimerTests {
func testTimerStarts() {
try! defaultTimer.start()
wait(for: defaultWaitingTime)
-
+
XCTAssertGreaterThan(currentTime, 0)
+ XCTAssertEqual(.inProgress, timer.timerState)
}
func testTimerIsCancellable() {
try! defaultTimer.start()
wait(for: defaultWaitingTime)
- MTimer.stop()
+ timer.cancel()
wait(for: defaultWaitingTime)
let timeAfterStop = currentTime
wait(for: defaultWaitingTime)
XCTAssertEqual(timeAfterStop, currentTime)
+ XCTAssertEqual(.cancelled, timer.timerState)
}
func testTimerIsResetable() {
let startTime: TimeInterval = 3
-
try! defaultTimer.start(from: startTime)
wait(for: defaultWaitingTime)
-
+
XCTAssertNotEqual(currentTime, startTime)
-
+
+ wait(for: defaultWaitingTime)
+ timer.reset()
+ wait(for: defaultWaitingTime)
+
+ XCTAssertEqual(0, currentTime)
+ XCTAssertEqual(0, timer.timerProgress)
+ XCTAssertEqual(.notStarted, timer.timerState)
+ }
+ func testTimerIsSkippable() {
+ let endTime: TimeInterval = 3
+
+ try! defaultTimer.start(to: endTime)
wait(for: defaultWaitingTime)
- MTimer.reset()
+ timer.skip()
wait(for: defaultWaitingTime)
- XCTAssertEqual(startTime, currentTime)
+ XCTAssertEqual(endTime, currentTime)
+ XCTAssertEqual(1, timer.timerProgress)
+ XCTAssertEqual(.finished, timer.timerState)
}
func testTimerCanBeResumed() {
try! defaultTimer.start()
wait(for: defaultWaitingTime)
- MTimer.stop()
+ timer.pause()
let timeAfterStop = currentTime
wait(for: defaultWaitingTime)
- try! MTimer.resume()
+ try! timer.resume()
wait(for: defaultWaitingTime)
XCTAssertNotEqual(timeAfterStop, currentTime)
+ XCTAssertEqual(.inProgress, timer.timerState)
}
}
// MARK: - Additional Basics
extension MTimerTests {
func testTimerShouldPublishAccurateValuesWithZeroTolerance() {
- try! MTimer
- .publish(every: 0.1, tolerance: 0) { self.currentTime = $0.toTimeInterval() }
+ try! timer
+ .publish(every: 0.1, tolerance: 0.0) { self.currentTime = $0.toTimeInterval() }
.start()
wait(for: 0.6)
@@ -80,8 +98,10 @@ extension MTimerTests {
func testTimerShouldPublishInaccurateValuesWithNonZeroTolerance() {
try! defaultTimer.start()
wait(for: 1)
-
- XCTAssertNotEqual(currentTime, 1)
+
+ // usually returns 1.0000000000000002 that is equal to 1.0
+ // OLD test XCTAssertNotEqual(currentTime, 1)
+ XCTAssertEqual(currentTime, 1)
}
func testTimerCanRunBackwards() {
try! defaultTimer.start(from: 3, to: 1)
@@ -90,14 +110,14 @@ extension MTimerTests {
XCTAssertLessThan(currentTime, 3)
}
func testTimerPublishesStatuses() {
- var statuses: [Bool: Bool] = [true: false, false: false]
+ var statuses: [MTimerStatus: Bool] = [.inProgress: false, .cancelled: false]
try! defaultTimer
.onTimerActivityChange { statuses[$0] = true }
.start()
wait(for: defaultWaitingTime)
- MTimer.stop()
+ timer.cancel()
wait(for: defaultWaitingTime)
XCTAssertTrue(statuses.values.filter { !$0 }.isEmpty)
@@ -137,7 +157,7 @@ extension MTimerTests {
func testTimerCanHaveMultipleInstances() {
var newTime: TimeInterval = 0
- let newTimer = MTimer.createNewInstance()
+ let newTimer = MTimer(.multipleInstancesTimer)
try! newTimer
.publish(every: 0.3) { newTime = $0.toTimeInterval() }
.start(from: 10, to: 100)
@@ -150,20 +170,20 @@ extension MTimerTests {
XCTAssertNotEqual(newTime, currentTime)
}
func testNewInstanceTimerCanBeStopped() {
- let newTimer = MTimer.createNewInstance()
+ let newTimer = MTimer(.stoppableTimer)
try! newTimer
- .publish(every: 0.1) { self.currentTime = $0.toTimeInterval() }
+ .publish(every: 0.1) { print($0); self.currentTime = $0.toTimeInterval() }
.start()
wait(for: defaultWaitingTime)
- newTimer.stop()
+ newTimer.cancel()
wait(for: defaultWaitingTime)
let timeAfterStop = currentTime
wait(for: defaultWaitingTime)
- XCTAssertGreaterThan(currentTime, 0)
+ XCTAssertEqual(currentTime, 0)
XCTAssertEqual(timeAfterStop, currentTime)
}
}
@@ -173,7 +193,7 @@ extension MTimerTests {
func testTimerProgressCountsCorrectly_From0To10() {
var progress: Double = 0
- try! MTimer
+ try! timer
.publish(every: 0.5, tolerance: 0) { self.currentTime = $0.toTimeInterval() }
.onTimerProgressChange { progress = $0 }
.start(from: 0, to: 10)
@@ -184,7 +204,7 @@ extension MTimerTests {
func testTimerProgressCountsCorrectly_From10To29() {
var progress: Double = 0
- try! MTimer
+ try! timer
.publish(every: 0.5, tolerance: 0) { self.currentTime = $0.toTimeInterval() }
.onTimerProgressChange { progress = $0 }
.start(from: 10, to: 29)
@@ -195,7 +215,7 @@ extension MTimerTests {
func testTimerProgressCountsCorrectly_From31To100() {
var progress: Double = 0
- try! MTimer
+ try! timer
.publish(every: 0.5, tolerance: 0) { self.currentTime = $0.toTimeInterval() }
.onTimerProgressChange { progress = $0 }
.start(from: 31, to: 100)
@@ -206,7 +226,7 @@ extension MTimerTests {
func testTimerProgressCountsCorrectly_From100To0() {
var progress: Double = 0
- try! MTimer
+ try! timer
.publish(every: 0.5, tolerance: 0) { self.currentTime = $0.toTimeInterval() }
.onTimerProgressChange { progress = $0 }
.start(from: 100, to: 0)
@@ -217,20 +237,28 @@ extension MTimerTests {
func testTimerProgressCountsCorrectly_From31To14() {
var progress: Double = 0
- try! MTimer
+ try! timer
.publish(every: 0.25, tolerance: 0) { self.currentTime = $0.toTimeInterval() }
.onTimerProgressChange { progress = $0 }
.start(from: 31, to: 14)
wait(for: 1)
XCTAssertEqual(progress, 1/17)
+ XCTAssertEqual(timer.timerProgress, 1/17)
+ }
+ func timerShouldPublishStatusUpdateAtTheEndIfPublishersNotSetUpped() {
+ let timer = MTimer(.timerWithoutPublishers)
+ try! timer.start(to: 1)
+ wait(for: 1)
+
+ XCTAssertEqual(1.0, timer.timerTime.toTimeInterval())
}
}
// MARK: - Errors
extension MTimerTests {
func testTimerCannotBeInitialised_PublishTimeIsTooLess() {
- XCTAssertThrowsError(try MTimer.publish(every: 0.0001, { _ in })) { error in
+ XCTAssertThrowsError(try timer.publish(every: 0.0001, { _ in })) { error in
let error = error as! MTimer.Error
XCTAssertEqual(error, .publisherTimeCannotBeLessThanOneMillisecond)
}
@@ -254,7 +282,7 @@ extension MTimerTests {
}
}
func testCannotResumeTimer_WhenTimerIsNotInitialised() {
- XCTAssertThrowsError(try MTimer.resume()) { error in
+ XCTAssertThrowsError(try timer.resume()) { error in
let error = error as! MTimer.Error
XCTAssertEqual(error, .cannotResumeNotInitialisedTimer)
}
@@ -284,5 +312,12 @@ private extension MTimerTests {
}
private extension MTimerTests {
var defaultWaitingTime: TimeInterval { 0.15 }
- var defaultTimer: MTimer { try! .publish(every: 0.05, tolerance: 0.5) { self.currentTime = $0.toTimeInterval() } }
+ var defaultTimer: MTimer { try! timer.publish(every: 0.05, tolerance: 0.5) { self.currentTime = $0.toTimeInterval() } }
+ var timer: MTimer { .init(.testTimer) }
+}
+fileprivate extension MTimerID {
+ @MainActor static let testTimer: MTimerID = .init(rawValue: "Test timer")
+ @MainActor static let timerWithoutPublishers: MTimerID = .init(rawValue: "Timer Without Publishers")
+ @MainActor static let stoppableTimer: MTimerID = .init(rawValue: "Stoppable Timer")
+ @MainActor static let multipleInstancesTimer: MTimerID = .init(rawValue: "Multiple Instances")
}
From b68abb66bf527c4701d237dc638c778c8f819496 Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Fri, 22 Nov 2024 19:05:12 +0100
Subject: [PATCH 02/45] 1
---
.../xcshareddata/IDEWorkspaceChecks.plist | 8 --------
Package.swift | 5 +++--
2 files changed, 3 insertions(+), 10 deletions(-)
delete mode 100644 .swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
diff --git a/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
deleted file mode 100644
index 18d9810..0000000
--- a/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
- IDEDidComputeMac32BitWarning
-
-
-
diff --git a/Package.swift b/Package.swift
index 85206b0..d71f7cb 100644
--- a/Package.swift
+++ b/Package.swift
@@ -1,4 +1,4 @@
-// swift-tools-version: 5.9
+// swift-tools-version: 6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
@@ -15,5 +15,6 @@ let package = Package(
targets: [
.target(name: "MijickTimer", dependencies: [], path: "Sources"),
.testTarget(name: "MijickTimerTests", dependencies: ["MijickTimer"], path: "Tests")
- ]
+ ],
+ swiftLanguageModes: [.version("6"), .v5]
)
From 89f44a815cdcfe0e26d1acbcfa24fd888050433c Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Sat, 23 Nov 2024 01:49:08 +0100
Subject: [PATCH 03/45] 1
---
Sources/Public/MTimerStatus.swift | 8 +++++-
Sources/Public/Public+MTimer.swift | 46 +++++++++++++++---------------
2 files changed, 30 insertions(+), 24 deletions(-)
diff --git a/Sources/Public/MTimerStatus.swift b/Sources/Public/MTimerStatus.swift
index 6ee6e0c..d487fa3 100644
--- a/Sources/Public/MTimerStatus.swift
+++ b/Sources/Public/MTimerStatus.swift
@@ -5,4 +5,10 @@
// Created by Alina Petrovska on 11.11.2024.
//
-public enum MTimerStatus { case notStarted, inProgress, cancelled, paused, finished }
+public enum MTimerStatus {
+ case notStarted
+ case inProgress
+ case cancelled
+ case paused
+ case finished
+}
diff --git a/Sources/Public/Public+MTimer.swift b/Sources/Public/Public+MTimer.swift
index 1f9715d..882ccd6 100644
--- a/Sources/Public/Public+MTimer.swift
+++ b/Sources/Public/Public+MTimer.swift
@@ -12,15 +12,15 @@
import SwiftUI
// MARK: - Initialising Timer
-extension MTimer {
+public extension MTimer {
/// Prepares the timer to start.
/// WARNING: Use the start() method to start the timer.
- public func publish(every time: TimeInterval, tolerance: TimeInterval = 0.4, currentTime: Binding) throws -> MTimer {
+ func publish(every time: TimeInterval, tolerance: TimeInterval = 0.4, currentTime: Binding) throws -> MTimer {
try publish(every: time, tolerance: tolerance) { currentTime.wrappedValue = $0 }
}
/// Prepares the timer to start.
/// WARNING: Use the start() method to start the timer.
- public func publish(every time: TimeInterval, tolerance: TimeInterval = 0.4, _ completion: @escaping (_ currentTime: MTime) -> ()) throws -> MTimer {
+ func publish(every time: TimeInterval, tolerance: TimeInterval = 0.4, _ completion: @escaping (_ currentTime: MTime) -> ()) throws -> MTimer {
try checkRequirementsForInitializingTimer(time)
assignInitialPublisherValues(time, tolerance, completion)
return self
@@ -28,88 +28,88 @@ extension MTimer {
}
// MARK: - Starting Timer
-extension MTimer {
+public extension MTimer {
/// Starts the timer using the specified initial values. Can be run backwards - use any "to" value that is greater than "from".
- public func start(from startTime: MTime = .zero, to endTime: MTime = .max) throws {
+ func start(from startTime: MTime = .zero, to endTime: MTime = .max) throws {
try start(from: startTime.toTimeInterval(), to: endTime.toTimeInterval())
}
/// Starts the timer using the specified initial values. Can be run backwards - use any "to" value that is greater than "from".
- public func start(from startTime: TimeInterval = 0, to endTime: TimeInterval = .infinity) throws {
+ func start(from startTime: TimeInterval = 0, to endTime: TimeInterval = .infinity) throws {
try checkRequirementsForStartingTimer(startTime, endTime)
assignInitialStartValues(startTime, endTime)
startTimer()
}
/// Starts the timer.
- public func start() throws {
+ func start() throws {
try start(from: .zero, to: .infinity)
}
}
// MARK: - Stopping Timer
-extension MTimer {
+public extension MTimer {
/// Pause the timer.
- public func pause() {
+ func pause() {
pauseTimer()
}
}
// MARK: - Resuming Timer
-extension MTimer {
+public extension MTimer {
/// Resumes the stopped timer.
- public func resume() throws {
+ func resume() throws {
try checkRequirementsForResumingTimer()
startTimer()
}
}
// MARK: - Aborting Timer
-extension MTimer {
+public extension MTimer {
/// Stops the timer and resets its current time to the initial value.
- public func cancel() {
+ func cancel() {
resetRunningTime()
cancelTimer()
}
}
// MARK: - Aborting Timer
-extension MTimer {
+public extension MTimer {
/// Stops the timer and resets all timer states to default
- public func reset() {
+ func reset() {
resetTimer()
}
}
// MARK: - Skip Timer
-extension MTimer {
+public extension MTimer {
/// Stops the timer and skips it's condition to the final state.
- public func skip() {
+ func skip() {
skipRunningTime()
finishTimer()
}
}
// MARK: - Publishing Timer Activity Status
-extension MTimer {
+public extension MTimer {
/// Publishes the timer activity changes.
- public func onTimerActivityChange(_ action: @escaping (_ isRunning: MTimerStatus) -> ()) -> MTimer {
+ func onTimerActivityChange(_ action: @escaping (_ isRunning: MTimerStatus) -> ()) -> MTimer {
callbacks.onTimerActivityChange = action
return self
}
/// Publishes the timer activity changes.
- public func bindTimerStatus(isTimerRunning: Binding) -> MTimer {
+ func bindTimerStatus(isTimerRunning: Binding) -> MTimer {
onTimerActivityChange { isTimerRunning.wrappedValue = $0 == .inProgress }
}
}
// MARK: - Publishing Timer Progress
-extension MTimer {
+public extension MTimer {
/// Publishes the timer progress changes.
- public func onTimerProgressChange(_ action: @escaping (_ progress: Double) -> ()) -> MTimer {
+ func onTimerProgressChange(_ action: @escaping (_ progress: Double) -> ()) -> MTimer {
callbacks.onTimerProgressChange = action
return self
}
/// Publishes the timer progress changes.
- public func bindTimerProgress(progress: Binding) -> MTimer {
+ func bindTimerProgress(progress: Binding) -> MTimer {
onTimerProgressChange { progress.wrappedValue = $0 }
}
}
From 305a70e01d8e7018405710efefc92755d2a36553 Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Sat, 23 Nov 2024 13:16:33 +0100
Subject: [PATCH 04/45] 1
---
Package.swift | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Package.swift b/Package.swift
index d71f7cb..74a0714 100644
--- a/Package.swift
+++ b/Package.swift
@@ -16,5 +16,5 @@ let package = Package(
.target(name: "MijickTimer", dependencies: [], path: "Sources"),
.testTarget(name: "MijickTimerTests", dependencies: ["MijickTimer"], path: "Tests")
],
- swiftLanguageModes: [.version("6"), .v5]
+ swiftLanguageModes: [.v6]
)
From cddf0db6dc958ebd29c6759e119fb99275d11485 Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Sat, 23 Nov 2024 13:19:40 +0100
Subject: [PATCH 05/45] Renames
---
Sources/Internal/Helpers/MTimerCallbacks.swift | 2 +-
Sources/Internal/MTimer.swift | 16 ++++++++--------
Sources/Public/Public+MTimer.swift | 2 +-
Tests/MTimerTests.swift | 10 +++++-----
4 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/Sources/Internal/Helpers/MTimerCallbacks.swift b/Sources/Internal/Helpers/MTimerCallbacks.swift
index 555e556..83c6ff2 100644
--- a/Sources/Internal/Helpers/MTimerCallbacks.swift
+++ b/Sources/Internal/Helpers/MTimerCallbacks.swift
@@ -9,6 +9,6 @@ import SwiftUI
class MTimerCallbacks {
var onRunningTimeChange: ((MTime) -> ())?
- var onTimerActivityChange: ((MTimerStatus) -> ())?
+ var onTimerStatusChange: ((MTimerStatus) -> ())?
var onTimerProgressChange: ((Double) -> ())?
}
diff --git a/Sources/Internal/MTimer.swift b/Sources/Internal/MTimer.swift
index 46d9479..2974cc4 100644
--- a/Sources/Internal/MTimer.swift
+++ b/Sources/Internal/MTimer.swift
@@ -16,7 +16,7 @@ public final class MTimer: ObservableObject, FactoryInitializable {
let id: MTimerID
@Published public private(set) var timerTime: MTime = .init()
- @Published public private(set) var timerState: MTimerStatus = .notStarted
+ @Published public private(set) var timerStatus: MTimerStatus = .notStarted // Status not state
@Published public private(set) var timerProgress: Double = 0
init(identifier: MTimerID) { self.id = identifier }
@@ -36,7 +36,7 @@ extension MTimer {
// MARK: - Starting Timer
extension MTimer {
func checkRequirementsForStartingTimer(_ startTime: TimeInterval, _ endTime: TimeInterval) throws {
- try validator.checkRequirementsForStartingTimer(startTime, endTime, state, timerState)
+ try validator.checkRequirementsForStartingTimer(startTime, endTime, state, timerStatus)
}
func assignInitialStartValues(_ startTime: TimeInterval, _ endTime: TimeInterval) {
configuration.assignInitialStartValues(startTime, endTime)
@@ -67,7 +67,7 @@ extension MTimer {
func resetTimer() {
configuration.reset()
updateInternalTimer(false)
- timerState = .notStarted
+ timerStatus = .notStarted
updateObservers(false)
resetTimerPublishers()
publishTimerStatus()
@@ -83,7 +83,7 @@ extension MTimer {
// MARK: - Handling Timer
private extension MTimer {
func handleTimer(status: MTimerStatus) { if status != .inProgress || configuration.canTimerBeStarted {
- timerState = status
+ timerStatus = status
updateInternalTimer(isTimerRunning)
updateObservers(isTimerRunning)
publishTimerStatus()
@@ -187,7 +187,7 @@ private extension MTimer {
}
func resetTimerPublishers() {
guard isNeededReset else { return }
- timerState = .notStarted
+ timerStatus = .notStarted
timerProgress = 0
timerTime = .init(timeInterval: configuration.initialTime.start)
}
@@ -196,7 +196,7 @@ private extension MTimer {
private extension MTimer {
func publishTimerStatusChange() { DispatchQueue.main.async(qos: .userInteractive) { [weak self] in
guard let self else { return }
- callbacks.onTimerActivityChange?(timerState)
+ callbacks.onTimerStatusChange?(timerStatus)
}}
func publishRunningTimeChange() { DispatchQueue.main.async(qos: .userInteractive) { [weak self] in
guard let self else { return }
@@ -209,6 +209,6 @@ private extension MTimer {
// MARK: - Helpers
private extension MTimer {
- var isTimerRunning: Bool { timerState == .inProgress }
- var isNeededReset: Bool { timerState == .finished || timerState == .cancelled || timerState == .notStarted }
+ var isTimerRunning: Bool { timerStatus == .inProgress }
+ var isNeededReset: Bool { timerStatus == .finished || timerStatus == .cancelled || timerStatus == .notStarted }
}
diff --git a/Sources/Public/Public+MTimer.swift b/Sources/Public/Public+MTimer.swift
index 882ccd6..4e451c4 100644
--- a/Sources/Public/Public+MTimer.swift
+++ b/Sources/Public/Public+MTimer.swift
@@ -92,7 +92,7 @@ public extension MTimer {
public extension MTimer {
/// Publishes the timer activity changes.
func onTimerActivityChange(_ action: @escaping (_ isRunning: MTimerStatus) -> ()) -> MTimer {
- callbacks.onTimerActivityChange = action
+ callbacks.onTimerStatusChange = action
return self
}
/// Publishes the timer activity changes.
diff --git a/Tests/MTimerTests.swift b/Tests/MTimerTests.swift
index ad5b360..90e49e9 100644
--- a/Tests/MTimerTests.swift
+++ b/Tests/MTimerTests.swift
@@ -27,7 +27,7 @@ extension MTimerTests {
wait(for: defaultWaitingTime)
XCTAssertGreaterThan(currentTime, 0)
- XCTAssertEqual(.inProgress, timer.timerState)
+ XCTAssertEqual(.inProgress, timer.timerStatus)
}
func testTimerIsCancellable() {
try! defaultTimer.start()
@@ -40,7 +40,7 @@ extension MTimerTests {
wait(for: defaultWaitingTime)
XCTAssertEqual(timeAfterStop, currentTime)
- XCTAssertEqual(.cancelled, timer.timerState)
+ XCTAssertEqual(.cancelled, timer.timerStatus)
}
func testTimerIsResetable() {
let startTime: TimeInterval = 3
@@ -55,7 +55,7 @@ extension MTimerTests {
XCTAssertEqual(0, currentTime)
XCTAssertEqual(0, timer.timerProgress)
- XCTAssertEqual(.notStarted, timer.timerState)
+ XCTAssertEqual(.notStarted, timer.timerStatus)
}
func testTimerIsSkippable() {
let endTime: TimeInterval = 3
@@ -67,7 +67,7 @@ extension MTimerTests {
XCTAssertEqual(endTime, currentTime)
XCTAssertEqual(1, timer.timerProgress)
- XCTAssertEqual(.finished, timer.timerState)
+ XCTAssertEqual(.finished, timer.timerStatus)
}
func testTimerCanBeResumed() {
try! defaultTimer.start()
@@ -81,7 +81,7 @@ extension MTimerTests {
wait(for: defaultWaitingTime)
XCTAssertNotEqual(timeAfterStop, currentTime)
- XCTAssertEqual(.inProgress, timer.timerState)
+ XCTAssertEqual(.inProgress, timer.timerStatus)
}
}
From 4fe63eb919e272ecd73e021359e95e9e39699b4f Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Sat, 23 Nov 2024 13:31:45 +0100
Subject: [PATCH 06/45] 1
---
.../Helpers/MTimerConfigurationManager.swift | 48 +++++++++----------
Sources/Internal/MTimer.swift | 16 +++----
2 files changed, 32 insertions(+), 32 deletions(-)
diff --git a/Sources/Internal/Helpers/MTimerConfigurationManager.swift b/Sources/Internal/Helpers/MTimerConfigurationManager.swift
index f08a065..578b188 100644
--- a/Sources/Internal/Helpers/MTimerConfigurationManager.swift
+++ b/Sources/Internal/Helpers/MTimerConfigurationManager.swift
@@ -8,51 +8,51 @@
import SwiftUI
class MTimerConfigurationManager {
- var initialTime: (start: TimeInterval, end: TimeInterval) = (0, 1)
- var publisherTime: TimeInterval = 0
- var publisherTimeTolerance: TimeInterval = 0.4
- var runningTime: TimeInterval = 0
+ private(set) var time: (start: TimeInterval, end: TimeInterval) = (0, 1)
+ private(set) var publisherTime: TimeInterval = 0
+ private(set) var publisherTimeTolerance: TimeInterval = 0.4
+ private(set) var currentTime: TimeInterval = 0
}
extension MTimerConfigurationManager {
- func assignInitialStartValues(_ startTime: TimeInterval, _ endTime: TimeInterval) {
- initialTime = (startTime, endTime)
- runningTime = startTime
+ func setInitialTime(startTime: TimeInterval, endTime: TimeInterval) {
+ time = (startTime, endTime)
+ currentTime = startTime
}
- func assignInitialPublisherValues(_ time: TimeInterval, _ tolerance: TimeInterval) {
+ func setInitialPublisher(time: TimeInterval, tolerance: TimeInterval) {
publisherTime = time
publisherTimeTolerance = tolerance
}
- func resetRunningTime() {
- runningTime = initialTime.start
+ func setCurrentTimeToStart() {
+ currentTime = time.start
}
- func skipRunningTime() {
- runningTime = initialTime.end
+ func setCurrentTimeToEnd() {
+ currentTime = time.end
}
func getPublisherTime() -> TimeInterval {
- publisherTime == 0 ? max(initialTime.start, initialTime.end) : publisherTime
+ publisherTime == 0 ? max(time.start, time.end) : publisherTime
}
- func calculateNewRunningTime(_ timeChange: Any?) {
+ func calculateNewCurrentTime(_ timeChange: Any?) {
let timeChange = timeChange as? TimeInterval ?? publisherTime
- let newRunningTime = runningTime + timeChange * timeIncrementMultiplier
- runningTime = timeIncrementMultiplier == -1
- ? max(newRunningTime, initialTime.end)
- : min(newRunningTime, initialTime.end)
+ let newCurrentTime = currentTime + timeChange * timeIncrementMultiplier
+ currentTime = timeIncrementMultiplier == -1
+ ? max(newCurrentTime, time.end)
+ : min(newCurrentTime, time.end)
}
func calculateTimerProgress() -> Double {
- let timerTotalTime = max(initialTime.start, initialTime.end) - min(initialTime.start, initialTime.end)
- let timerRunningTime = abs(runningTime - initialTime.start)
+ let timerTotalTime = max(time.start, time.end) - min(time.start, time.end)
+ let timerRunningTime = abs(currentTime - time.start)
return timerRunningTime / timerTotalTime
}
func reset() {
- initialTime = (0, 1)
+ time = (0, 1)
publisherTime = 0
publisherTimeTolerance = 0.4
- runningTime = 0
+ currentTime = 0
}
}
extension MTimerConfigurationManager {
- var canTimerBeStarted: Bool { runningTime != initialTime.end }
- var timeIncrementMultiplier: Double { initialTime.start > initialTime.end ? -1 : 1 }
+ var canTimerBeStarted: Bool { currentTime != time.end }
+ var timeIncrementMultiplier: Double { time.start > time.end ? -1 : 1 }
}
diff --git a/Sources/Internal/MTimer.swift b/Sources/Internal/MTimer.swift
index 2974cc4..cf9fdaa 100644
--- a/Sources/Internal/MTimer.swift
+++ b/Sources/Internal/MTimer.swift
@@ -28,7 +28,7 @@ extension MTimer {
try validator.checkRequirementsForInitializingTimer(publisherTime)
}
func assignInitialPublisherValues(_ time: TimeInterval, _ tolerance: TimeInterval, _ completion: @escaping (MTime) -> ()) {
- configuration.assignInitialPublisherValues(time, tolerance)
+ configuration.setInitialPublisher(time: time, tolerance: tolerance)
callbacks.onRunningTimeChange = completion
}
}
@@ -39,7 +39,7 @@ extension MTimer {
try validator.checkRequirementsForStartingTimer(startTime, endTime, state, timerStatus)
}
func assignInitialStartValues(_ startTime: TimeInterval, _ endTime: TimeInterval) {
- configuration.assignInitialStartValues(startTime, endTime)
+ configuration.setInitialTime(startTime: startTime, endTime: endTime)
resetRunningTime()
resetTimerPublishers()
}
@@ -76,8 +76,8 @@ extension MTimer {
// MARK: - Running Time Updates
extension MTimer {
- func resetRunningTime() { configuration.resetRunningTime() }
- func skipRunningTime() { configuration.skipRunningTime() }
+ func resetRunningTime() { configuration.setCurrentTimeToStart() }
+ func skipRunningTime() { configuration.setCurrentTimeToEnd() }
}
// MARK: - Handling Timer
@@ -132,7 +132,7 @@ private extension MTimer {
// MARK: - Handling Time Change
private extension MTimer {
@objc func handleTimeChange(_ timeChange: Any) {
- configuration.calculateNewRunningTime(timeChange)
+ configuration.calculateNewCurrentTime(timeChange)
stopTimerIfNecessary()
publishRunningTimeChange()
}
@@ -189,7 +189,7 @@ private extension MTimer {
guard isNeededReset else { return }
timerStatus = .notStarted
timerProgress = 0
- timerTime = .init(timeInterval: configuration.initialTime.start)
+ timerTime = .init(timeInterval: configuration.time.start)
}
}
@@ -200,9 +200,9 @@ private extension MTimer {
}}
func publishRunningTimeChange() { DispatchQueue.main.async(qos: .userInteractive) { [weak self] in
guard let self else { return }
- callbacks.onRunningTimeChange?(.init(timeInterval: configuration.runningTime))
+ callbacks.onRunningTimeChange?(.init(timeInterval: configuration.currentTime))
callbacks.onTimerProgressChange?(configuration.calculateTimerProgress())
- timerTime = .init(timeInterval: configuration.runningTime)
+ timerTime = .init(timeInterval: configuration.currentTime)
timerProgress = configuration.calculateTimerProgress()
}}
}
From d8c9224b7c698df5a49a2ca2cbe7aa47b829c653 Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Sat, 23 Nov 2024 13:33:53 +0100
Subject: [PATCH 07/45] 1
---
.../Helpers/MTimerConfigurationManager.swift | 23 +++++++++++--------
Sources/Internal/MTimer.swift | 6 ++---
2 files changed, 17 insertions(+), 12 deletions(-)
diff --git a/Sources/Internal/Helpers/MTimerConfigurationManager.swift b/Sources/Internal/Helpers/MTimerConfigurationManager.swift
index 578b188..a66d7b9 100644
--- a/Sources/Internal/Helpers/MTimerConfigurationManager.swift
+++ b/Sources/Internal/Helpers/MTimerConfigurationManager.swift
@@ -14,6 +14,7 @@ class MTimerConfigurationManager {
private(set) var currentTime: TimeInterval = 0
}
+// MARK: Setters
extension MTimerConfigurationManager {
func setInitialTime(startTime: TimeInterval, endTime: TimeInterval) {
time = (startTime, endTime)
@@ -29,21 +30,13 @@ extension MTimerConfigurationManager {
func setCurrentTimeToEnd() {
currentTime = time.end
}
- func getPublisherTime() -> TimeInterval {
- publisherTime == 0 ? max(time.start, time.end) : publisherTime
- }
- func calculateNewCurrentTime(_ timeChange: Any?) {
+ func setNewCurrentTime(_ timeChange: Any?) {
let timeChange = timeChange as? TimeInterval ?? publisherTime
let newCurrentTime = currentTime + timeChange * timeIncrementMultiplier
currentTime = timeIncrementMultiplier == -1
? max(newCurrentTime, time.end)
: min(newCurrentTime, time.end)
}
- func calculateTimerProgress() -> Double {
- let timerTotalTime = max(time.start, time.end) - min(time.start, time.end)
- let timerRunningTime = abs(currentTime - time.start)
- return timerRunningTime / timerTotalTime
- }
func reset() {
time = (0, 1)
publisherTime = 0
@@ -52,6 +45,18 @@ extension MTimerConfigurationManager {
}
}
+// MARK: Getters
+extension MTimerConfigurationManager {
+ func getPublisherTime() -> TimeInterval {
+ publisherTime == 0 ? max(time.start, time.end) : publisherTime
+ }
+ func getTimerProgress() -> Double {
+ let timerTotalTime = max(time.start, time.end) - min(time.start, time.end)
+ let timerRunningTime = abs(currentTime - time.start)
+ return timerRunningTime / timerTotalTime
+ }
+}
+
extension MTimerConfigurationManager {
var canTimerBeStarted: Bool { currentTime != time.end }
var timeIncrementMultiplier: Double { time.start > time.end ? -1 : 1 }
diff --git a/Sources/Internal/MTimer.swift b/Sources/Internal/MTimer.swift
index cf9fdaa..c06d513 100644
--- a/Sources/Internal/MTimer.swift
+++ b/Sources/Internal/MTimer.swift
@@ -132,7 +132,7 @@ private extension MTimer {
// MARK: - Handling Time Change
private extension MTimer {
@objc func handleTimeChange(_ timeChange: Any) {
- configuration.calculateNewCurrentTime(timeChange)
+ configuration.setNewCurrentTime(timeChange)
stopTimerIfNecessary()
publishRunningTimeChange()
}
@@ -201,9 +201,9 @@ private extension MTimer {
func publishRunningTimeChange() { DispatchQueue.main.async(qos: .userInteractive) { [weak self] in
guard let self else { return }
callbacks.onRunningTimeChange?(.init(timeInterval: configuration.currentTime))
- callbacks.onTimerProgressChange?(configuration.calculateTimerProgress())
+ callbacks.onTimerProgressChange?(configuration.getTimerProgress())
timerTime = .init(timeInterval: configuration.currentTime)
- timerProgress = configuration.calculateTimerProgress()
+ timerProgress = configuration.getTimerProgress()
}}
}
From eddd62a1f348810ccd85984083b58c14a21da57a Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Sat, 23 Nov 2024 13:36:49 +0100
Subject: [PATCH 08/45] 1
---
.../Helpers/MTimerConfigurationManager.swift | 29 ++++++++++---------
Sources/Internal/MTimer.swift | 2 +-
2 files changed, 17 insertions(+), 14 deletions(-)
diff --git a/Sources/Internal/Helpers/MTimerConfigurationManager.swift b/Sources/Internal/Helpers/MTimerConfigurationManager.swift
index a66d7b9..3c3c1a6 100644
--- a/Sources/Internal/Helpers/MTimerConfigurationManager.swift
+++ b/Sources/Internal/Helpers/MTimerConfigurationManager.swift
@@ -14,13 +14,25 @@ class MTimerConfigurationManager {
private(set) var currentTime: TimeInterval = 0
}
+// MARK: Getters
+extension MTimerConfigurationManager {
+ func getPublisherTime() -> TimeInterval {
+ publisherTime == 0 ? max(time.start, time.end) : publisherTime
+ }
+ func getTimerProgress() -> Double {
+ let timerTotalTime = max(time.start, time.end) - min(time.start, time.end)
+ let timerRunningTime = abs(currentTime - time.start)
+ return timerRunningTime / timerTotalTime
+ }
+}
+
// MARK: Setters
extension MTimerConfigurationManager {
func setInitialTime(startTime: TimeInterval, endTime: TimeInterval) {
time = (startTime, endTime)
currentTime = startTime
}
- func setInitialPublisher(time: TimeInterval, tolerance: TimeInterval) {
+ func setPublishers(time: TimeInterval, tolerance: TimeInterval) {
publisherTime = time
publisherTimeTolerance = tolerance
}
@@ -44,20 +56,11 @@ extension MTimerConfigurationManager {
currentTime = 0
}
}
-
-// MARK: Getters
-extension MTimerConfigurationManager {
- func getPublisherTime() -> TimeInterval {
- publisherTime == 0 ? max(time.start, time.end) : publisherTime
- }
- func getTimerProgress() -> Double {
- let timerTotalTime = max(time.start, time.end) - min(time.start, time.end)
- let timerRunningTime = abs(currentTime - time.start)
- return timerRunningTime / timerTotalTime
- }
+private extension MTimerConfigurationManager {
+ var timeIncrementMultiplier: Double { time.start > time.end ? -1 : 1 }
}
+// MARK: Helpers
extension MTimerConfigurationManager {
var canTimerBeStarted: Bool { currentTime != time.end }
- var timeIncrementMultiplier: Double { time.start > time.end ? -1 : 1 }
}
diff --git a/Sources/Internal/MTimer.swift b/Sources/Internal/MTimer.swift
index c06d513..cffb786 100644
--- a/Sources/Internal/MTimer.swift
+++ b/Sources/Internal/MTimer.swift
@@ -28,7 +28,7 @@ extension MTimer {
try validator.checkRequirementsForInitializingTimer(publisherTime)
}
func assignInitialPublisherValues(_ time: TimeInterval, _ tolerance: TimeInterval, _ completion: @escaping (MTime) -> ()) {
- configuration.setInitialPublisher(time: time, tolerance: tolerance)
+ configuration.setPublishers(time: time, tolerance: tolerance)
callbacks.onRunningTimeChange = completion
}
}
From 11e712925f5ff88acef0a5bb700f498bb50b5d22 Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Sat, 23 Nov 2024 13:39:18 +0100
Subject: [PATCH 09/45] 1
---
Sources/Internal/Helpers/MTimerContainer.swift | 11 ++++-------
Sources/Internal/Protocols/FactoryInitializable.swift | 4 ++--
Tests/MTimerTests.swift | 2 +-
3 files changed, 7 insertions(+), 10 deletions(-)
diff --git a/Sources/Internal/Helpers/MTimerContainer.swift b/Sources/Internal/Helpers/MTimerContainer.swift
index 1d103fb..fb08c36 100644
--- a/Sources/Internal/Helpers/MTimerContainer.swift
+++ b/Sources/Internal/Helpers/MTimerContainer.swift
@@ -6,17 +6,14 @@
//
@MainActor class MTimerContainer {
- private var timers: [MTimer] = []
- static let shared = MTimerContainer()
-
- private init() { }
+ private static var timers: [MTimer] = []
}
extension MTimerContainer {
- func getTimer(_ id: MTimerID) -> MTimer? { timers.first(where: { $0.id == id }) }
- func register(_ timer: MTimer) { if getTimer(timer.id) == nil { timers.append(timer) }}
+ static func getTimer(_ id: MTimerID) -> MTimer? { timers.first(where: { $0.id == id }) }
+ static func register(_ timer: MTimer) { if getTimer(timer.id) == nil { timers.append(timer) }}
}
extension MTimerContainer {
- func resetAll() { timers.forEach { $0.reset() }}
+ static func resetAll() { timers.forEach { $0.reset() }}
}
diff --git a/Sources/Internal/Protocols/FactoryInitializable.swift b/Sources/Internal/Protocols/FactoryInitializable.swift
index 9bd68c1..6ada06f 100644
--- a/Sources/Internal/Protocols/FactoryInitializable.swift
+++ b/Sources/Internal/Protocols/FactoryInitializable.swift
@@ -13,8 +13,8 @@ import SwiftUI
extension FactoryInitializable where Self: MTimer {
public init(_ id: MTimerID) {
- let timer = MTimerContainer.shared.getTimer(id) ?? MTimer(identifier: id)
- MTimerContainer.shared.register(timer)
+ let timer = MTimerContainer.getTimer(id) ?? MTimer(identifier: id)
+ MTimerContainer.register(timer)
self = timer as! Self
}
}
diff --git a/Tests/MTimerTests.swift b/Tests/MTimerTests.swift
index 90e49e9..ebc4542 100644
--- a/Tests/MTimerTests.swift
+++ b/Tests/MTimerTests.swift
@@ -16,7 +16,7 @@ import XCTest
var currentTime: TimeInterval = 0
override func setUp() async throws {
- MTimerContainer.shared.resetAll()
+ MTimerContainer.resetAll()
}
}
From 3b613c14fcb51cb3511458b5b7a7ec088db1dcf0 Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Sat, 23 Nov 2024 13:42:27 +0100
Subject: [PATCH 10/45] 1
---
Sources/Internal/Helpers/MTimerContainer.swift | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/Sources/Internal/Helpers/MTimerContainer.swift b/Sources/Internal/Helpers/MTimerContainer.swift
index fb08c36..398e4e8 100644
--- a/Sources/Internal/Helpers/MTimerContainer.swift
+++ b/Sources/Internal/Helpers/MTimerContainer.swift
@@ -10,8 +10,16 @@
}
extension MTimerContainer {
- static func getTimer(_ id: MTimerID) -> MTimer? { timers.first(where: { $0.id == id }) }
- static func register(_ timer: MTimer) { if getTimer(timer.id) == nil { timers.append(timer) }}
+ static func getTimer(_ id: MTimerID) -> MTimer? {
+ timers.first(where: { $0.id == id })
+ }
+}
+
+extension MTimerContainer {
+ static func register(_ timer: MTimer) {
+ guard getTimer(timer.id) == nil else { return }
+ timers.append(timer)
+ }
}
extension MTimerContainer {
From dfe6f38b389ec2cd71b82d9bfde7b75ade6026e1 Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Sat, 23 Nov 2024 13:51:20 +0100
Subject: [PATCH 11/45] 1
---
Sources/Internal/Helpers/MTimerStateManager.swift | 7 +++++++
Sources/Internal/MTimer.swift | 6 +-----
2 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/Sources/Internal/Helpers/MTimerStateManager.swift b/Sources/Internal/Helpers/MTimerStateManager.swift
index 491f4f8..fa28d44 100644
--- a/Sources/Internal/Helpers/MTimerStateManager.swift
+++ b/Sources/Internal/Helpers/MTimerStateManager.swift
@@ -15,6 +15,13 @@ class MTimerStateManager {
}
extension MTimerStateManager {
+ func runTimer(_ target: Any, _ timeInterval: TimeInterval, _ selector: Selector) {
+ internalTimer = .scheduledTimer(timeInterval: timeInterval,
+ target: target,
+ selector: selector,
+ userInfo: nil,
+ repeats: true)
+ }
func didEnterBackground() {
internalTimer?.invalidate()
backgroundTransitionDate = .init()
diff --git a/Sources/Internal/MTimer.swift b/Sources/Internal/MTimer.swift
index cffb786..f335561 100644
--- a/Sources/Internal/MTimer.swift
+++ b/Sources/Internal/MTimer.swift
@@ -105,11 +105,7 @@ private extension MTimer {
private extension MTimer {
func updateInternalTimerStart() {
let publisherTime = configuration.getPublisherTime()
- state.internalTimer = .scheduledTimer(timeInterval: publisherTime,
- target: self,
- selector: #selector(handleTimeChange),
- userInfo: nil,
- repeats: true)
+ state.runTimer(self, publisherTime, #selector(handleTimeChange))
state.internalTimer?.tolerance = configuration.publisherTimeTolerance
updateInternalTimerStartAddToRunLoop()
}
From d08a1b8856efe2796453fe751e702b17cb3fb04e Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Sat, 23 Nov 2024 13:55:22 +0100
Subject: [PATCH 12/45] 1
---
Sources/Internal/Helpers/MTimerStateManager.swift | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/Sources/Internal/Helpers/MTimerStateManager.swift b/Sources/Internal/Helpers/MTimerStateManager.swift
index fa28d44..4dc612a 100644
--- a/Sources/Internal/Helpers/MTimerStateManager.swift
+++ b/Sources/Internal/Helpers/MTimerStateManager.swift
@@ -16,12 +16,17 @@ class MTimerStateManager {
extension MTimerStateManager {
func runTimer(_ target: Any, _ timeInterval: TimeInterval, _ selector: Selector) {
- internalTimer = .scheduledTimer(timeInterval: timeInterval,
- target: target,
- selector: selector,
- userInfo: nil,
- repeats: true)
+ internalTimer = .scheduledTimer(
+ timeInterval: timeInterval,
+ target: target,
+ selector: selector,
+ userInfo: nil,
+ repeats: true
+ )
}
+}
+
+extension MTimerStateManager {
func didEnterBackground() {
internalTimer?.invalidate()
backgroundTransitionDate = .init()
From 63992a877295d74eca5c365bb807414042e1933d Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Sat, 23 Nov 2024 13:57:23 +0100
Subject: [PATCH 13/45] 1
---
Sources/Internal/Helpers/MTimerStateManager.swift | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Sources/Internal/Helpers/MTimerStateManager.swift b/Sources/Internal/Helpers/MTimerStateManager.swift
index 4dc612a..12c9ade 100644
--- a/Sources/Internal/Helpers/MTimerStateManager.swift
+++ b/Sources/Internal/Helpers/MTimerStateManager.swift
@@ -15,7 +15,7 @@ class MTimerStateManager {
}
extension MTimerStateManager {
- func runTimer(_ target: Any, _ timeInterval: TimeInterval, _ selector: Selector) {
+ func runTimer(_ target: Any, _ timeInterval: TimeInterval, _ selector: Selector) { // TODO: create separate func to hadnle it
internalTimer = .scheduledTimer(
timeInterval: timeInterval,
target: target,
From b3b90016f4165922bd0474f02dfca3ce48480153 Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Sat, 23 Nov 2024 13:59:15 +0100
Subject: [PATCH 14/45] 1
---
Sources/Internal/Helpers/MTimerStateManager.swift | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/Sources/Internal/Helpers/MTimerStateManager.swift b/Sources/Internal/Helpers/MTimerStateManager.swift
index 12c9ade..00225ae 100644
--- a/Sources/Internal/Helpers/MTimerStateManager.swift
+++ b/Sources/Internal/Helpers/MTimerStateManager.swift
@@ -10,12 +10,10 @@ import SwiftUI
class MTimerStateManager {
var internalTimer: Timer?
var backgroundTransitionDate: Date? = nil
-
- deinit { internalTimer?.invalidate() }
}
extension MTimerStateManager {
- func runTimer(_ target: Any, _ timeInterval: TimeInterval, _ selector: Selector) { // TODO: create separate func to hadnle it
+ func runTimer(_ target: Any, _ timeInterval: TimeInterval, _ selector: Selector) { // TODO: create separate func to handle it
internalTimer = .scheduledTimer(
timeInterval: timeInterval,
target: target,
@@ -26,6 +24,7 @@ extension MTimerStateManager {
}
}
+// MARK: App State Handle
extension MTimerStateManager {
func didEnterBackground() {
internalTimer?.invalidate()
From 6b5f0d405a8a3e023b87370e1b7df3354c1aca94 Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Sat, 23 Nov 2024 14:10:16 +0100
Subject: [PATCH 15/45] 1
---
Sources/Internal/Helpers/MTimerValidator.swift | 10 +++++-----
Sources/Public/Public+MTimer.swift | 1 +
...c+MTimer.Error.swift => Public+MTimerError.swift} | 6 +++---
Tests/MTimerTests.swift | 12 ++++++------
4 files changed, 15 insertions(+), 14 deletions(-)
rename Sources/Public/{Public+MTimer.Error.swift => Public+MTimerError.swift} (82%)
diff --git a/Sources/Internal/Helpers/MTimerValidator.swift b/Sources/Internal/Helpers/MTimerValidator.swift
index cceb809..15d88f4 100644
--- a/Sources/Internal/Helpers/MTimerValidator.swift
+++ b/Sources/Internal/Helpers/MTimerValidator.swift
@@ -9,14 +9,14 @@ import Foundation
class MTimerValidator {
func checkRequirementsForInitializingTimer(_ publisherTime: TimeInterval) throws {
- if publisherTime < 0.001 { throw MTimer.Error.publisherTimeCannotBeLessThanOneMillisecond }
+ if publisherTime < 0.001 { throw MTimerError.publisherTimeCannotBeLessThanOneMillisecond }
}
func checkRequirementsForStartingTimer(_ startTime: TimeInterval, _ endTime: TimeInterval, _ state: MTimerStateManager, _ status: MTimerStatus) throws {
- if startTime < 0 || endTime < 0 { throw MTimer.Error.timeCannotBeLessThanZero }
- if startTime == endTime { throw MTimer.Error.startTimeCannotBeTheSameAsEndTime }
- if status == .inProgress && state.backgroundTransitionDate == nil { throw MTimer.Error.timerIsAlreadyRunning }
+ if startTime < 0 || endTime < 0 { throw MTimerError.timeCannotBeLessThanZero }
+ if startTime == endTime { throw MTimerError.startTimeCannotBeTheSameAsEndTime }
+ if status == .inProgress && state.backgroundTransitionDate == nil { throw MTimerError.timerIsAlreadyRunning }
}
func checkRequirementsForResumingTimer(_ callbacks: MTimerCallbacks) throws {
- if callbacks.onRunningTimeChange == nil { throw MTimer.Error.cannotResumeNotInitialisedTimer }
+ if callbacks.onRunningTimeChange == nil { throw MTimerError.cannotResumeNotInitialisedTimer }
}
}
diff --git a/Sources/Public/Public+MTimer.swift b/Sources/Public/Public+MTimer.swift
index 4e451c4..a53d08b 100644
--- a/Sources/Public/Public+MTimer.swift
+++ b/Sources/Public/Public+MTimer.swift
@@ -21,6 +21,7 @@ public extension MTimer {
/// Prepares the timer to start.
/// WARNING: Use the start() method to start the timer.
func publish(every time: TimeInterval, tolerance: TimeInterval = 0.4, _ completion: @escaping (_ currentTime: MTime) -> ()) throws -> MTimer {
+ // TODO: add and test assert instead of throwing error
try checkRequirementsForInitializingTimer(time)
assignInitialPublisherValues(time, tolerance, completion)
return self
diff --git a/Sources/Public/Public+MTimer.Error.swift b/Sources/Public/Public+MTimerError.swift
similarity index 82%
rename from Sources/Public/Public+MTimer.Error.swift
rename to Sources/Public/Public+MTimerError.swift
index 751e6b2..90c6cee 100644
--- a/Sources/Public/Public+MTimer.Error.swift
+++ b/Sources/Public/Public+MTimerError.swift
@@ -1,5 +1,5 @@
//
-// Public+MTimer.Error.swift of Timer
+// Public+MTimerError.swift of Timer
//
// Created by Tomasz Kurylik
// - Twitter: https://twitter.com/tkurylik
@@ -11,9 +11,9 @@
import Foundation
-extension MTimer { public enum Error: Swift.Error {
+public enum MTimerError: Error {
case publisherTimeCannotBeLessThanOneMillisecond
case startTimeCannotBeTheSameAsEndTime, timeCannotBeLessThanZero
case cannotResumeNotInitialisedTimer
case timerIsAlreadyRunning
-}}
+}
diff --git a/Tests/MTimerTests.swift b/Tests/MTimerTests.swift
index ebc4542..2204fbd 100644
--- a/Tests/MTimerTests.swift
+++ b/Tests/MTimerTests.swift
@@ -259,31 +259,31 @@ extension MTimerTests {
extension MTimerTests {
func testTimerCannotBeInitialised_PublishTimeIsTooLess() {
XCTAssertThrowsError(try timer.publish(every: 0.0001, { _ in })) { error in
- let error = error as! MTimer.Error
+ let error = error as! MTimerError
XCTAssertEqual(error, .publisherTimeCannotBeLessThanOneMillisecond)
}
}
func testTimerDoesNotStart_StartTimeEqualsEndTime() {
XCTAssertThrowsError(try defaultTimer.start(from: 0, to: 0)) { error in
- let error = error as! MTimer.Error
+ let error = error as! MTimerError
XCTAssertEqual(error, .startTimeCannotBeTheSameAsEndTime)
}
}
func testTimerDoesNotStart_StartTimeIsLessThanZero() {
XCTAssertThrowsError(try defaultTimer.start(from: -10, to: 5)) { error in
- let error = error as! MTimer.Error
+ let error = error as! MTimerError
XCTAssertEqual(error, .timeCannotBeLessThanZero)
}
}
func testTimerDoesNotStart_EndTimeIsLessThanZero() {
XCTAssertThrowsError(try defaultTimer.start(from: 10, to: -15)) { error in
- let error = error as! MTimer.Error
+ let error = error as! MTimerError
XCTAssertEqual(error, .timeCannotBeLessThanZero)
}
}
func testCannotResumeTimer_WhenTimerIsNotInitialised() {
XCTAssertThrowsError(try timer.resume()) { error in
- let error = error as! MTimer.Error
+ let error = error as! MTimerError
XCTAssertEqual(error, .cannotResumeNotInitialisedTimer)
}
}
@@ -291,7 +291,7 @@ extension MTimerTests {
try! defaultTimer.start()
XCTAssertThrowsError(try defaultTimer.start()) { error in
- let error = error as! MTimer.Error
+ let error = error as! MTimerError
XCTAssertEqual(error, .timerIsAlreadyRunning)
}
}
From 552a0f8230b8709b3ed827a0f232907446eb261c Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Sat, 23 Nov 2024 14:24:09 +0100
Subject: [PATCH 16/45] 1
---
Sources/Internal/Helpers/MTimerContainer.swift | 14 +++++++-------
.../Internal/Protocols/FactoryInitializable.swift | 6 +++---
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/Sources/Internal/Helpers/MTimerContainer.swift b/Sources/Internal/Helpers/MTimerContainer.swift
index 398e4e8..0700726 100644
--- a/Sources/Internal/Helpers/MTimerContainer.swift
+++ b/Sources/Internal/Helpers/MTimerContainer.swift
@@ -10,15 +10,15 @@
}
extension MTimerContainer {
- static func getTimer(_ id: MTimerID) -> MTimer? {
- timers.first(where: { $0.id == id })
+ static func register(_ timer: MTimer) -> MTimer {
+ if let timer = getTimer(timer.id) { return timer }
+ timers.append(timer)
+ return timer
}
}
-
-extension MTimerContainer {
- static func register(_ timer: MTimer) {
- guard getTimer(timer.id) == nil else { return }
- timers.append(timer)
+private extension MTimerContainer {
+ static func getTimer(_ id: MTimerID) -> MTimer? {
+ timers.first(where: { $0.id == id })
}
}
diff --git a/Sources/Internal/Protocols/FactoryInitializable.swift b/Sources/Internal/Protocols/FactoryInitializable.swift
index 6ada06f..2e7a52e 100644
--- a/Sources/Internal/Protocols/FactoryInitializable.swift
+++ b/Sources/Internal/Protocols/FactoryInitializable.swift
@@ -13,8 +13,8 @@ import SwiftUI
extension FactoryInitializable where Self: MTimer {
public init(_ id: MTimerID) {
- let timer = MTimerContainer.getTimer(id) ?? MTimer(identifier: id)
- MTimerContainer.register(timer)
- self = timer as! Self
+ let timer = MTimer(identifier: id)
+ let registeredTimer = MTimerContainer.register(timer)
+ self = registeredTimer as! Self
}
}
From 7a67e980a6bbbef5242af72bd6733f45581ccfc3 Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Sat, 23 Nov 2024 14:26:17 +0100
Subject: [PATCH 17/45] 1
---
Sources/Public/MTimerID.swift | 2 +-
Tests/MTimerTests.swift | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/Sources/Public/MTimerID.swift b/Sources/Public/MTimerID.swift
index 1737292..6a23c63 100644
--- a/Sources/Public/MTimerID.swift
+++ b/Sources/Public/MTimerID.swift
@@ -5,7 +5,7 @@
// Created by Alina Petrovska on 11.11.2024.
//
-public struct MTimerID: Equatable {
+public struct MTimerID: Equatable, Sendable {
public let rawValue: String
public init(rawValue: String) { self.rawValue = rawValue }
diff --git a/Tests/MTimerTests.swift b/Tests/MTimerTests.swift
index 2204fbd..bc222d9 100644
--- a/Tests/MTimerTests.swift
+++ b/Tests/MTimerTests.swift
@@ -316,8 +316,8 @@ private extension MTimerTests {
var timer: MTimer { .init(.testTimer) }
}
fileprivate extension MTimerID {
- @MainActor static let testTimer: MTimerID = .init(rawValue: "Test timer")
- @MainActor static let timerWithoutPublishers: MTimerID = .init(rawValue: "Timer Without Publishers")
- @MainActor static let stoppableTimer: MTimerID = .init(rawValue: "Stoppable Timer")
- @MainActor static let multipleInstancesTimer: MTimerID = .init(rawValue: "Multiple Instances")
+ static let testTimer: MTimerID = .init(rawValue: "Test timer")
+ static let timerWithoutPublishers: MTimerID = .init(rawValue: "Timer Without Publishers")
+ static let stoppableTimer: MTimerID = .init(rawValue: "Stoppable Timer")
+ static let multipleInstancesTimer: MTimerID = .init(rawValue: "Multiple Instances")
}
From 82e33e642275497260ceab4e0af6834bba274b24 Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Sat, 23 Nov 2024 14:35:27 +0100
Subject: [PATCH 18/45] 1
---
Sources/Public/Public+MTime.swift | 1 +
Tests/MTimerTests.swift | 4 ++--
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/Sources/Public/Public+MTime.swift b/Sources/Public/Public+MTime.swift
index 8f8bc27..3239589 100644
--- a/Sources/Public/Public+MTime.swift
+++ b/Sources/Public/Public+MTime.swift
@@ -23,6 +23,7 @@ extension MTime {
self.init(timeInterval: timeInterval)
}
public init(timeInterval: TimeInterval) {
+ // TODO: Refactor
let millisecondsInt = timeInterval == .infinity ? Int(Self.max.toTimeInterval() * 1000) : Int(timeInterval * 1000)
let hoursDiv = 1000 * 60 * 60
diff --git a/Tests/MTimerTests.swift b/Tests/MTimerTests.swift
index bc222d9..ce2fc54 100644
--- a/Tests/MTimerTests.swift
+++ b/Tests/MTimerTests.swift
@@ -98,7 +98,7 @@ extension MTimerTests {
func testTimerShouldPublishInaccurateValuesWithNonZeroTolerance() {
try! defaultTimer.start()
wait(for: 1)
-
+ // TODO: TIMER run not from main actor / therad. How much takes resources
// usually returns 1.0000000000000002 that is equal to 1.0
// OLD test XCTAssertNotEqual(currentTime, 1)
XCTAssertEqual(currentTime, 1)
@@ -312,7 +312,7 @@ private extension MTimerTests {
}
private extension MTimerTests {
var defaultWaitingTime: TimeInterval { 0.15 }
- var defaultTimer: MTimer { try! timer.publish(every: 0.05, tolerance: 0.5) { self.currentTime = $0.toTimeInterval() } }
+ var defaultTimer: MTimer { try! timer.publish(every: 0.05, tolerance: 20) { self.currentTime = $0.toTimeInterval() } }
var timer: MTimer { .init(.testTimer) }
}
fileprivate extension MTimerID {
From fcd8bd7078e7ef4af026d9e618d20c69b324c777 Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Sat, 23 Nov 2024 14:38:25 +0100
Subject: [PATCH 19/45] 1
---
Sources/Internal/MTimer.swift | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/Sources/Internal/MTimer.swift b/Sources/Internal/MTimer.swift
index f335561..b10cd60 100644
--- a/Sources/Internal/MTimer.swift
+++ b/Sources/Internal/MTimer.swift
@@ -8,16 +8,16 @@
import SwiftUI
public final class MTimer: ObservableObject, FactoryInitializable {
- private let state = MTimerStateManager()
- private let configuration = MTimerConfigurationManager()
- private let validator = MTimerValidator()
+ @Published public private(set) var timerTime: MTime = .init()
+ @Published public private(set) var timerStatus: MTimerStatus = .notStarted
+ @Published public private(set) var timerProgress: Double = 0
let callbacks = MTimerCallbacks()
let id: MTimerID
- @Published public private(set) var timerTime: MTime = .init()
- @Published public private(set) var timerStatus: MTimerStatus = .notStarted // Status not state
- @Published public private(set) var timerProgress: Double = 0
+ private let state = MTimerStateManager()
+ private let configuration = MTimerConfigurationManager()
+ private let validator = MTimerValidator()
init(identifier: MTimerID) { self.id = identifier }
}
From 7b0d42635c1cdda45c5f09355d08803d5f940641 Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Sat, 23 Nov 2024 14:40:09 +0100
Subject: [PATCH 20/45] 1
---
Sources/Internal/Helpers/MTimerValidator.swift | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Sources/Internal/Helpers/MTimerValidator.swift b/Sources/Internal/Helpers/MTimerValidator.swift
index 15d88f4..554e2fe 100644
--- a/Sources/Internal/Helpers/MTimerValidator.swift
+++ b/Sources/Internal/Helpers/MTimerValidator.swift
@@ -8,15 +8,15 @@
import Foundation
class MTimerValidator {
- func checkRequirementsForInitializingTimer(_ publisherTime: TimeInterval) throws {
+ static func checkRequirementsForInitializingTimer(_ publisherTime: TimeInterval) throws {
if publisherTime < 0.001 { throw MTimerError.publisherTimeCannotBeLessThanOneMillisecond }
}
- func checkRequirementsForStartingTimer(_ startTime: TimeInterval, _ endTime: TimeInterval, _ state: MTimerStateManager, _ status: MTimerStatus) throws {
+ static func checkRequirementsForStartingTimer(_ startTime: TimeInterval, _ endTime: TimeInterval, _ state: MTimerStateManager, _ status: MTimerStatus) throws {
if startTime < 0 || endTime < 0 { throw MTimerError.timeCannotBeLessThanZero }
if startTime == endTime { throw MTimerError.startTimeCannotBeTheSameAsEndTime }
if status == .inProgress && state.backgroundTransitionDate == nil { throw MTimerError.timerIsAlreadyRunning }
}
- func checkRequirementsForResumingTimer(_ callbacks: MTimerCallbacks) throws {
+ static func checkRequirementsForResumingTimer(_ callbacks: MTimerCallbacks) throws {
if callbacks.onRunningTimeChange == nil { throw MTimerError.cannotResumeNotInitialisedTimer }
}
}
From af9afadfbd478ebc1937b11ec535f86c0bbcd4e6 Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Sat, 23 Nov 2024 14:40:13 +0100
Subject: [PATCH 21/45] 1
---
Sources/Internal/MTimer.swift | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/Sources/Internal/MTimer.swift b/Sources/Internal/MTimer.swift
index b10cd60..dda95aa 100644
--- a/Sources/Internal/MTimer.swift
+++ b/Sources/Internal/MTimer.swift
@@ -17,7 +17,6 @@ public final class MTimer: ObservableObject, FactoryInitializable {
private let state = MTimerStateManager()
private let configuration = MTimerConfigurationManager()
- private let validator = MTimerValidator()
init(identifier: MTimerID) { self.id = identifier }
}
@@ -25,7 +24,7 @@ public final class MTimer: ObservableObject, FactoryInitializable {
// MARK: - Initialising Timer
extension MTimer {
func checkRequirementsForInitializingTimer(_ publisherTime: TimeInterval) throws {
- try validator.checkRequirementsForInitializingTimer(publisherTime)
+ try MTimerValidator.checkRequirementsForInitializingTimer(publisherTime)
}
func assignInitialPublisherValues(_ time: TimeInterval, _ tolerance: TimeInterval, _ completion: @escaping (MTime) -> ()) {
configuration.setPublishers(time: time, tolerance: tolerance)
@@ -36,7 +35,7 @@ extension MTimer {
// MARK: - Starting Timer
extension MTimer {
func checkRequirementsForStartingTimer(_ startTime: TimeInterval, _ endTime: TimeInterval) throws {
- try validator.checkRequirementsForStartingTimer(startTime, endTime, state, timerStatus)
+ try MTimerValidator.checkRequirementsForStartingTimer(startTime, endTime, state, timerStatus)
}
func assignInitialStartValues(_ startTime: TimeInterval, _ endTime: TimeInterval) {
configuration.setInitialTime(startTime: startTime, endTime: endTime)
@@ -51,7 +50,7 @@ extension MTimer {
// MARK: - Resuming Timer
extension MTimer {
func checkRequirementsForResumingTimer() throws {
- try validator.checkRequirementsForResumingTimer(callbacks)
+ try MTimerValidator.checkRequirementsForResumingTimer(callbacks)
}
}
From 383c2e9d699ec976367e1a4fb243ed21e61b4cc9 Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Sat, 23 Nov 2024 14:46:18 +0100
Subject: [PATCH 22/45] 1
---
Sources/Internal/MTimer.swift | 22 ++++------------------
Sources/Public/Public+MTimer.swift | 8 ++++----
2 files changed, 8 insertions(+), 22 deletions(-)
diff --git a/Sources/Internal/MTimer.swift b/Sources/Internal/MTimer.swift
index dda95aa..68c11a2 100644
--- a/Sources/Internal/MTimer.swift
+++ b/Sources/Internal/MTimer.swift
@@ -12,21 +12,17 @@ public final class MTimer: ObservableObject, FactoryInitializable {
@Published public private(set) var timerStatus: MTimerStatus = .notStarted
@Published public private(set) var timerProgress: Double = 0
- let callbacks = MTimerCallbacks()
let id: MTimerID
-
- private let state = MTimerStateManager()
- private let configuration = MTimerConfigurationManager()
+ let callbacks = MTimerCallbacks()
+ let state = MTimerStateManager()
+ let configuration = MTimerConfigurationManager()
init(identifier: MTimerID) { self.id = identifier }
}
// MARK: - Initialising Timer
extension MTimer {
- func checkRequirementsForInitializingTimer(_ publisherTime: TimeInterval) throws {
- try MTimerValidator.checkRequirementsForInitializingTimer(publisherTime)
- }
- func assignInitialPublisherValues(_ time: TimeInterval, _ tolerance: TimeInterval, _ completion: @escaping (MTime) -> ()) {
+ func setupPublishers(_ time: TimeInterval, _ tolerance: TimeInterval, _ completion: @escaping (MTime) -> ()) {
configuration.setPublishers(time: time, tolerance: tolerance)
callbacks.onRunningTimeChange = completion
}
@@ -34,9 +30,6 @@ extension MTimer {
// MARK: - Starting Timer
extension MTimer {
- func checkRequirementsForStartingTimer(_ startTime: TimeInterval, _ endTime: TimeInterval) throws {
- try MTimerValidator.checkRequirementsForStartingTimer(startTime, endTime, state, timerStatus)
- }
func assignInitialStartValues(_ startTime: TimeInterval, _ endTime: TimeInterval) {
configuration.setInitialTime(startTime: startTime, endTime: endTime)
resetRunningTime()
@@ -47,13 +40,6 @@ extension MTimer {
}
}
-// MARK: - Resuming Timer
-extension MTimer {
- func checkRequirementsForResumingTimer() throws {
- try MTimerValidator.checkRequirementsForResumingTimer(callbacks)
- }
-}
-
// MARK: - Timer State Control
extension MTimer {
func pauseTimer() { handleTimer(status: .paused) }
diff --git a/Sources/Public/Public+MTimer.swift b/Sources/Public/Public+MTimer.swift
index a53d08b..404d70b 100644
--- a/Sources/Public/Public+MTimer.swift
+++ b/Sources/Public/Public+MTimer.swift
@@ -22,8 +22,8 @@ public extension MTimer {
/// WARNING: Use the start() method to start the timer.
func publish(every time: TimeInterval, tolerance: TimeInterval = 0.4, _ completion: @escaping (_ currentTime: MTime) -> ()) throws -> MTimer {
// TODO: add and test assert instead of throwing error
- try checkRequirementsForInitializingTimer(time)
- assignInitialPublisherValues(time, tolerance, completion)
+ try MTimerValidator.checkRequirementsForInitializingTimer(time)
+ setupPublishers(time, tolerance, completion)
return self
}
}
@@ -36,7 +36,7 @@ public extension MTimer {
}
/// Starts the timer using the specified initial values. Can be run backwards - use any "to" value that is greater than "from".
func start(from startTime: TimeInterval = 0, to endTime: TimeInterval = .infinity) throws {
- try checkRequirementsForStartingTimer(startTime, endTime)
+ try MTimerValidator.checkRequirementsForStartingTimer(startTime, endTime, state, timerStatus)
assignInitialStartValues(startTime, endTime)
startTimer()
}
@@ -58,7 +58,7 @@ public extension MTimer {
public extension MTimer {
/// Resumes the stopped timer.
func resume() throws {
- try checkRequirementsForResumingTimer()
+ try MTimerValidator.checkRequirementsForResumingTimer(callbacks)
startTimer()
}
}
From 5a6624d496baa5991ff7ad9759ef6b2ced65750f Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Sun, 24 Nov 2024 21:38:35 +0100
Subject: [PATCH 23/45] 1
---
.../Helpers/MTimerConfigurationManager.swift | 2 +-
.../Internal/Helpers/MTimerStateManager.swift | 31 +++++++++++++++++--
.../Internal/Helpers/MTimerValidator.swift | 2 +-
Sources/Internal/MTimer.swift | 24 ++------------
Sources/Public/Public+MTime.swift | 6 ++--
Sources/Public/Public+MTimer.swift | 8 ++---
Tests/MTimerTests.swift | 8 ++---
7 files changed, 44 insertions(+), 37 deletions(-)
diff --git a/Sources/Internal/Helpers/MTimerConfigurationManager.swift b/Sources/Internal/Helpers/MTimerConfigurationManager.swift
index 3c3c1a6..64704f0 100644
--- a/Sources/Internal/Helpers/MTimerConfigurationManager.swift
+++ b/Sources/Internal/Helpers/MTimerConfigurationManager.swift
@@ -43,7 +43,7 @@ extension MTimerConfigurationManager {
currentTime = time.end
}
func setNewCurrentTime(_ timeChange: Any?) {
- let timeChange = timeChange as? TimeInterval ?? publisherTime
+ let timeChange = timeChange as? TimeInterval ?? getPublisherTime()
let newCurrentTime = currentTime + timeChange * timeIncrementMultiplier
currentTime = timeIncrementMultiplier == -1
? max(newCurrentTime, time.end)
diff --git a/Sources/Internal/Helpers/MTimerStateManager.swift b/Sources/Internal/Helpers/MTimerStateManager.swift
index 00225ae..c7e0eac 100644
--- a/Sources/Internal/Helpers/MTimerStateManager.swift
+++ b/Sources/Internal/Helpers/MTimerStateManager.swift
@@ -8,20 +8,45 @@
import SwiftUI
class MTimerStateManager {
- var internalTimer: Timer?
+ private var internalTimer: Timer?
var backgroundTransitionDate: Date? = nil
}
+// MARK: Run Timer
extension MTimerStateManager {
- func runTimer(_ target: Any, _ timeInterval: TimeInterval, _ selector: Selector) { // TODO: create separate func to handle it
+ func runTimer(_ configuration: MTimerConfigurationManager, _ target: Any, _ completion: Selector) {
+ stopTimer()
+ runTimer(target, configuration.getPublisherTime(), completion)
+ setTolerance(configuration.publisherTimeTolerance)
+ updateInternalTimerStartAddToRunLoop()
+ }
+}
+private extension MTimerStateManager {
+ func runTimer(_ target: Any, _ timeInterval: TimeInterval, _ completion: Selector) {
internalTimer = .scheduledTimer(
timeInterval: timeInterval,
target: target,
- selector: selector,
+ selector: completion,
userInfo: nil,
repeats: true
)
}
+ func setTolerance(_ value: TimeInterval) {
+ internalTimer?.tolerance = value
+ }
+ func updateInternalTimerStartAddToRunLoop() {
+ #if os(macOS)
+ guard let internalTimer = internalTimer else { return }
+ RunLoop.main.add(internalTimer, forMode: .common)
+ #endif
+ }
+}
+
+// MARK: Stop Timer
+extension MTimerStateManager {
+ func stopTimer() {
+ internalTimer?.invalidate()
+ }
}
// MARK: App State Handle
diff --git a/Sources/Internal/Helpers/MTimerValidator.swift b/Sources/Internal/Helpers/MTimerValidator.swift
index 554e2fe..096d1a5 100644
--- a/Sources/Internal/Helpers/MTimerValidator.swift
+++ b/Sources/Internal/Helpers/MTimerValidator.swift
@@ -9,7 +9,7 @@ import Foundation
class MTimerValidator {
static func checkRequirementsForInitializingTimer(_ publisherTime: TimeInterval) throws {
- if publisherTime < 0.001 { throw MTimerError.publisherTimeCannotBeLessThanOneMillisecond }
+ if publisherTime < 0 { throw MTimerError.publisherTimeCannotBeLessThanOneMillisecond }
}
static func checkRequirementsForStartingTimer(_ startTime: TimeInterval, _ endTime: TimeInterval, _ state: MTimerStateManager, _ status: MTimerStatus) throws {
if startTime < 0 || endTime < 0 { throw MTimerError.timeCannotBeLessThanZero }
diff --git a/Sources/Internal/MTimer.swift b/Sources/Internal/MTimer.swift
index 68c11a2..08aba96 100644
--- a/Sources/Internal/MTimer.swift
+++ b/Sources/Internal/MTimer.swift
@@ -12,7 +12,7 @@ public final class MTimer: ObservableObject, FactoryInitializable {
@Published public private(set) var timerStatus: MTimerStatus = .notStarted
@Published public private(set) var timerProgress: Double = 0
- let id: MTimerID
+ public let id: MTimerID
let callbacks = MTimerCallbacks()
let state = MTimerStateManager()
let configuration = MTimerConfigurationManager()
@@ -88,26 +88,8 @@ private extension MTimer {
}
}
private extension MTimer {
- func updateInternalTimerStart() {
- let publisherTime = configuration.getPublisherTime()
- state.runTimer(self, publisherTime, #selector(handleTimeChange))
- state.internalTimer?.tolerance = configuration.publisherTimeTolerance
- updateInternalTimerStartAddToRunLoop()
- }
- func updateInternalTimerStop() {
- state.internalTimer?.invalidate()
- }
-}
-
-private extension MTimer {
- /// **CONTEXT**: On macOS, when the mouse is down in a menu item or other tracking loop, the timer will not start.
- /// **DECISION**: Adding a timer the RunLoop seems to fix the issue issue.
- func updateInternalTimerStartAddToRunLoop() {
- #if os(macOS)
- guard let internalTimer = state.internalTimer else { return }
- RunLoop.main.add(internalTimer, forMode: .common)
- #endif
- }
+ func updateInternalTimerStart() { state.runTimer(configuration, self, #selector(handleTimeChange)) }
+ func updateInternalTimerStop() { state.stopTimer() }
}
// MARK: - Handling Time Change
diff --git a/Sources/Public/Public+MTime.swift b/Sources/Public/Public+MTime.swift
index 3239589..d89b219 100644
--- a/Sources/Public/Public+MTime.swift
+++ b/Sources/Public/Public+MTime.swift
@@ -23,8 +23,7 @@ extension MTime {
self.init(timeInterval: timeInterval)
}
public init(timeInterval: TimeInterval) {
- // TODO: Refactor
- let millisecondsInt = timeInterval == .infinity ? Int(Self.max.toTimeInterval() * 1000) : Int(timeInterval * 1000)
+ let millisecondsInt = timeInterval == .infinity ? Self.maxMilliseconds : Int(timeInterval * 1000)
let hoursDiv = 1000 * 60 * 60
let minutesDiv = 1000 * 60
@@ -39,6 +38,9 @@ extension MTime {
public static var zero: MTime { .init() }
public static var max: MTime { .init(hours: 60 * 60 * 24 * 365 * 100) }
}
+private extension MTime {
+ static var maxMilliseconds: Int { Int(max.toTimeInterval() * 1000) }
+}
// MARK: - Converting to TimeInterval
extension MTime {
diff --git a/Sources/Public/Public+MTimer.swift b/Sources/Public/Public+MTimer.swift
index 404d70b..c019d7a 100644
--- a/Sources/Public/Public+MTimer.swift
+++ b/Sources/Public/Public+MTimer.swift
@@ -18,10 +18,10 @@ public extension MTimer {
func publish(every time: TimeInterval, tolerance: TimeInterval = 0.4, currentTime: Binding) throws -> MTimer {
try publish(every: time, tolerance: tolerance) { currentTime.wrappedValue = $0 }
}
+
/// Prepares the timer to start.
/// WARNING: Use the start() method to start the timer.
func publish(every time: TimeInterval, tolerance: TimeInterval = 0.4, _ completion: @escaping (_ currentTime: MTime) -> ()) throws -> MTimer {
- // TODO: add and test assert instead of throwing error
try MTimerValidator.checkRequirementsForInitializingTimer(time)
setupPublishers(time, tolerance, completion)
return self
@@ -91,14 +91,14 @@ public extension MTimer {
// MARK: - Publishing Timer Activity Status
public extension MTimer {
- /// Publishes the timer activity changes.
- func onTimerActivityChange(_ action: @escaping (_ isRunning: MTimerStatus) -> ()) -> MTimer {
+ /// Publishes the timer status changes.
+ func onTimerStatusChange(_ action: @escaping (_ isRunning: MTimerStatus) -> ()) -> MTimer {
callbacks.onTimerStatusChange = action
return self
}
/// Publishes the timer activity changes.
func bindTimerStatus(isTimerRunning: Binding) -> MTimer {
- onTimerActivityChange { isTimerRunning.wrappedValue = $0 == .inProgress }
+ onTimerStatusChange { isTimerRunning.wrappedValue = $0 == .inProgress }
}
}
diff --git a/Tests/MTimerTests.swift b/Tests/MTimerTests.swift
index ce2fc54..ec4abf7 100644
--- a/Tests/MTimerTests.swift
+++ b/Tests/MTimerTests.swift
@@ -98,9 +98,7 @@ extension MTimerTests {
func testTimerShouldPublishInaccurateValuesWithNonZeroTolerance() {
try! defaultTimer.start()
wait(for: 1)
- // TODO: TIMER run not from main actor / therad. How much takes resources
- // usually returns 1.0000000000000002 that is equal to 1.0
- // OLD test XCTAssertNotEqual(currentTime, 1)
+
XCTAssertEqual(currentTime, 1)
}
func testTimerCanRunBackwards() {
@@ -113,7 +111,7 @@ extension MTimerTests {
var statuses: [MTimerStatus: Bool] = [.inProgress: false, .cancelled: false]
try! defaultTimer
- .onTimerActivityChange { statuses[$0] = true }
+ .onTimerStatusChange { statuses[$0] = true }
.start()
wait(for: defaultWaitingTime)
@@ -258,7 +256,7 @@ extension MTimerTests {
// MARK: - Errors
extension MTimerTests {
func testTimerCannotBeInitialised_PublishTimeIsTooLess() {
- XCTAssertThrowsError(try timer.publish(every: 0.0001, { _ in })) { error in
+ XCTAssertThrowsError(try timer.publish(every: -1, { _ in })) { error in
let error = error as! MTimerError
XCTAssertEqual(error, .publisherTimeCannotBeLessThanOneMillisecond)
}
From 0eaa34ffb52bcfa6e6455eefcf9a2a6af413bf22 Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Mon, 25 Nov 2024 19:05:51 +0100
Subject: [PATCH 24/45] 1
---
.../Internal/Helpers/MTimerValidator.swift | 2 +-
Sources/Internal/MTimer.swift | 11 ++++++
.../Public+MTimerError.swift | 2 +-
.../Enumerations/Public+MTimerStatus.swift | 39 +++++++++++++++++++
Sources/Public/MTimerStatus.swift | 14 -------
.../Public+MTimerID.swift} | 3 +-
Sources/Public/Public+MTime.swift | 5 ++-
Sources/Public/Public+MTimer.swift | 39 ++++++++++++++-----
Tests/MTimerTests.swift | 2 +-
9 files changed, 89 insertions(+), 28 deletions(-)
rename Sources/Public/{ => Enumerations}/Public+MTimerError.swift (89%)
create mode 100644 Sources/Public/Enumerations/Public+MTimerStatus.swift
delete mode 100644 Sources/Public/MTimerStatus.swift
rename Sources/Public/{MTimerID.swift => Models/Public+MTimerID.swift} (67%)
diff --git a/Sources/Internal/Helpers/MTimerValidator.swift b/Sources/Internal/Helpers/MTimerValidator.swift
index 096d1a5..bb93fa8 100644
--- a/Sources/Internal/Helpers/MTimerValidator.swift
+++ b/Sources/Internal/Helpers/MTimerValidator.swift
@@ -9,7 +9,7 @@ import Foundation
class MTimerValidator {
static func checkRequirementsForInitializingTimer(_ publisherTime: TimeInterval) throws {
- if publisherTime < 0 { throw MTimerError.publisherTimeCannotBeLessThanOneMillisecond }
+ if publisherTime < 0 { throw MTimerError.publisherTimeCannotBeLessThanZero }
}
static func checkRequirementsForStartingTimer(_ startTime: TimeInterval, _ endTime: TimeInterval, _ state: MTimerStateManager, _ status: MTimerStatus) throws {
if startTime < 0 || endTime < 0 { throw MTimerError.timeCannotBeLessThanZero }
diff --git a/Sources/Internal/MTimer.swift b/Sources/Internal/MTimer.swift
index 08aba96..54fa487 100644
--- a/Sources/Internal/MTimer.swift
+++ b/Sources/Internal/MTimer.swift
@@ -8,11 +8,22 @@
import SwiftUI
public final class MTimer: ObservableObject, FactoryInitializable {
+ /// Timer time updates publisher.
+ /// - important: The frequency for updating this property can be configured with function ``MTimer/publish(every:tolerance:currentTime:)``
+ /// - NOTE: By default, updates are triggered each time the timer status is marked as **finished**
@Published public private(set) var timerTime: MTime = .init()
+
+ /// Timer status updates publisher.
@Published public private(set) var timerStatus: MTimerStatus = .notStarted
+
+ /// Timer progress publisher.
+ /// - important: The frequency for updating this property can be configured with function ``MTimer/publish(every:tolerance:currentTime:)``
+ /// - NOTE: By default, updates are triggered each time the timer status is marked as **finished**
@Published public private(set) var timerProgress: Double = 0
+ /// Unique id that enables an access to the registered timer from any location.
public let id: MTimerID
+
let callbacks = MTimerCallbacks()
let state = MTimerStateManager()
let configuration = MTimerConfigurationManager()
diff --git a/Sources/Public/Public+MTimerError.swift b/Sources/Public/Enumerations/Public+MTimerError.swift
similarity index 89%
rename from Sources/Public/Public+MTimerError.swift
rename to Sources/Public/Enumerations/Public+MTimerError.swift
index 90c6cee..bbf6b3c 100644
--- a/Sources/Public/Public+MTimerError.swift
+++ b/Sources/Public/Enumerations/Public+MTimerError.swift
@@ -12,7 +12,7 @@
import Foundation
public enum MTimerError: Error {
- case publisherTimeCannotBeLessThanOneMillisecond
+ case publisherTimeCannotBeLessThanZero
case startTimeCannotBeTheSameAsEndTime, timeCannotBeLessThanZero
case cannotResumeNotInitialisedTimer
case timerIsAlreadyRunning
diff --git a/Sources/Public/Enumerations/Public+MTimerStatus.swift b/Sources/Public/Enumerations/Public+MTimerStatus.swift
new file mode 100644
index 0000000..0fd2325
--- /dev/null
+++ b/Sources/Public/Enumerations/Public+MTimerStatus.swift
@@ -0,0 +1,39 @@
+//
+// Public+MTimerStatus.swift
+// MijickTimer
+//
+// Created by Alina Petrovska on 11.11.2024.
+//
+
+public enum MTimerStatus {
+ /// Initial timer state
+ /// ## Triggered by methods
+ /// - ``MTimer/reset()``
+ case notStarted
+
+ /// Timer in progress
+ ///
+ /// ## Triggered by methods
+ /// - ``MTimer/start()``
+ /// - ``MTimer/start(from:to:)-1mvp1``
+ /// - ``MTimer/resume()``
+ case inProgress
+
+ /// Timer was stopped/cancelled
+ ///
+ /// ## Triggered by methods
+ /// - ``MTimer/cancel()``
+ case cancelled
+
+ /// Timer is in a pause
+ ///
+ /// ## Triggered by methods
+ /// - ``MTimer/pause()``
+ case paused
+
+ /// Timer was finished by running out of time or by calling function
+ ///
+ /// ## Triggered by methods
+ /// - ``MTimer/skip()``
+ case finished
+}
diff --git a/Sources/Public/MTimerStatus.swift b/Sources/Public/MTimerStatus.swift
deleted file mode 100644
index d487fa3..0000000
--- a/Sources/Public/MTimerStatus.swift
+++ /dev/null
@@ -1,14 +0,0 @@
-//
-// MTimerStatus.swift
-// MijickTimer
-//
-// Created by Alina Petrovska on 11.11.2024.
-//
-
-public enum MTimerStatus {
- case notStarted
- case inProgress
- case cancelled
- case paused
- case finished
-}
diff --git a/Sources/Public/MTimerID.swift b/Sources/Public/Models/Public+MTimerID.swift
similarity index 67%
rename from Sources/Public/MTimerID.swift
rename to Sources/Public/Models/Public+MTimerID.swift
index 6a23c63..2e582ad 100644
--- a/Sources/Public/MTimerID.swift
+++ b/Sources/Public/Models/Public+MTimerID.swift
@@ -1,10 +1,11 @@
//
-// MTimerID.swift
+// Public+MTimerID.swift
// MijickTimer
//
// Created by Alina Petrovska on 11.11.2024.
//
+/// Unique id that enables an access to the registered timer from any location.
public struct MTimerID: Equatable, Sendable {
public let rawValue: String
diff --git a/Sources/Public/Public+MTime.swift b/Sources/Public/Public+MTime.swift
index d89b219..6b58623 100644
--- a/Sources/Public/Public+MTime.swift
+++ b/Sources/Public/Public+MTime.swift
@@ -44,6 +44,7 @@ private extension MTime {
// MARK: - Converting to TimeInterval
extension MTime {
+ /// Converts MTime values to TimeInterval
public func toTimeInterval() -> TimeInterval {
let hoursAsTimeInterval = 60 * 60 * TimeInterval(hours)
let minutesAsTimeInterval = 60 * TimeInterval(minutes)
@@ -56,7 +57,9 @@ extension MTime {
// MARK: - Converting To String
extension MTime {
- /// Converts the object to a string representation. Output can be customised by modifying the formatter block.
+ /// Converts the object to a string representation. Output can be customized by modifying the formatter block.
+ /// - Parameters:
+ /// - formatter: A formatter that creates string representations of quantities of time
public func toString(_ formatter: (DateComponentsFormatter) -> DateComponentsFormatter = { $0 }) -> String {
formatter(defaultTimeFormatter).string(from: toTimeInterval()) ?? ""
}
diff --git a/Sources/Public/Public+MTimer.swift b/Sources/Public/Public+MTimer.swift
index c019d7a..8b4d188 100644
--- a/Sources/Public/Public+MTimer.swift
+++ b/Sources/Public/Public+MTimer.swift
@@ -13,14 +13,26 @@ import SwiftUI
// MARK: - Initialising Timer
public extension MTimer {
- /// Prepares the timer to start.
- /// WARNING: Use the start() method to start the timer.
+ /// Configure the interval at which the timer's status will be published.
+ ///
+ /// - Parameters:
+ /// - time: the interval of publishing timer state
+ /// - tolerance: The amount of time after the scheduled fire date that the timer may fire.
+ /// - currentTime: Binding value that will be updated every **time** interval
+ ///
+ /// - WARNING: Use the ``start()`` method to start the timer.
func publish(every time: TimeInterval, tolerance: TimeInterval = 0.4, currentTime: Binding) throws -> MTimer {
try publish(every: time, tolerance: tolerance) { currentTime.wrappedValue = $0 }
}
- /// Prepares the timer to start.
- /// WARNING: Use the start() method to start the timer.
+ /// Configure the interval at which the timer's status will be published.
+ ///
+ /// - Parameters:
+ /// - time: the interval of publishing timer state
+ /// - tolerance: The amount of time after the scheduled fire date that the timer may fire.
+ /// - completion: Completion block that will be executed every **time** interval
+ ///
+ /// - WARNING: Use the ``start()`` method to start the timer.
func publish(every time: TimeInterval, tolerance: TimeInterval = 0.4, _ completion: @escaping (_ currentTime: MTime) -> ()) throws -> MTimer {
try MTimerValidator.checkRequirementsForInitializingTimer(time)
setupPublishers(time, tolerance, completion)
@@ -30,17 +42,22 @@ public extension MTimer {
// MARK: - Starting Timer
public extension MTimer {
- /// Starts the timer using the specified initial values. Can be run backwards - use any "to" value that is greater than "from".
+ /// Starts the timer using the specified initial values.
+ ///
+ /// - Note: Can be run backwards - use any **to** value that is greater than **from**.
func start(from startTime: MTime = .zero, to endTime: MTime = .max) throws {
try start(from: startTime.toTimeInterval(), to: endTime.toTimeInterval())
}
- /// Starts the timer using the specified initial values. Can be run backwards - use any "to" value that is greater than "from".
+
+ /// Starts the timer using the specified initial values.
+ ///
+ /// - Note: Can be run backwards - use any **to** value that is greater than **from**.
func start(from startTime: TimeInterval = 0, to endTime: TimeInterval = .infinity) throws {
try MTimerValidator.checkRequirementsForStartingTimer(startTime, endTime, state, timerStatus)
assignInitialStartValues(startTime, endTime)
startTimer()
}
- /// Starts the timer.
+ /// Starts infinity timer
func start() throws {
try start(from: .zero, to: .infinity)
}
@@ -56,7 +73,7 @@ public extension MTimer {
// MARK: - Resuming Timer
public extension MTimer {
- /// Resumes the stopped timer.
+ /// Resumes the paused timer.
func resume() throws {
try MTimerValidator.checkRequirementsForResumingTimer(callbacks)
startTimer()
@@ -82,7 +99,7 @@ public extension MTimer {
// MARK: - Skip Timer
public extension MTimer {
- /// Stops the timer and skips it's condition to the final state.
+ /// Stops the timer and it's condition to the final state.
func skip() {
skipRunningTime()
finishTimer()
@@ -92,11 +109,13 @@ public extension MTimer {
// MARK: - Publishing Timer Activity Status
public extension MTimer {
/// Publishes the timer status changes.
+ /// - Note: To configure the interval at which the timer's status will be published use the method ``publish(every:tolerance:currentTime:)``
func onTimerStatusChange(_ action: @escaping (_ isRunning: MTimerStatus) -> ()) -> MTimer {
callbacks.onTimerStatusChange = action
return self
}
/// Publishes the timer activity changes.
+ /// - Note: To configure the interval at which the timer's status will be published use the method ``publish(every:tolerance:currentTime:)``
func bindTimerStatus(isTimerRunning: Binding) -> MTimer {
onTimerStatusChange { isTimerRunning.wrappedValue = $0 == .inProgress }
}
@@ -105,11 +124,13 @@ public extension MTimer {
// MARK: - Publishing Timer Progress
public extension MTimer {
/// Publishes the timer progress changes.
+ /// - Note: To configure the interval at which the timer's progress will be published use the method ``publish(every:tolerance:currentTime:)``
func onTimerProgressChange(_ action: @escaping (_ progress: Double) -> ()) -> MTimer {
callbacks.onTimerProgressChange = action
return self
}
/// Publishes the timer progress changes.
+ /// - Note: To configure the interval at which the timer's progress will be published use the method ``publish(every:tolerance:currentTime:)``
func bindTimerProgress(progress: Binding) -> MTimer {
onTimerProgressChange { progress.wrappedValue = $0 }
}
diff --git a/Tests/MTimerTests.swift b/Tests/MTimerTests.swift
index ec4abf7..5f6a938 100644
--- a/Tests/MTimerTests.swift
+++ b/Tests/MTimerTests.swift
@@ -258,7 +258,7 @@ extension MTimerTests {
func testTimerCannotBeInitialised_PublishTimeIsTooLess() {
XCTAssertThrowsError(try timer.publish(every: -1, { _ in })) { error in
let error = error as! MTimerError
- XCTAssertEqual(error, .publisherTimeCannotBeLessThanOneMillisecond)
+ XCTAssertEqual(error, .publisherTimeCannotBeLessThanZero)
}
}
func testTimerDoesNotStart_StartTimeEqualsEndTime() {
From f564d37d02cc43a81fc5de9c02529c19757c6689 Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Mon, 25 Nov 2024 19:19:36 +0100
Subject: [PATCH 25/45] 1
---
Sources/Internal/MTimer.swift | 5 +++--
Sources/Internal/Protocols/FactoryInitializable.swift | 5 ++---
Sources/Public/Public+MTimer.swift | 10 +++++-----
3 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/Sources/Internal/MTimer.swift b/Sources/Internal/MTimer.swift
index 54fa487..894305e 100644
--- a/Sources/Internal/MTimer.swift
+++ b/Sources/Internal/MTimer.swift
@@ -8,12 +8,12 @@
import SwiftUI
public final class MTimer: ObservableObject, FactoryInitializable {
- /// Timer time updates publisher.
+ /// Timer time publisher.
/// - important: The frequency for updating this property can be configured with function ``MTimer/publish(every:tolerance:currentTime:)``
/// - NOTE: By default, updates are triggered each time the timer status is marked as **finished**
@Published public private(set) var timerTime: MTime = .init()
- /// Timer status updates publisher.
+ /// Timer status publisher.
@Published public private(set) var timerStatus: MTimerStatus = .notStarted
/// Timer progress publisher.
@@ -36,6 +36,7 @@ extension MTimer {
func setupPublishers(_ time: TimeInterval, _ tolerance: TimeInterval, _ completion: @escaping (MTime) -> ()) {
configuration.setPublishers(time: time, tolerance: tolerance)
callbacks.onRunningTimeChange = completion
+ resetTimerPublishers()
}
}
diff --git a/Sources/Internal/Protocols/FactoryInitializable.swift b/Sources/Internal/Protocols/FactoryInitializable.swift
index 2e7a52e..de384c0 100644
--- a/Sources/Internal/Protocols/FactoryInitializable.swift
+++ b/Sources/Internal/Protocols/FactoryInitializable.swift
@@ -7,11 +7,10 @@
import SwiftUI
-@MainActor public protocol FactoryInitializable {
- init(_ id: MTimerID)
-}
+@MainActor public protocol FactoryInitializable { }
extension FactoryInitializable where Self: MTimer {
+ /// Registers or returns registered Timer
public init(_ id: MTimerID) {
let timer = MTimer(identifier: id)
let registeredTimer = MTimerContainer.register(timer)
diff --git a/Sources/Public/Public+MTimer.swift b/Sources/Public/Public+MTimer.swift
index 8b4d188..a83b3fe 100644
--- a/Sources/Public/Public+MTimer.swift
+++ b/Sources/Public/Public+MTimer.swift
@@ -20,7 +20,7 @@ public extension MTimer {
/// - tolerance: The amount of time after the scheduled fire date that the timer may fire.
/// - currentTime: Binding value that will be updated every **time** interval
///
- /// - WARNING: Use the ``start()`` method to start the timer.
+ /// - WARNING: Use the ``start()`` or ``start(from:to:)-1mvp1`` methods to start the timer.
func publish(every time: TimeInterval, tolerance: TimeInterval = 0.4, currentTime: Binding) throws -> MTimer {
try publish(every: time, tolerance: tolerance) { currentTime.wrappedValue = $0 }
}
@@ -32,7 +32,7 @@ public extension MTimer {
/// - tolerance: The amount of time after the scheduled fire date that the timer may fire.
/// - completion: Completion block that will be executed every **time** interval
///
- /// - WARNING: Use the ``start()`` method to start the timer.
+ /// - WARNING: Use the ``start()`` or ``start(from:to:)-1mvp1`` method to start the timer.
func publish(every time: TimeInterval, tolerance: TimeInterval = 0.4, _ completion: @escaping (_ currentTime: MTime) -> ()) throws -> MTimer {
try MTimerValidator.checkRequirementsForInitializingTimer(time)
setupPublishers(time, tolerance, completion)
@@ -110,14 +110,14 @@ public extension MTimer {
public extension MTimer {
/// Publishes the timer status changes.
/// - Note: To configure the interval at which the timer's status will be published use the method ``publish(every:tolerance:currentTime:)``
- func onTimerStatusChange(_ action: @escaping (_ isRunning: MTimerStatus) -> ()) -> MTimer {
+ func onTimerStatusChange(_ action: @escaping (_ timerStatus: MTimerStatus) -> ()) -> MTimer {
callbacks.onTimerStatusChange = action
return self
}
/// Publishes the timer activity changes.
/// - Note: To configure the interval at which the timer's status will be published use the method ``publish(every:tolerance:currentTime:)``
- func bindTimerStatus(isTimerRunning: Binding) -> MTimer {
- onTimerStatusChange { isTimerRunning.wrappedValue = $0 == .inProgress }
+ func bindTimerStatus(timerStatus: Binding) -> MTimer {
+ onTimerStatusChange { timerStatus.wrappedValue = $0 }
}
}
From 4b11347ccda6b95912f38c69858addea4cf76be1 Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Mon, 25 Nov 2024 19:28:28 +0100
Subject: [PATCH 26/45] 1
---
Sources/Public/Public+MTimer.swift | 43 +++++++++++++++++++++++++-----
1 file changed, 36 insertions(+), 7 deletions(-)
diff --git a/Sources/Public/Public+MTimer.swift b/Sources/Public/Public+MTimer.swift
index a83b3fe..31d05f0 100644
--- a/Sources/Public/Public+MTimer.swift
+++ b/Sources/Public/Public+MTimer.swift
@@ -42,22 +42,51 @@ public extension MTimer {
// MARK: - Starting Timer
public extension MTimer {
- /// Starts the timer using the specified initial values.
- ///
- /// - Note: Can be run backwards - use any **to** value that is greater than **from**.
+ /**
+ Starts the timer using the specified initial values.
+
+ - Note: Can be run backwards - use any **to** value that is greater than **from**.
+
+ ### Up going timer
+ ```swift
+ MTimer(.exampleId)
+ .start(from: .zero, to: .init(seconds: 10))
+ ```
+
+ ### Down going timer
+ ```swift
+ MTimer(.exampleId)
+ .start(from: .init(seconds: 10), to: .zero)
+ ```
+ */
func start(from startTime: MTime = .zero, to endTime: MTime = .max) throws {
try start(from: startTime.toTimeInterval(), to: endTime.toTimeInterval())
}
- /// Starts the timer using the specified initial values.
- ///
- /// - Note: Can be run backwards - use any **to** value that is greater than **from**.
+ /**
+ Starts the timer using the specified initial values.
+
+ - Note: Can be run backwards - use any **to** value that is greater than **from**.
+
+ ### Up going timer
+ ```swift
+ MTimer(.exampleId)
+ .start(from: .zero, to: 10)
+ ```
+
+ ### Down going timer
+ ```swift
+ MTimer(.exampleId)
+ .start(from: 10, to: .zero)
+ ```
+ */
func start(from startTime: TimeInterval = 0, to endTime: TimeInterval = .infinity) throws {
try MTimerValidator.checkRequirementsForStartingTimer(startTime, endTime, state, timerStatus)
assignInitialStartValues(startTime, endTime)
startTimer()
}
- /// Starts infinity timer
+
+ /// Starts up going infinity timer
func start() throws {
try start(from: .zero, to: .infinity)
}
From cb58f85d83c6a233b3ff4d77c15fadc086385811 Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Thu, 28 Nov 2024 11:20:25 +0100
Subject: [PATCH 27/45] 1
---
Sources/Internal/Helpers/MTimerValidator.swift | 4 ++++
Sources/Public/Enumerations/Public+MTimerError.swift | 2 +-
Sources/Public/Public+MTimer.swift | 11 ++++++-----
3 files changed, 11 insertions(+), 6 deletions(-)
diff --git a/Sources/Internal/Helpers/MTimerValidator.swift b/Sources/Internal/Helpers/MTimerValidator.swift
index bb93fa8..3f698fc 100644
--- a/Sources/Internal/Helpers/MTimerValidator.swift
+++ b/Sources/Internal/Helpers/MTimerValidator.swift
@@ -19,4 +19,8 @@ class MTimerValidator {
static func checkRequirementsForResumingTimer(_ callbacks: MTimerCallbacks) throws {
if callbacks.onRunningTimeChange == nil { throw MTimerError.cannotResumeNotInitialisedTimer }
}
+ static func isCanBeSkipped(_ timerStatus: MTimerStatus) throws {
+ if timerStatus == .inProgress || timerStatus == .paused { return }
+ throw MTimerError.timerIsNotStarted
+ }
}
diff --git a/Sources/Public/Enumerations/Public+MTimerError.swift b/Sources/Public/Enumerations/Public+MTimerError.swift
index bbf6b3c..c1da998 100644
--- a/Sources/Public/Enumerations/Public+MTimerError.swift
+++ b/Sources/Public/Enumerations/Public+MTimerError.swift
@@ -15,5 +15,5 @@ public enum MTimerError: Error {
case publisherTimeCannotBeLessThanZero
case startTimeCannotBeTheSameAsEndTime, timeCannotBeLessThanZero
case cannotResumeNotInitialisedTimer
- case timerIsAlreadyRunning
+ case timerIsAlreadyRunning, timerIsNotStarted
}
diff --git a/Sources/Public/Public+MTimer.swift b/Sources/Public/Public+MTimer.swift
index 31d05f0..6713abe 100644
--- a/Sources/Public/Public+MTimer.swift
+++ b/Sources/Public/Public+MTimer.swift
@@ -33,7 +33,7 @@ public extension MTimer {
/// - completion: Completion block that will be executed every **time** interval
///
/// - WARNING: Use the ``start()`` or ``start(from:to:)-1mvp1`` method to start the timer.
- func publish(every time: TimeInterval, tolerance: TimeInterval = 0.4, _ completion: @escaping (_ currentTime: MTime) -> ()) throws -> MTimer {
+ func publish(every time: TimeInterval, tolerance: TimeInterval = 0.4, _ completion: @escaping (_ currentTime: MTime) -> () = { _ in }) throws -> MTimer {
try MTimerValidator.checkRequirementsForInitializingTimer(time)
setupPublishers(time, tolerance, completion)
return self
@@ -50,13 +50,13 @@ public extension MTimer {
### Up going timer
```swift
MTimer(.exampleId)
- .start(from: .zero, to: .init(seconds: 10))
+ .start(from: .zero, to: MTime(seconds: 10))
```
### Down going timer
```swift
MTimer(.exampleId)
- .start(from: .init(seconds: 10), to: .zero)
+ .start(from: MTime(seconds: 10), to: .zero)
```
*/
func start(from startTime: MTime = .zero, to endTime: MTime = .max) throws {
@@ -128,8 +128,9 @@ public extension MTimer {
// MARK: - Skip Timer
public extension MTimer {
- /// Stops the timer and it's condition to the final state.
- func skip() {
+ /// Stops the timer and updates its status to the final state
+ func skip() throws {
+ try MTimerValidator.isCanBeSkipped(timerStatus)
skipRunningTime()
finishTimer()
}
From e4580d22498628e960ed800e727c745ac17a9c92 Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Tue, 3 Dec 2024 12:17:09 +0100
Subject: [PATCH 28/45] Adding some constraints on status change
---
Sources/Internal/MTimer.swift | 4 ++--
Sources/Public/Enumerations/Public+MTimerStatus.swift | 8 ++++++++
Sources/Public/Public+MTimer.swift | 3 +++
Tests/MTimerTests.swift | 2 +-
4 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/Sources/Internal/MTimer.swift b/Sources/Internal/MTimer.swift
index 894305e..dcf4e3a 100644
--- a/Sources/Internal/MTimer.swift
+++ b/Sources/Internal/MTimer.swift
@@ -184,6 +184,6 @@ private extension MTimer {
// MARK: - Helpers
private extension MTimer {
- var isTimerRunning: Bool { timerStatus == .inProgress }
- var isNeededReset: Bool { timerStatus == .finished || timerStatus == .cancelled || timerStatus == .notStarted }
+ var isTimerRunning: Bool { timerStatus.isTimerRunning }
+ var isNeededReset: Bool { timerStatus.isNeededReset }
}
diff --git a/Sources/Public/Enumerations/Public+MTimerStatus.swift b/Sources/Public/Enumerations/Public+MTimerStatus.swift
index 0fd2325..3f7c737 100644
--- a/Sources/Public/Enumerations/Public+MTimerStatus.swift
+++ b/Sources/Public/Enumerations/Public+MTimerStatus.swift
@@ -37,3 +37,11 @@ public enum MTimerStatus {
/// - ``MTimer/skip()``
case finished
}
+
+
+extension MTimerStatus {
+ var isTimerRunning: Bool { self == .inProgress }
+ var isNeededReset: Bool { self == .notStarted || self == .finished || self == .cancelled }
+ var isSkippable: Bool { self == .inProgress || self == .paused }
+ var isCancellable: Bool { self == .inProgress || self == .paused }
+}
diff --git a/Sources/Public/Public+MTimer.swift b/Sources/Public/Public+MTimer.swift
index 6713abe..9f0104f 100644
--- a/Sources/Public/Public+MTimer.swift
+++ b/Sources/Public/Public+MTimer.swift
@@ -96,6 +96,7 @@ public extension MTimer {
public extension MTimer {
/// Pause the timer.
func pause() {
+ guard timerStatus == .inProgress else { return }
pauseTimer()
}
}
@@ -113,6 +114,7 @@ public extension MTimer {
public extension MTimer {
/// Stops the timer and resets its current time to the initial value.
func cancel() {
+ guard timerStatus.isCancellable else { return }
resetRunningTime()
cancelTimer()
}
@@ -130,6 +132,7 @@ public extension MTimer {
public extension MTimer {
/// Stops the timer and updates its status to the final state
func skip() throws {
+ guard timerStatus.isSkippable else { return }
try MTimerValidator.isCanBeSkipped(timerStatus)
skipRunningTime()
finishTimer()
diff --git a/Tests/MTimerTests.swift b/Tests/MTimerTests.swift
index 5f6a938..a16e04a 100644
--- a/Tests/MTimerTests.swift
+++ b/Tests/MTimerTests.swift
@@ -62,7 +62,7 @@ extension MTimerTests {
try! defaultTimer.start(to: endTime)
wait(for: defaultWaitingTime)
- timer.skip()
+ try! timer.skip()
wait(for: defaultWaitingTime)
XCTAssertEqual(endTime, currentTime)
From d426078f5a3f59084ae833f46cda879c22c18049 Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Wed, 4 Dec 2024 14:44:57 +0100
Subject: [PATCH 29/45] 1
---
Sources/Internal/Helpers/MTimerValidator.swift | 4 ++--
Sources/Internal/MTimer.swift | 6 +++---
.../Enumerations/Public+MTimerStatus.swift | 16 ++++------------
Sources/Public/Public+MTimer.swift | 3 +--
Tests/MTimerTests.swift | 8 ++++----
5 files changed, 14 insertions(+), 23 deletions(-)
diff --git a/Sources/Internal/Helpers/MTimerValidator.swift b/Sources/Internal/Helpers/MTimerValidator.swift
index 3f698fc..7887e20 100644
--- a/Sources/Internal/Helpers/MTimerValidator.swift
+++ b/Sources/Internal/Helpers/MTimerValidator.swift
@@ -14,13 +14,13 @@ class MTimerValidator {
static func checkRequirementsForStartingTimer(_ startTime: TimeInterval, _ endTime: TimeInterval, _ state: MTimerStateManager, _ status: MTimerStatus) throws {
if startTime < 0 || endTime < 0 { throw MTimerError.timeCannotBeLessThanZero }
if startTime == endTime { throw MTimerError.startTimeCannotBeTheSameAsEndTime }
- if status == .inProgress && state.backgroundTransitionDate == nil { throw MTimerError.timerIsAlreadyRunning }
+ if status == .running && state.backgroundTransitionDate == nil { throw MTimerError.timerIsAlreadyRunning }
}
static func checkRequirementsForResumingTimer(_ callbacks: MTimerCallbacks) throws {
if callbacks.onRunningTimeChange == nil { throw MTimerError.cannotResumeNotInitialisedTimer }
}
static func isCanBeSkipped(_ timerStatus: MTimerStatus) throws {
- if timerStatus == .inProgress || timerStatus == .paused { return }
+ if timerStatus == .running || timerStatus == .paused { return }
throw MTimerError.timerIsNotStarted
}
}
diff --git a/Sources/Internal/MTimer.swift b/Sources/Internal/MTimer.swift
index dcf4e3a..1cd13d2 100644
--- a/Sources/Internal/MTimer.swift
+++ b/Sources/Internal/MTimer.swift
@@ -48,14 +48,14 @@ extension MTimer {
resetTimerPublishers()
}
func startTimer() {
- handleTimer(status: .inProgress)
+ handleTimer(status: .running)
}
}
// MARK: - Timer State Control
extension MTimer {
func pauseTimer() { handleTimer(status: .paused) }
- func cancelTimer() { handleTimer(status: .cancelled) }
+ func cancelTimer() { handleTimer(status: .notStarted) }
func finishTimer() { handleTimer(status: .finished) }
}
@@ -79,7 +79,7 @@ extension MTimer {
// MARK: - Handling Timer
private extension MTimer {
- func handleTimer(status: MTimerStatus) { if status != .inProgress || configuration.canTimerBeStarted {
+ func handleTimer(status: MTimerStatus) { if status != .running || configuration.canTimerBeStarted {
timerStatus = status
updateInternalTimer(isTimerRunning)
updateObservers(isTimerRunning)
diff --git a/Sources/Public/Enumerations/Public+MTimerStatus.swift b/Sources/Public/Enumerations/Public+MTimerStatus.swift
index 3f7c737..0f1b4c1 100644
--- a/Sources/Public/Enumerations/Public+MTimerStatus.swift
+++ b/Sources/Public/Enumerations/Public+MTimerStatus.swift
@@ -17,13 +17,7 @@ public enum MTimerStatus {
/// - ``MTimer/start()``
/// - ``MTimer/start(from:to:)-1mvp1``
/// - ``MTimer/resume()``
- case inProgress
-
- /// Timer was stopped/cancelled
- ///
- /// ## Triggered by methods
- /// - ``MTimer/cancel()``
- case cancelled
+ case running
/// Timer is in a pause
///
@@ -38,10 +32,8 @@ public enum MTimerStatus {
case finished
}
-
extension MTimerStatus {
- var isTimerRunning: Bool { self == .inProgress }
- var isNeededReset: Bool { self == .notStarted || self == .finished || self == .cancelled }
- var isSkippable: Bool { self == .inProgress || self == .paused }
- var isCancellable: Bool { self == .inProgress || self == .paused }
+ var isTimerRunning: Bool { self == .running }
+ var isNeededReset: Bool { self == .notStarted || self == .finished }
+ var isSkippable: Bool { self == .running || self == .paused }
}
diff --git a/Sources/Public/Public+MTimer.swift b/Sources/Public/Public+MTimer.swift
index 9f0104f..525a3e4 100644
--- a/Sources/Public/Public+MTimer.swift
+++ b/Sources/Public/Public+MTimer.swift
@@ -96,7 +96,7 @@ public extension MTimer {
public extension MTimer {
/// Pause the timer.
func pause() {
- guard timerStatus == .inProgress else { return }
+ guard timerStatus == .running else { return }
pauseTimer()
}
}
@@ -114,7 +114,6 @@ public extension MTimer {
public extension MTimer {
/// Stops the timer and resets its current time to the initial value.
func cancel() {
- guard timerStatus.isCancellable else { return }
resetRunningTime()
cancelTimer()
}
diff --git a/Tests/MTimerTests.swift b/Tests/MTimerTests.swift
index a16e04a..00f5bef 100644
--- a/Tests/MTimerTests.swift
+++ b/Tests/MTimerTests.swift
@@ -27,7 +27,7 @@ extension MTimerTests {
wait(for: defaultWaitingTime)
XCTAssertGreaterThan(currentTime, 0)
- XCTAssertEqual(.inProgress, timer.timerStatus)
+ XCTAssertEqual(.running, timer.timerStatus)
}
func testTimerIsCancellable() {
try! defaultTimer.start()
@@ -40,7 +40,7 @@ extension MTimerTests {
wait(for: defaultWaitingTime)
XCTAssertEqual(timeAfterStop, currentTime)
- XCTAssertEqual(.cancelled, timer.timerStatus)
+ XCTAssertEqual(.notStarted, timer.timerStatus)
}
func testTimerIsResetable() {
let startTime: TimeInterval = 3
@@ -81,7 +81,7 @@ extension MTimerTests {
wait(for: defaultWaitingTime)
XCTAssertNotEqual(timeAfterStop, currentTime)
- XCTAssertEqual(.inProgress, timer.timerStatus)
+ XCTAssertEqual(.running, timer.timerStatus)
}
}
@@ -108,7 +108,7 @@ extension MTimerTests {
XCTAssertLessThan(currentTime, 3)
}
func testTimerPublishesStatuses() {
- var statuses: [MTimerStatus: Bool] = [.inProgress: false, .cancelled: false]
+ var statuses: [MTimerStatus: Bool] = [.running: false, .notStarted: false]
try! defaultTimer
.onTimerStatusChange { statuses[$0] = true }
From 2a224d88c1027992370ae10b1a22de8e573e7756 Mon Sep 17 00:00:00 2001
From: Alina P
Date: Tue, 10 Dec 2024 18:01:41 +0100
Subject: [PATCH 30/45] README Updated
---
README.md | 352 +++++++++++++++++++++++++++++-------------------------
1 file changed, 186 insertions(+), 166 deletions(-)
diff --git a/README.md b/README.md
index 73f49ad..f9c9bee 100644
--- a/README.md
+++ b/README.md
@@ -1,208 +1,228 @@
-
-
+
-
-
-
-
-
+
+ // ADD PUCTURE HERE
+
-
- Modern API for Timer
-
-
- Easy to use yet powerful Timer library. Keep your code clean
+
+
+
Modern API for Timer
+ Easy to use yet powerful Timer library. Keep your code clean
+
- Try demo we prepared
- |
- Roadmap
+ Try demo we prepared
|
- Propose a new feature
+ Framework documentation
+
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-Timer is a free and open-source library dedicated for Swift that makes the process of handling timers easier and much cleaner.
-* **Improves code quality.** Start timer using the `publish().start()` method. Stop the timer with `stop()`. Simple as never.
-* **Run your timer in both directions.** Our Timer can operate in both modes (increasing or decreasing).
-* **Supports background mode.** Don't worry about the timer when the app goes into the background. We handled it!
-* **And much more.** Our library allows you to convert the current time to a string or to display the timer progress in no time.
-
-
-
-# Getting Started
-### ✋ Requirements
-
-| **Platforms** | **Minimum Swift Version** |
-|:----------|:----------|
-| iOS 13+ | 5.0 |
-| iPadOS 13+ | 5.0 |
-| macOS 10.15+ | 5.0 |
-
-### ⏳ Installation
-
-#### [Swift package manager][spm]
-Swift package manager is a tool for automating the distribution of Swift code and is integrated into the Swift compiler.
-
-Once you have your Swift package set up, adding Timer as a dependency is as easy as adding it to the `dependencies` value of your `Package.swift`.
-
-```Swift
-dependencies: [
- .package(url: "https://github.com/Mijick/Timer", branch(“main”))
-]
-```
-
-#### [Cocoapods][cocoapods]
-Cocoapods is a dependency manager for Swift and Objective-C Cocoa projects that helps to scale them elegantly.
-
-Installation steps:
-- Install CocoaPods 1.10.0 (or later)
-- [Generate CocoaPods][generate_cocoapods] for your project
-```Swift
- pod init
-```
-- Add CocoaPods dependency into your `Podfile`
-```Swift
- pod 'MijickTimer'
-```
-- Install dependency and generate `.xcworkspace` file
-```Swift
- pod install
-```
-- Use new XCode project file `.xcworkspace`
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-# Usage
+
+
+# ☀️ Why MijickTimer?
+MijickTimer library it’s a Swift-based library that offers powerful and flexible timer features for iOS and macOS apps. Allows to create both countdown and count-up timers with enhanced state management and observation options.
+
+# Features
+
+
+
+
+
+
+ ⏳
+
+
+ Countdown Timer (Down-Going)
+
+
+
+
+ ⏱️
+
+
+ Count-Up Timer (Elapsed Time)
+
+
+
+
+ ⏸️
+
+
+ Pause Timer
+
+
+
+
+ ▶️
+
+
+ Resume Timer
+
+
+
+
+ ⏭️
+
+
+ Skip Timer
+
+
+
+
+ ⏮️
+
+
+ Cancel Timer
+
+
+
+
+ ⚡
+
+
+ Reactive programming friendly
+
+
+
+
+
+
+
+
Count-Up Timer
+ Track elapsed time seamlessly with a count-up timer. Ideal for productivity, logging, or workout apps.
+ Take a look at the implementation details here .
+
-### 1. Initialise the timer
-Call the `publish()` method that has three parameters:
-* **time** - The number of seconds between firings of the timer.
-* **tolerance** - The number of seconds after the update date that the timer may fire.
-* **currentTime** - The current timer time.
```Swift
- try! MTimer.publish(every: 1, currentTime: $currentTime)
+@MainActor class ViewModel: ObservableObject {
+ @Published var time: TimeInterval = 0
+
+ func startTimer() {
+ try? MTimer(.id)
+ .publish(every: 1, onTimerCallback)
+ .start(from: 0, to: 10)
+ }
+ func onTimerCallback(_ time: MTime) {
+ self.time = time.toTimeInterval()
+ }
+}
```
-### 2. Start the timer
-Start the timer using the `start()` method. You can customise the start and end time using the parameters of this method.
-```Swift
- try! MTimer
- .publish(every: 1, currentTime: $currentTime)
- .start(from: .init(minutes: 21, seconds: 37), to: .zero)
-```
+
+
Countdown Timer
+ Easily create countdown timers to track remaining time. Perfect for games, events, or task timers.
+ Take a look at the implementation details here .
+
-### 3. *(Optional)* Observe TimerStatus and TimerProgress
-You can observe changes in both values by calling either of the methods
```Swift
- try! MTimer
- .publish(every: 1, currentTime: $currentTime)
- .bindTimerStatus(isTimerRunning: $isTimerRunning)
- .bindTimerProgress(progress: $timerProgress)
- .start(from: .init(minutes: 21, seconds: 37), to: .zero)
+func startTimer() {
+ try? MTimer(.id)
+ .start(from: 10, to: 0)
+}
```
-### 4. Stop the timer
-Timer can be stopped with `stop()` method.
-```Swift
- MTimer.stop()
-```
+
+
Control Timer state
+ Pause timers and resume them later without losing progress, skip and cancel.
+ Take a look at the implementation details here .
+
+
-### 5. Additional timer controls
-- Once stopped, the timer can be resumed - simply use the `resume()` method.
```Swift
- try! MTimer.resume()
-```
-- To stop and reset the timer to its initial values, use the `reset()` method.
-```Swift
- MTimer.reset()
-```
+struct ContentView: View {
+ @ObservedObject var timer = MTimer(.id)
-### 6. Displaying the current time as String
-You can convert the current MTime to String by calling the `toString()` method. Use the `formatter` parameter to customise the output.
-```Swift
- currentTime.toString {
- $0.unitsStyle = .full
- $0.allowedUnits = [.hour, .minute]
- return $0
+ var body: some View {
+ (...)
}
+
+ func pause() { timer.pause() }
+ func resume() throws { try timer.resume() }
+ func stop() { timer.cancel() }
+ func skip() throws { try timer.skip() }
+}
```
-### 7. Creating more timer instances
-Create a new instance of the timer and assign it to a new variable. Use the above functions directly with it
-```Swift
- let newTimer = MTimer.createNewInstance()
-
- try! newTimer
- .publish(every: 1, currentTime: $currentTime)
- .start()
-
- newTimer.stop()
-```
-
-
-
-# Try our demo
-See for yourself how does it work by cloning [project][Demo] we created
-
-# License
-Timer is released under the MIT license. See [LICENSE][License] for details.
-
+State Observation Made Easy
+
+
Monitor timer states with a variety of different approaches.
+ Take a look at the implementation details here .
+
+
+
+# ✅ Why Choose This Timer Library?
+**Multiple Apple Platform Support:**
+* Works on iPhone, iPad. Requires iOS 13.0+ .
+* Works on Mac. Requires macOS 10.15+.
+
+**Built for Swift 6:**
+* Modern, efficient, and designed for performance.
+
+**All-in-One Timer Solution:**
+* Handles countdowns, count-ups, pausing, resuming, and state management seamlessly.
+
+**Versatile Observation:**
+* Choose callbacks, bindings, or Combine for the implementation that works best for you.
+
+**It's just a cool library 😎**
-
-# Our other open source SwiftUI libraries
-[PopupView] - The most powerful popup library that allows you to present any popup
-
-[NavigationView] - Easier and cleaner way of navigating through your app
-
-[CalendarView] - Create your own calendar object in no time
-
-[GridView] - Lay out your data with no effort
-
-[CameraView] - The most powerful CameraController. Designed for SwiftUI
+
+# 🔧 Installation
+Follow the [installation guide](https://github.com/Mijick/Timer/wiki/Installation) to integrate the Timer library into your project.
+
+# 🚀 How to use it?
+Visit the framework's [documentation](https://github.com/Mijick/Timer/wiki) to learn how to integrate your project with **MijickTimer**.
+See for yourself how does it work by cloning [project](https://github.com/Mijick/Timer-Demo) we created
+
+# 🍀 Community
+Join the welcoming community of developers on [Discord](https://link.mijick.com/discord).
+
+# 💜 Sponsor our work
+Support our work by [becoming a backer](https://link.mijick.com/buymeacoffee).
-[MIT]: https://en.wikipedia.org/wiki/MIT_License
-[SPM]: https://www.swift.org/package-manager
-[cocoapods]: https://cocoapods.org/
-[generate_cocoapods]: https://github.com/square/cocoapods-generate
-
-[Demo]: https://github.com/Mijick/Timer-Demo
-[License]: https://github.com/Mijick/Timer/blob/main/LICENSE
-
-[PopupView]: https://github.com/Mijick/PopupView
-[NavigationView]: https://github.com/Mijick/NavigationView
-[CalendarView]: https://github.com/Mijick/CalendarView
-[GridView]: https://github.com/Mijick/GridView
-[CameraView]: https://github.com/Mijick/CameraView
From 369af69a525d8b28445eb34c2d5cd1c9a864936d Mon Sep 17 00:00:00 2001
From: Alina P
Date: Tue, 10 Dec 2024 19:56:37 +0100
Subject: [PATCH 31/45] Update LICENSE
---
LICENSE | 222 ++++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 201 insertions(+), 21 deletions(-)
diff --git a/LICENSE b/LICENSE
index ec86490..7aaaeb7 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,21 +1,201 @@
-MIT License
-
-Copyright (c) 2024 Mijick
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright ©2023 Mijick
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
From e0ab6402d1889cc2e268b966fbbbb6c166060d94 Mon Sep 17 00:00:00 2001
From: Alina P
Date: Tue, 10 Dec 2024 19:58:42 +0100
Subject: [PATCH 32/45] Update LICENSE
From 062e3c4d52d37c46a8d28d4664f3190ed1761d0b Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Tue, 10 Dec 2024 20:03:59 +0100
Subject: [PATCH 33/45] 1
---
.github/CODEOWNERS | 1 +
.github/CODE_OF_CONDUCT.md | 128 ++++++++++++++++++
.github/CONTRIBUTING.md | 1 +
.github/ISSUE_TEMPLATE/config.yml | 5 +
.../\360\237\232\200-feature-request.md" | 17 +++
.../\360\237\246\237-bug-report.md" | 41 ++++++
6 files changed, 193 insertions(+)
create mode 100644 .github/CODEOWNERS
create mode 100644 .github/CODE_OF_CONDUCT.md
create mode 100644 .github/CONTRIBUTING.md
create mode 100644 .github/ISSUE_TEMPLATE/config.yml
create mode 100644 ".github/ISSUE_TEMPLATE/\360\237\232\200-feature-request.md"
create mode 100644 ".github/ISSUE_TEMPLATE/\360\237\246\237-bug-report.md"
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 0000000..02e39f6
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1 @@
+* @FulcrumOne
diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..916f081
--- /dev/null
+++ b/.github/CODE_OF_CONDUCT.md
@@ -0,0 +1,128 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, religion, or sexual identity
+and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
+
+## Our Standards
+
+Examples of behavior that contributes to a positive environment for our
+community include:
+
+* Demonstrating empathy and kindness toward other people
+* Being respectful of differing opinions, viewpoints, and experiences
+* Giving and gracefully accepting constructive feedback
+* Accepting responsibility and apologizing to those affected by our mistakes,
+ and learning from the experience
+* Focusing on what is best not just for us as individuals, but for the
+ overall community
+
+Examples of unacceptable behavior include:
+
+* The use of sexualized language or imagery, and sexual attention or
+ advances of any kind
+* Trolling, insulting or derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or email
+ address, without their explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
+
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
+
+## Scope
+
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official e-mail address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported to the community leaders responsible for enforcement at
+team@mijick.com.
+All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series
+of actions.
+
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or
+permanent ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within
+the community.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.0, available at
+https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
+
+Community Impact Guidelines were inspired by [Mozilla's code of conduct
+enforcement ladder](https://github.com/mozilla/diversity).
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see the FAQ at
+https://www.contributor-covenant.org/faq. Translations are available at
+https://www.contributor-covenant.org/translations.
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
new file mode 100644
index 0000000..684e27f
--- /dev/null
+++ b/.github/CONTRIBUTING.md
@@ -0,0 +1 @@
+Coming soon...
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000..d06b778
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,5 @@
+blank_issues_enabled: false
+contact_links:
+ - name: ❓ Help and Support Discord Channel
+ url: https://discord.com/invite/dT5V7nm5SC
+ about: Please ask and answer questions here
diff --git "a/.github/ISSUE_TEMPLATE/\360\237\232\200-feature-request.md" "b/.github/ISSUE_TEMPLATE/\360\237\232\200-feature-request.md"
new file mode 100644
index 0000000..08dc366
--- /dev/null
+++ "b/.github/ISSUE_TEMPLATE/\360\237\232\200-feature-request.md"
@@ -0,0 +1,17 @@
+---
+name: "\U0001F680 Feature Request"
+about: If you have a feature request
+title: "[FREQ]"
+labels: 'state: inactive, type: feature'
+assignees: FulcrumOne
+
+---
+
+## Context
+What are you trying to do and how would you want to do it differently? Is it something you currently you cannot do? Is this related to an issue/problem?
+
+## Alternatives
+Can you achieve the same result doing it in an alternative way? Is the alternative considerable?
+
+## If the feature request is approved, would you be willing to submit a PR?
+Yes / No _(Help can be provided if you need assistance submitting a PR)_
diff --git "a/.github/ISSUE_TEMPLATE/\360\237\246\237-bug-report.md" "b/.github/ISSUE_TEMPLATE/\360\237\246\237-bug-report.md"
new file mode 100644
index 0000000..f6783f4
--- /dev/null
+++ "b/.github/ISSUE_TEMPLATE/\360\237\246\237-bug-report.md"
@@ -0,0 +1,41 @@
+---
+name: "\U0001F99F Bug Report"
+about: If something isn't working
+title: "[BUG]"
+labels: 'state: inactive, type: bug'
+assignees: FulcrumOne, jay-jay-lama
+
+---
+
+## Prerequisites
+- [ ] I checked the [documentation](https://github.com/Mijick/Popups/wiki) and found no answer
+- [ ] I checked to make sure that this issue has not already been filed
+
+## Expected Behavior
+Please describe the behavior you are expecting
+
+## Current Behavior
+What is the current behavior?
+
+## Steps to Reproduce
+Please provide detailed steps for reproducing the issue.
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+## Code Sample
+If you can, please include a code sample that we can use to debug the bug.
+
+## Screenshots
+If applicable, add screenshots to help explain your problem.
+
+## Context
+Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions.
+
+| Name | Version |
+| ------| ---------|
+| SDK | e.g. 3.0.0 |
+| Xcode | e.g. 14.0 |
+| Operating System | e.g. iOS 18.0 |
+| Device | e.g. iPhone 14 Pro |
From bcbaa5d5c9bcc6a8d06e8491c1eaeebb853bb074 Mon Sep 17 00:00:00 2001
From: Alina P
Date: Tue, 10 Dec 2024 20:10:01 +0100
Subject: [PATCH 34/45] Update README.md
---
README.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/README.md b/README.md
index f9c9bee..0514e41 100644
--- a/README.md
+++ b/README.md
@@ -222,6 +222,12 @@ See for yourself how does it work by cloning [project](https://github.com/Mijick
# 🍀 Community
Join the welcoming community of developers on [Discord](https://link.mijick.com/discord).
+
+# 🌼 Contribute
+To contribute a feature or idea to **MijickTimer**, create an [issue](https://github.com/Mijick/Timer/issues/new?assignees=FulcrumOne&labels=state%3A+inactive%2C+type%3A+feature&projects=&template=🚀-feature-request.md&title=%5BFREQ%5D) explaining your idea or bring it up on [Discord](https://discord.com/invite/dT5V7nm5SC).
+If you find a bug, please create an [issue](https://github.com/Mijick/Timer/issues/new?assignees=FulcrumOne%2C+jay-jay-lama&labels=state%3A+inactive%2C+type%3A+bug&projects=&template=🦟-bug-report.md&title=%5BBUG%5D).
+If you would like to contribute, please refer to the [Contribution Guidelines](https://link.mijick.com/contribution-guidelines).
+
# 💜 Sponsor our work
Support our work by [becoming a backer](https://link.mijick.com/buymeacoffee).
From eb3378ea243c931557b9fad97596feb1d7f21a08 Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Wed, 11 Dec 2024 17:30:32 +0100
Subject: [PATCH 35/45] Added visionOS support
---
Package.swift | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/Package.swift b/Package.swift
index 74a0714..b434338 100644
--- a/Package.swift
+++ b/Package.swift
@@ -7,7 +7,8 @@ let package = Package(
name: "MijickTimer",
platforms: [
.iOS(.v13),
- .macOS(.v10_15)
+ .macOS(.v10_15),
+ .visionOS(.v1)
],
products: [
.library(name: "MijickTimer", targets: ["MijickTimer"]),
From 3039bffeb8ac7e63c97e98ca8ebd61ecd7670b7c Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Wed, 11 Dec 2024 17:51:45 +0100
Subject: [PATCH 36/45] 1
---
.../Enumerations/Public+MTimerStatus.swift | 8 ++--
Sources/Public/Public+MTimer.swift | 46 +++++++++----------
2 files changed, 27 insertions(+), 27 deletions(-)
diff --git a/Sources/Public/Enumerations/Public+MTimerStatus.swift b/Sources/Public/Enumerations/Public+MTimerStatus.swift
index 0f1b4c1..963f37e 100644
--- a/Sources/Public/Enumerations/Public+MTimerStatus.swift
+++ b/Sources/Public/Enumerations/Public+MTimerStatus.swift
@@ -6,12 +6,12 @@
//
public enum MTimerStatus {
- /// Initial timer state
+ /// Initial timer status
/// ## Triggered by methods
/// - ``MTimer/reset()``
case notStarted
- /// Timer in progress
+ /// Timer is in a progress
///
/// ## Triggered by methods
/// - ``MTimer/start()``
@@ -19,13 +19,13 @@ public enum MTimerStatus {
/// - ``MTimer/resume()``
case running
- /// Timer is in a pause
+ /// Timer is in a paused state
///
/// ## Triggered by methods
/// - ``MTimer/pause()``
case paused
- /// Timer was finished by running out of time or by calling function
+ /// The timer was terminated by running out of time or calling the function
///
/// ## Triggered by methods
/// - ``MTimer/skip()``
diff --git a/Sources/Public/Public+MTimer.swift b/Sources/Public/Public+MTimer.swift
index 525a3e4..d90a1e2 100644
--- a/Sources/Public/Public+MTimer.swift
+++ b/Sources/Public/Public+MTimer.swift
@@ -13,24 +13,24 @@ import SwiftUI
// MARK: - Initialising Timer
public extension MTimer {
- /// Configure the interval at which the timer's status will be published.
+ /// Configure the interval for publishing the timer status.
///
/// - Parameters:
- /// - time: the interval of publishing timer state
+ /// - time: timer status publishing interval
/// - tolerance: The amount of time after the scheduled fire date that the timer may fire.
- /// - currentTime: Binding value that will be updated every **time** interval
+ /// - currentTime: A binding value that will be updated every **time** interval.
///
/// - WARNING: Use the ``start()`` or ``start(from:to:)-1mvp1`` methods to start the timer.
func publish(every time: TimeInterval, tolerance: TimeInterval = 0.4, currentTime: Binding) throws -> MTimer {
try publish(every: time, tolerance: tolerance) { currentTime.wrappedValue = $0 }
}
- /// Configure the interval at which the timer's status will be published.
+ /// Configure the interval for publishing the timer status.
///
/// - Parameters:
- /// - time: the interval of publishing timer state
+ /// - time: timer status publishing interval
/// - tolerance: The amount of time after the scheduled fire date that the timer may fire.
- /// - completion: Completion block that will be executed every **time** interval
+ /// - completion: A completion block that will be executed every **time** interval
///
/// - WARNING: Use the ``start()`` or ``start(from:to:)-1mvp1`` method to start the timer.
func publish(every time: TimeInterval, tolerance: TimeInterval = 0.4, _ completion: @escaping (_ currentTime: MTime) -> () = { _ in }) throws -> MTimer {
@@ -45,15 +45,15 @@ public extension MTimer {
/**
Starts the timer using the specified initial values.
- - Note: Can be run backwards - use any **to** value that is greater than **from**.
+ - Note: The timer can be run backwards - use any value **to** that is greater than **from**.
- ### Up going timer
+ ### Up-going timer
```swift
MTimer(.exampleId)
.start(from: .zero, to: MTime(seconds: 10))
```
- ### Down going timer
+ ### Down-going timer
```swift
MTimer(.exampleId)
.start(from: MTime(seconds: 10), to: .zero)
@@ -66,15 +66,15 @@ public extension MTimer {
/**
Starts the timer using the specified initial values.
- - Note: Can be run backwards - use any **to** value that is greater than **from**.
+ - Note: The timer can be run backwards - use any value **to** that is greater than **from**.
- ### Up going timer
+ ### Up-going timer
```swift
MTimer(.exampleId)
.start(from: .zero, to: 10)
```
- ### Down going timer
+ ### Down-going timer
```swift
MTimer(.exampleId)
.start(from: 10, to: .zero)
@@ -86,7 +86,7 @@ public extension MTimer {
startTimer()
}
- /// Starts up going infinity timer
+ /// Starts the up-going infinity timer
func start() throws {
try start(from: .zero, to: .infinity)
}
@@ -121,7 +121,7 @@ public extension MTimer {
// MARK: - Aborting Timer
public extension MTimer {
- /// Stops the timer and resets all timer states to default
+ /// Stops the timer and resets all timer states to default values.
func reset() {
resetTimer()
}
@@ -129,7 +129,7 @@ public extension MTimer {
// MARK: - Skip Timer
public extension MTimer {
- /// Stops the timer and updates its status to the final state
+ /// Stops the timer and updates its status to the final state.
func skip() throws {
guard timerStatus.isSkippable else { return }
try MTimerValidator.isCanBeSkipped(timerStatus)
@@ -140,14 +140,14 @@ public extension MTimer {
// MARK: - Publishing Timer Activity Status
public extension MTimer {
- /// Publishes the timer status changes.
- /// - Note: To configure the interval at which the timer's status will be published use the method ``publish(every:tolerance:currentTime:)``
+ /// Publishes timer status changes.
+ /// - Note: To configure the interval at which the state of the timer will be published, use method ``publish(every:tolerance:currentTime:)``
func onTimerStatusChange(_ action: @escaping (_ timerStatus: MTimerStatus) -> ()) -> MTimer {
callbacks.onTimerStatusChange = action
return self
}
- /// Publishes the timer activity changes.
- /// - Note: To configure the interval at which the timer's status will be published use the method ``publish(every:tolerance:currentTime:)``
+ /// Publishes timer status changes.
+ /// - Note: To configure the interval at which the state of the timer will be published, use method ``publish(every:tolerance:currentTime:)``
func bindTimerStatus(timerStatus: Binding) -> MTimer {
onTimerStatusChange { timerStatus.wrappedValue = $0 }
}
@@ -155,14 +155,14 @@ public extension MTimer {
// MARK: - Publishing Timer Progress
public extension MTimer {
- /// Publishes the timer progress changes.
- /// - Note: To configure the interval at which the timer's progress will be published use the method ``publish(every:tolerance:currentTime:)``
+ /// Publishes timer progress changes.
+ /// - Note: To configure the interval at which the timer's progress will be published, use method ``publish(every:tolerance:currentTime:)``
func onTimerProgressChange(_ action: @escaping (_ progress: Double) -> ()) -> MTimer {
callbacks.onTimerProgressChange = action
return self
}
- /// Publishes the timer progress changes.
- /// - Note: To configure the interval at which the timer's progress will be published use the method ``publish(every:tolerance:currentTime:)``
+ /// Publishes timer progress changes.
+ /// - Note: To configure the interval at which the timer's progress will be published, use method ``publish(every:tolerance:currentTime:)``
func bindTimerProgress(progress: Binding) -> MTimer {
onTimerProgressChange { progress.wrappedValue = $0 }
}
From 4fcd6ede7102f1ecb22cb36e32cf4ac1ac08b282 Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Wed, 11 Dec 2024 19:27:51 +0100
Subject: [PATCH 37/45] 1
---
MijickTimer.podspec | 9 +++++----
.../Internal/Extensions/NotificationCenter++.swift | 12 ++++++------
Sources/Internal/Helpers/MTimerCallbacks.swift | 5 ++++-
.../Helpers/MTimerConfigurationManager.swift | 5 ++++-
Sources/Internal/Helpers/MTimerContainer.swift | 5 ++++-
Sources/Internal/Helpers/MTimerStateManager.swift | 5 ++++-
Sources/Internal/Helpers/MTimerValidator.swift | 5 ++++-
Sources/Internal/MTime.swift | 4 ++--
Sources/Internal/MTimer.swift | 5 ++++-
.../Internal/Protocols/FactoryInitializable.swift | 5 ++++-
Sources/Public/Enumerations/Public+MTimerError.swift | 4 ++--
.../Public/Enumerations/Public+MTimerStatus.swift | 5 ++++-
Sources/Public/Models/Public+MTimerID.swift | 5 ++++-
Sources/Public/Public+MTime.swift | 2 +-
Sources/Public/Public+MTimer.swift | 10 +++++-----
15 files changed, 57 insertions(+), 29 deletions(-)
diff --git a/MijickTimer.podspec b/MijickTimer.podspec
index 439a687..f1fe449 100644
--- a/MijickTimer.podspec
+++ b/MijickTimer.podspec
@@ -1,18 +1,19 @@
Pod::Spec.new do |s|
s.name = 'MijickTimer'
s.summary = 'Modern API for Timer'
- s.description = 'MijickTimer is a free, open-source library for the Swift language that makes the process of managing timers much easier and clearer.'
+ s.description = 'Swift library for timers, supporting countdown, count-up, pause, resume, and state management for iOS, macOS and visionOS.'
- s.version = '1.0.2'
+ s.version = '2.0.0'
s.ios.deployment_target = '13.0'
s.osx.deployment_target = '10.15'
- s.swift_version = '5.0'
+ s.visionos.deployment_target = '1.0'
+ s.swift_version = '6.0'
s.source_files = 'Sources/**/*'
s.frameworks = 'SwiftUI', 'Foundation', 'Combine'
s.homepage = 'https://github.com/Mijick/Timer.git'
s.license = { :type => 'MIT', :file => 'LICENSE' }
- s.author = { 'Mijick' => 'team@mijick.com' }
+ s.author = { 'Alina P. from Mijick' => 'alina.petrovska@mijick.com' }
s.source = { :git => 'https://github.com/Mijick/Timer.git', :tag => s.version.to_s }
end
diff --git a/Sources/Internal/Extensions/NotificationCenter++.swift b/Sources/Internal/Extensions/NotificationCenter++.swift
index 0fc4c6f..974bb78 100644
--- a/Sources/Internal/Extensions/NotificationCenter++.swift
+++ b/Sources/Internal/Extensions/NotificationCenter++.swift
@@ -1,12 +1,12 @@
//
-// NotificationCenter++.swift of Timer
+// NotificationCenter++.swift
+// MijickTimer
//
-// Created by Tomasz Kurylik
-// - Twitter: https://twitter.com/tkurylik
-// - Mail: tomasz.kurylik@mijick.com
-// - GitHub: https://github.com/FulcrumOne
+// Created by Alina Petrovska
+// - Mail: alina.petrovska@mijick.com
+// - GitHub: https://github.com/Mijick
//
-// Copyright ©2023 Mijick. Licensed under MIT License.
+// Copyright ©2024 Mijick. All rights reserved.
import SwiftUI
diff --git a/Sources/Internal/Helpers/MTimerCallbacks.swift b/Sources/Internal/Helpers/MTimerCallbacks.swift
index 83c6ff2..70a741e 100644
--- a/Sources/Internal/Helpers/MTimerCallbacks.swift
+++ b/Sources/Internal/Helpers/MTimerCallbacks.swift
@@ -2,8 +2,11 @@
// MTimerCallbacks.swift
// MijickTimer
//
-// Created by Alina Petrovska on 11.11.2024.
+// Created by Alina Petrovska
+// - Mail: alina.petrovska@mijick.com
+// - GitHub: https://github.com/Mijick
//
+// Copyright ©2024 Mijick. All rights reserved.
import SwiftUI
diff --git a/Sources/Internal/Helpers/MTimerConfigurationManager.swift b/Sources/Internal/Helpers/MTimerConfigurationManager.swift
index 64704f0..449524b 100644
--- a/Sources/Internal/Helpers/MTimerConfigurationManager.swift
+++ b/Sources/Internal/Helpers/MTimerConfigurationManager.swift
@@ -2,8 +2,11 @@
// MTimerConfigurationManager.swift
// MijickTimer
//
-// Created by Alina Petrovska on 11.11.2024.
+// Created by Alina Petrovska
+// - Mail: alina.petrovska@mijick.com
+// - GitHub: https://github.com/Mijick
//
+// Copyright ©2024 Mijick. All rights reserved.
import SwiftUI
diff --git a/Sources/Internal/Helpers/MTimerContainer.swift b/Sources/Internal/Helpers/MTimerContainer.swift
index 0700726..c25fcab 100644
--- a/Sources/Internal/Helpers/MTimerContainer.swift
+++ b/Sources/Internal/Helpers/MTimerContainer.swift
@@ -2,8 +2,11 @@
// MTimerContainer.swift
// MijickTimer
//
-// Created by Alina Petrovska on 11.11.2024.
+// Created by Alina Petrovska
+// - Mail: alina.petrovska@mijick.com
+// - GitHub: https://github.com/Mijick
//
+// Copyright ©2024 Mijick. All rights reserved.
@MainActor class MTimerContainer {
private static var timers: [MTimer] = []
diff --git a/Sources/Internal/Helpers/MTimerStateManager.swift b/Sources/Internal/Helpers/MTimerStateManager.swift
index c7e0eac..fbfb135 100644
--- a/Sources/Internal/Helpers/MTimerStateManager.swift
+++ b/Sources/Internal/Helpers/MTimerStateManager.swift
@@ -2,8 +2,11 @@
// MTimerStateManager.swift
// MijickTimer
//
-// Created by Alina Petrovska on 11.11.2024.
+// Created by Alina Petrovska
+// - Mail: alina.petrovska@mijick.com
+// - GitHub: https://github.com/Mijick
//
+// Copyright ©2024 Mijick. All rights reserved.
import SwiftUI
diff --git a/Sources/Internal/Helpers/MTimerValidator.swift b/Sources/Internal/Helpers/MTimerValidator.swift
index 7887e20..697fbdd 100644
--- a/Sources/Internal/Helpers/MTimerValidator.swift
+++ b/Sources/Internal/Helpers/MTimerValidator.swift
@@ -2,8 +2,11 @@
// MTimerValidator.swift
// MijickTimer
//
-// Created by Alina Petrovska on 11.11.2024.
+// Created by Alina Petrovska
+// - Mail: alina.petrovska@mijick.com
+// - GitHub: https://github.com/Mijick
//
+// Copyright ©2024 Mijick. All rights reserved.
import Foundation
diff --git a/Sources/Internal/MTime.swift b/Sources/Internal/MTime.swift
index 2feb36f..6548956 100644
--- a/Sources/Internal/MTime.swift
+++ b/Sources/Internal/MTime.swift
@@ -1,12 +1,12 @@
//
-// MTime.swift of Timer
+// MTime.swift
//
// Created by Tomasz Kurylik
// - Twitter: https://twitter.com/tkurylik
// - Mail: tomasz.kurylik@mijick.com
// - GitHub: https://github.com/FulcrumOne
//
-// Copyright ©2023 Mijick. Licensed under MIT License.
+// Copyright ©2023 Mijick. All rights reserved.
import Foundation
diff --git a/Sources/Internal/MTimer.swift b/Sources/Internal/MTimer.swift
index 1cd13d2..3b9efdc 100644
--- a/Sources/Internal/MTimer.swift
+++ b/Sources/Internal/MTimer.swift
@@ -2,8 +2,11 @@
// MTimer.swift
// MijickTimer
//
-// Created by Alina Petrovska on 11.11.2024.
+// Created by Alina Petrovska
+// - Mail: alina.petrovska@mijick.com
+// - GitHub: https://github.com/Mijick
//
+// Copyright ©2024 Mijick. All rights reserved.
import SwiftUI
diff --git a/Sources/Internal/Protocols/FactoryInitializable.swift b/Sources/Internal/Protocols/FactoryInitializable.swift
index de384c0..78a1220 100644
--- a/Sources/Internal/Protocols/FactoryInitializable.swift
+++ b/Sources/Internal/Protocols/FactoryInitializable.swift
@@ -2,8 +2,11 @@
// FactoryInitializable.swift
// MijickTimer
//
-// Created by Alina Petrovska on 15.11.2024.
+// Created by Alina Petrovska
+// - Mail: alina.petrovska@mijick.com
+// - GitHub: https://github.com/Mijick
//
+// Copyright ©2024 Mijick. All rights reserved.
import SwiftUI
diff --git a/Sources/Public/Enumerations/Public+MTimerError.swift b/Sources/Public/Enumerations/Public+MTimerError.swift
index c1da998..72357a9 100644
--- a/Sources/Public/Enumerations/Public+MTimerError.swift
+++ b/Sources/Public/Enumerations/Public+MTimerError.swift
@@ -1,12 +1,12 @@
//
-// Public+MTimerError.swift of Timer
+// Public+MTimerError.swift
//
// Created by Tomasz Kurylik
// - Twitter: https://twitter.com/tkurylik
// - Mail: tomasz.kurylik@mijick.com
// - GitHub: https://github.com/FulcrumOne
//
-// Copyright ©2023 Mijick. Licensed under MIT License.
+// Copyright ©2023 Mijick. All rights reserved.
import Foundation
diff --git a/Sources/Public/Enumerations/Public+MTimerStatus.swift b/Sources/Public/Enumerations/Public+MTimerStatus.swift
index 963f37e..956012b 100644
--- a/Sources/Public/Enumerations/Public+MTimerStatus.swift
+++ b/Sources/Public/Enumerations/Public+MTimerStatus.swift
@@ -2,8 +2,11 @@
// Public+MTimerStatus.swift
// MijickTimer
//
-// Created by Alina Petrovska on 11.11.2024.
+// Created by Alina Petrovska
+// - Mail: alina.petrovska@mijick.com
+// - GitHub: https://github.com/Mijick
//
+// Copyright ©2024 Mijick. All rights reserved.
public enum MTimerStatus {
/// Initial timer status
diff --git a/Sources/Public/Models/Public+MTimerID.swift b/Sources/Public/Models/Public+MTimerID.swift
index 2e582ad..b48820e 100644
--- a/Sources/Public/Models/Public+MTimerID.swift
+++ b/Sources/Public/Models/Public+MTimerID.swift
@@ -2,8 +2,11 @@
// Public+MTimerID.swift
// MijickTimer
//
-// Created by Alina Petrovska on 11.11.2024.
+// Created by Alina Petrovska
+// - Mail: alina.petrovska@mijick.com
+// - GitHub: https://github.com/Mijick
//
+// Copyright ©2024 Mijick. All rights reserved.
/// Unique id that enables an access to the registered timer from any location.
public struct MTimerID: Equatable, Sendable {
diff --git a/Sources/Public/Public+MTime.swift b/Sources/Public/Public+MTime.swift
index 6b58623..4f1d203 100644
--- a/Sources/Public/Public+MTime.swift
+++ b/Sources/Public/Public+MTime.swift
@@ -6,7 +6,7 @@
// - Mail: tomasz.kurylik@mijick.com
// - GitHub: https://github.com/FulcrumOne
//
-// Copyright ©2023 Mijick. Licensed under MIT License.
+// Copyright ©2023 Mijick. All rights reserved.
import Foundation
diff --git a/Sources/Public/Public+MTimer.swift b/Sources/Public/Public+MTimer.swift
index d90a1e2..a7e661e 100644
--- a/Sources/Public/Public+MTimer.swift
+++ b/Sources/Public/Public+MTimer.swift
@@ -1,12 +1,12 @@
//
// Public+MTimer.swift of Timer
+// MijickTimer
//
-// Created by Tomasz Kurylik
-// - Twitter: https://twitter.com/tkurylik
-// - Mail: tomasz.kurylik@mijick.com
-// - GitHub: https://github.com/FulcrumOne
+// Created by Alina Petrovska
+// - Mail: alina.petrovska@mijick.com
+// - GitHub: https://github.com/Mijick
//
-// Copyright ©2023 Mijick. Licensed under MIT License.
+// Copyright ©2024 Mijick. All rights reserved.
import SwiftUI
From 53ce14e86663c6ccab8f8aeca175388cc81ec03e Mon Sep 17 00:00:00 2001
From: Alina P
Date: Wed, 11 Dec 2024 20:24:28 +0100
Subject: [PATCH 38/45] Update README.md
---
README.md | 35 ++++++++++++++++++++---------------
1 file changed, 20 insertions(+), 15 deletions(-)
diff --git a/README.md b/README.md
index 0514e41..992bcb1 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
- // ADD PUCTURE HERE
+
@@ -60,13 +60,7 @@
-
-
-# ☀️ Why MijickTimer?
-MijickTimer library it’s a Swift-based library that offers powerful and flexible timer features for iOS and macOS apps. Allows to create both countdown and count-up timers with enhanced state management and observation options.
-
# Features
-
@@ -129,7 +123,14 @@ MijickTimer library it’s a Swift-based library that offers powerful and flexib
+
+# ☀️ Why MijickTimer?
+MijickTimer library it’s a Swift-based library that offers powerful and flexible timer features for iOS and macOS apps. Allows to create both countdown and count-up timers with enhanced state management and observation options.
+
+
+
+
Count-Up Timer
Track elapsed time seamlessly with a count-up timer. Ideal for productivity, logging, or workout apps.
@@ -194,21 +195,25 @@ struct ContentView: View {
# ✅ Why Choose This Timer Library?
-**Multiple Apple Platform Support:**
-* Works on iPhone, iPad. Requires iOS 13.0+ .
-* Works on Mac. Requires macOS 10.15+.
+Multiple Apple Platform Support:
+
+* iPhone, iPad. Requires iOS 13.0+ .
+* Mac. Requires macOS 10.15+.
+* Apple Vision Pro. Requires visionOS 1.0+.
-**Built for Swift 6:**
+Built for Swift 6:
+
* Modern, efficient, and designed for performance.
-**All-in-One Timer Solution:**
+All-in-One Timer Solution:
+
* Handles countdowns, count-ups, pausing, resuming, and state management seamlessly.
-**Versatile Observation:**
+Versatile Observation:
+
* Choose callbacks, bindings, or Combine for the implementation that works best for you.
-**It's just a cool library 😎**
-
+It's just a cool library 😎
# 🔧 Installation
From 4ad854e96acf45bf556eee4e36d67d721ed0e04e Mon Sep 17 00:00:00 2001
From: Alina P
Date: Thu, 12 Dec 2024 22:45:07 +0100
Subject: [PATCH 39/45] Update README.md
---
README.md | 62 ++++++++++++++-----------------------------------------
1 file changed, 15 insertions(+), 47 deletions(-)
diff --git a/README.md b/README.md
index 992bcb1..ae5db64 100644
--- a/README.md
+++ b/README.md
@@ -14,9 +14,11 @@
- Try demo we prepared
+ Try demo we prepared
|
- Framework documentation
+ Framework documentation
+ |
+ Roadmap
@@ -125,10 +127,10 @@
-# ☀️ Why MijickTimer?
+# ☀️ What Is MijickTimer?
MijickTimer library it’s a Swift-based library that offers powerful and flexible timer features for iOS and macOS apps. Allows to create both countdown and count-up timers with enhanced state management and observation options.
-
+## 💡 Feature Insights
@@ -136,34 +138,14 @@ MijickTimer library it’s a Swift-based library that offers powerful and flexib
Track elapsed time seamlessly with a count-up timer. Ideal for productivity, logging, or workout apps.
Take a look at the implementation details here .
-
-```Swift
-@MainActor class ViewModel: ObservableObject {
- @Published var time: TimeInterval = 0
-
- func startTimer() {
- try? MTimer(.id)
- .publish(every: 1, onTimerCallback)
- .start(from: 0, to: 10)
- }
- func onTimerCallback(_ time: MTime) {
- self.time = time.toTimeInterval()
- }
-}
-```
+
Countdown Timer
Easily create countdown timers to track remaining time. Perfect for games, events, or task timers.
Take a look at the implementation details here .
-
-```Swift
-func startTimer() {
- try? MTimer(.id)
- .start(from: 10, to: 0)
-}
-```
+
Control Timer state
@@ -171,30 +153,16 @@ func startTimer() {
Take a look at the implementation details here .
+
-```Swift
-struct ContentView: View {
- @ObservedObject var timer = MTimer(.id)
-
- var body: some View {
- (...)
- }
-
- func pause() { timer.pause() }
- func resume() throws { try timer.resume() }
- func stop() { timer.cancel() }
- func skip() throws { try timer.skip() }
-}
-```
-
-State Observation Made Easy
+Observe Timer State
-
Monitor timer states with a variety of different approaches.
+ Monitor timer states with a variety of different approaches: binding, callbacks, combine, state value updates
Take a look at the implementation details here .
-
+
-# ✅ Why Choose This Timer Library?
+# ✅ Why MijickTimer?
Multiple Apple Platform Support:
* iPhone, iPad. Requires iOS 13.0+ .
@@ -220,8 +188,8 @@ struct ContentView: View {
Follow the [installation guide](https://github.com/Mijick/Timer/wiki/Installation) to integrate the Timer library into your project.
# 🚀 How to use it?
-Visit the framework's [documentation](https://github.com/Mijick/Timer/wiki) to learn how to integrate your project with **MijickTimer**.
-See for yourself how does it work by cloning [project](https://github.com/Mijick/Timer-Demo) we created
+Visit the framework's [documentation](https://link.mijick.com/timer-wiki) to learn how to integrate your project with **MijickTimer**.
+See for yourself how does it work by cloning [project](https://link.mijick.com/timer-demo) we created
# 🍀 Community
From 9d819c29a141de38ceec5d47d1f80fa9dfc97a48 Mon Sep 17 00:00:00 2001
From: Alina P
Date: Thu, 12 Dec 2024 23:06:05 +0100
Subject: [PATCH 40/45] Update README.md
---
README.md | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index ae5db64..97c9815 100644
--- a/README.md
+++ b/README.md
@@ -162,6 +162,9 @@ MijickTimer library it’s a Swift-based library that offers powerful and flexib
+
+
+
# ✅ Why MijickTimer?
Multiple Apple Platform Support:
@@ -180,7 +183,8 @@ MijickTimer library it’s a Swift-based library that offers powerful and flexib
Versatile Observation:
* Choose callbacks, bindings, or Combine for the implementation that works best for you.
-
+* Provides the ability to access the state of a specific timer from any part of the code base.
+
It's just a cool library 😎
From 0dc8adfffe641cd7c6572c7ef8eb6e01d94a8401 Mon Sep 17 00:00:00 2001
From: Alina P
Date: Thu, 12 Dec 2024 23:11:39 +0100
Subject: [PATCH 41/45] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 97c9815..1fce99e 100644
--- a/README.md
+++ b/README.md
@@ -145,7 +145,7 @@ MijickTimer library it’s a Swift-based library that offers powerful and flexib
Easily create countdown timers to track remaining time. Perfect for games, events, or task timers.
Take a look at the implementation details here .
-
+
Control Timer state
From 86264cbfe21dd4425140e91d7a4db901d4a02b6a Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Thu, 12 Dec 2024 23:19:22 +0100
Subject: [PATCH 42/45] Issue templates updated
---
".github/ISSUE_TEMPLATE/\360\237\232\200-feature-request.md" | 3 ++-
".github/ISSUE_TEMPLATE/\360\237\246\237-bug-report.md" | 5 +++--
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git "a/.github/ISSUE_TEMPLATE/\360\237\232\200-feature-request.md" "b/.github/ISSUE_TEMPLATE/\360\237\232\200-feature-request.md"
index 08dc366..37c27b9 100644
--- "a/.github/ISSUE_TEMPLATE/\360\237\232\200-feature-request.md"
+++ "b/.github/ISSUE_TEMPLATE/\360\237\232\200-feature-request.md"
@@ -2,7 +2,8 @@
name: "\U0001F680 Feature Request"
about: If you have a feature request
title: "[FREQ]"
-labels: 'state: inactive, type: feature'
+labels: 'feature'
+projects: "Mijick/17"
assignees: FulcrumOne
---
diff --git "a/.github/ISSUE_TEMPLATE/\360\237\246\237-bug-report.md" "b/.github/ISSUE_TEMPLATE/\360\237\246\237-bug-report.md"
index f6783f4..0ccee61 100644
--- "a/.github/ISSUE_TEMPLATE/\360\237\246\237-bug-report.md"
+++ "b/.github/ISSUE_TEMPLATE/\360\237\246\237-bug-report.md"
@@ -2,13 +2,14 @@
name: "\U0001F99F Bug Report"
about: If something isn't working
title: "[BUG]"
-labels: 'state: inactive, type: bug'
+labels: 'bug'
+projects: "Mijick/17"
assignees: FulcrumOne, jay-jay-lama
---
## Prerequisites
-- [ ] I checked the [documentation](https://github.com/Mijick/Popups/wiki) and found no answer
+- [ ] I checked the [documentation](https://github.com/Mijick/Timer/wiki) and found no answer
- [ ] I checked to make sure that this issue has not already been filed
## Expected Behavior
From c76296ac6cf06379dd5721dba84145a927d7ce73 Mon Sep 17 00:00:00 2001
From: Alina P
Date: Thu, 12 Dec 2024 23:42:47 +0100
Subject: [PATCH 43/45] Update README.md
---
README.md | 27 ++++++++++++---------------
1 file changed, 12 insertions(+), 15 deletions(-)
diff --git a/README.md b/README.md
index 1fce99e..8f88f7f 100644
--- a/README.md
+++ b/README.md
@@ -8,8 +8,8 @@
-
Modern API for Timer
- Easy to use yet powerful Timer library. Keep your code clean
+ Timers made simple
+ Easy to use yet powerful Timer library. Keep your code clean.
@@ -62,7 +62,7 @@
-# Features
+# ✨ Features
@@ -128,28 +128,28 @@
# ☀️ What Is MijickTimer?
-MijickTimer library it’s a Swift-based library that offers powerful and flexible timer features for iOS and macOS apps. Allows to create both countdown and count-up timers with enhanced state management and observation options.
+MijickTimer library is Swift-based library that offers powerful and flexible timer features for iOS and macOS and visionOS apps. It allows to create both countdown and count-up timers with enhanced state management and observation options.
-## 💡 Feature Insights
+# 💡 Feature Insights
Count-Up Timer
- Track elapsed time seamlessly with a count-up timer. Ideal for productivity, logging, or workout apps.
+ Track elapsed time seamlessly with a count-up timer. Ideal for productivity, logging or workout apps.
Take a look at the implementation details here .
Countdown Timer
- Easily create countdown timers to track remaining time. Perfect for games, events, or task timers.
+ Easily create countdown timers to track remaining time. Perfect for games, events or task apps.
Take a look at the implementation details here .
Control Timer state
- Pause timers and resume them later without losing progress, skip and cancel.
+ Pause timers and resume them later without losing progress. Also allows to skip and cancel the progress.
Take a look at the implementation details here .
@@ -157,10 +157,10 @@ MijickTimer library it’s a Swift-based library that offers powerful and flexib
Observe Timer State
-
Monitor timer states with a variety of different approaches: binding, callbacks, combine, state value updates
+ Monitor timer state with a variety of different approaches: binding, callbacks, combine, state value updates.
Take a look at the implementation details here .
-
+
@@ -178,19 +178,16 @@ MijickTimer library it’s a Swift-based library that offers powerful and flexib
All-in-One Timer Solution:
-* Handles countdowns, count-ups, pausing, resuming, and state management seamlessly.
+* Handles countdowns, count-ups, pausing, resuming and state management seamlessly.
Versatile Observation:
-* Choose callbacks, bindings, or Combine for the implementation that works best for you.
+* Choose callbacks, bindings or Combine for the implementation that works best for you.
* Provides the ability to access the state of a specific timer from any part of the code base.
It's just a cool library 😎
-# 🔧 Installation
-Follow the [installation guide](https://github.com/Mijick/Timer/wiki/Installation) to integrate the Timer library into your project.
-
# 🚀 How to use it?
Visit the framework's [documentation](https://link.mijick.com/timer-wiki) to learn how to integrate your project with **MijickTimer**.
See for yourself how does it work by cloning [project](https://link.mijick.com/timer-demo) we created
From d612e6128f644f3ec8e2fee8ceee7c106985deb5 Mon Sep 17 00:00:00 2001
From: Alina P
Date: Thu, 12 Dec 2024 23:43:21 +0100
Subject: [PATCH 44/45] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 8f88f7f..c89cac5 100644
--- a/README.md
+++ b/README.md
@@ -149,7 +149,7 @@ MijickTimer library is Swift-based library that offers powerful and flexible tim
Control Timer state
- Pause timers and resume them later without losing progress. Also allows to skip and cancel the progress.
+ Pause timers and resume them later without losing progress. It also allows to skip and cancel the progress.
Take a look at the implementation details here .
From 1d4ab0d0263abbf03734d0d4cccaa8a0324ef5ec Mon Sep 17 00:00:00 2001
From: jay-jay-lama
Date: Thu, 12 Dec 2024 23:46:44 +0100
Subject: [PATCH 45/45] Pod update
---
MijickTimer.podspec | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/MijickTimer.podspec b/MijickTimer.podspec
index f1fe449..f4c23d4 100644
--- a/MijickTimer.podspec
+++ b/MijickTimer.podspec
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|
s.name = 'MijickTimer'
s.summary = 'Modern API for Timer'
- s.description = 'Swift library for timers, supporting countdown, count-up, pause, resume, and state management for iOS, macOS and visionOS.'
+ s.description = 'Timers made simple: The Ultimate Swift Framework for Modern Apps on iOS, macOS, and visionOS.'
s.version = '2.0.0'
s.ios.deployment_target = '13.0'