Files
2017-12-09 22:56:24 +01:00

124 lines
3.3 KiB
Swift
Executable File

//
// PlayerTimer.swift
// MusicPlayer
//
// Created by Michael Row on 2017/9/18.
//
import Foundation
class PlayerTimer {
class Key: Hashable {
var hashValue: Int
init<T: MusicPlayerEX>(_ player: T) {
hashValue = player.hashValue
}
static func ==(lhs: Key, rhs: Key) -> Bool {
return lhs.hashValue == rhs.hashValue
}
}
class Event {
enum Kind {
case Infinite
case Period(Date)
}
typealias Action = (TimeInterval) -> Void
private(set) var action: Action
private(set) var kind: Kind
private(set) var precision: TimeInterval
init(kind: Kind, precision: TimeInterval, action: @escaping Action) {
self.kind = kind
self.precision = precision
self.action = action
}
}
static let shared = PlayerTimer()
private var eventDict = [Key : Event]()
private var timer: Timer?
private init() {}
// MARK: - Public Methods
/// Register player event for periodically callback.
///
/// - Warning: This method is based on Timer, so it's not a real-time mechanism. And the interval of
/// internal timer is equal or less than the precision needed. Note that if the main runloop is busy,
/// it would delay or just skip. The dispatcher would maintain strong reference to the player and closure
/// before you unregistering from it.
func register<T: MusicPlayerEX>(_ player: T, event: Event) {
timer?.invalidate()
eventDict[player.key] = event
fireTimer()
}
/// Unregister player for periodically callback
///
/// - Parameter player: Selected player.
func unregister<T: MusicPlayerEX>(_ player: T) {
timer?.invalidate()
eventDict.removeValue(forKey: player.key)
fireTimer()
}
// MARK: - Private Methods
private func fireTimer() {
guard eventDict.count > 0 else { return }
// get the min time interval
let allEvents = eventDict.values
var minPrecision = allEvents.first!.precision
for event in allEvents {
minPrecision = min(minPrecision, event.precision)
}
// start timer
timer?.invalidate()
timer = Timer(timeInterval: 2, target: self, selector: #selector(notifyAllObservers(_:)), userInfo: nil, repeats: true)
RunLoop.main.add(timer!, forMode: .commonModes)
}
@objc private func notifyAllObservers(_ timer: Timer) {
guard eventDict.count > 0 else {
timer.invalidate()
return
}
let precision = timer.timeInterval
for key in eventDict.keys {
guard let event = eventDict[key] else { continue }
if case let .Period(endDate) = event.kind,
endDate.timeIntervalSinceNow < 0
{
eventDict.removeValue(forKey: key)
continue
}
event.action(precision)
}
}
}
fileprivate extension MusicPlayerEX {
var key: PlayerTimer.Key {
return PlayerTimer.Key(self)
}
}