Files

259 lines
9.6 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// RunLoop+Scheduler.swift
//
//
// Created by Sergej Jaskiewicz on 13.12.2019.
//
import Foundation
import OpenCombine
extension RunLoop {
/// A namespace for disambiguation when both OpenCombine and Combine are imported.
///
/// Foundation overlay for Combine extends `RunLoop` with new methods and nested
/// types.
/// If you import both OpenCombine and Foundation, you will not be able
/// to write `RunLoop.SchedulerTimeType`,
/// because Swift is unable to understand which `SchedulerTimeType`
/// you're referring to.
///
/// So you have to write `RunLoop.OCombine.SchedulerTimeType`.
///
/// This bug is tracked [here](https://bugs.swift.org/browse/SR-11183).
///
/// You can omit this whenever Combine is not available (e. g. on Linux).
public struct OCombine: Scheduler {
public let runLoop: RunLoop
public init(_ runLoop: RunLoop) {
self.runLoop = runLoop
}
/// The scheduler time type used by the run loop.
public struct SchedulerTimeType: Strideable, Codable, Hashable {
/// The date represented by this type.
public var date: Date
/// Initializes a run loop scheduler time with the given date.
///
/// - Parameter date: The date to represent.
public init(_ date: Date) {
self.date = date
}
/// Returns the distance to another run loop scheduler time.
///
/// - Parameter other: Another run loop time.
/// - Returns: The time interval between this time and the provided time.
public func distance(to other: SchedulerTimeType) -> Stride {
let absoluteSelf = date.timeIntervalSinceReferenceDate
let absoluteOther = other.date.timeIntervalSinceReferenceDate
return Stride(absoluteSelf.distance(to: absoluteOther))
}
/// Returns a run loop scheduler time calculated by advancing this instances
/// time by the given interval.
///
/// - Parameter value: A time interval to advance.
/// - Returns: A run loop time advanced by the given interval from this
/// instances time.
public func advanced(by value: Stride) -> SchedulerTimeType {
return SchedulerTimeType(date + value.magnitude)
}
/// The interval by which run loop times advance.
public struct Stride: SchedulerTimeIntervalConvertible,
Comparable,
SignedNumeric,
ExpressibleByFloatLiteral,
Codable {
public typealias FloatLiteralType = TimeInterval
public typealias IntegerLiteralType = TimeInterval
/// A type that can represent the absolute value of any possible value
/// of the conforming type.
public typealias Magnitude = TimeInterval
/// The value of this time interval in seconds.
public var magnitude: TimeInterval
/// The value of this time interval in seconds.
public var timeInterval: TimeInterval { return magnitude }
public init(integerLiteral value: TimeInterval) {
self.magnitude = value
}
public init(floatLiteral value: TimeInterval) {
self.magnitude = value
}
public init(_ timeInterval: TimeInterval) {
self.magnitude = timeInterval
}
public init?<Source: BinaryInteger>(exactly source: Source) {
guard let value = TimeInterval(exactly: source) else { return nil }
magnitude = value
}
public static func < (lhs: Stride, rhs: Stride) -> Bool {
return lhs.magnitude < rhs.magnitude
}
public static func * (lhs: Stride, rhs: Stride) -> Stride {
return Stride(lhs.magnitude * rhs.magnitude)
}
public static func + (lhs: Stride, rhs: Stride) -> Stride {
return Stride(lhs.magnitude + rhs.magnitude)
}
public static func - (lhs: Stride, rhs: Stride) -> Stride {
return Stride(lhs.magnitude - rhs.magnitude)
}
public static func *= (lhs: inout Stride, rhs: Stride) {
lhs.magnitude *= rhs.magnitude
}
public static func += (lhs: inout Stride, rhs: Stride) {
lhs.magnitude += rhs.magnitude
}
public static func -= (lhs: inout Stride, rhs: Stride) {
lhs.magnitude -= rhs.magnitude
}
public static func seconds(_ value: Int) -> Stride {
return Stride(TimeInterval(value))
}
public static func seconds(_ value: Double) -> Stride {
return Stride(TimeInterval(value))
}
public static func milliseconds(_ value: Int) -> Stride {
return Stride(TimeInterval(value) / 1_000)
}
public static func microseconds(_ value: Int) -> Stride {
return Stride(TimeInterval(value) / 1_000_000)
}
public static func nanoseconds(_ value: Int) -> Stride {
return Stride(TimeInterval(value) / 1_000_000_000)
}
}
}
/// Options that affect the operation of the run loop scheduler.
public struct SchedulerOptions {
}
public func schedule(options: SchedulerOptions?, _ action: @escaping () -> Void) {
runLoop.performBlockPortably(action)
}
public func schedule(after date: SchedulerTimeType,
tolerance: SchedulerTimeType.Stride,
options: SchedulerOptions?,
_ action: @escaping () -> Void) {
let timer = Timer(fire: date.date,
interval: 0,
repeats: false,
block: { _ in action() })
timer.tolerance = tolerance.timeInterval
runLoop.add(timer, forMode: .default)
}
public func schedule(after date: SchedulerTimeType,
interval: SchedulerTimeType.Stride,
tolerance: SchedulerTimeType.Stride,
options: SchedulerOptions?,
_ action: @escaping () -> Void) -> Cancellable {
let timer = Timer(fire: date.date,
interval: interval.timeInterval,
repeats: true,
block: { _ in action() })
timer.tolerance = tolerance.timeInterval
runLoop.add(timer, forMode: .default)
return AnyCancellable { timer.invalidate() }
}
public var now: SchedulerTimeType {
return .init(Date())
}
public var minimumTolerance: SchedulerTimeType.Stride {
return .init(0)
}
}
/// A namespace for disambiguation when both OpenCombine and Foundation are imported.
///
/// Foundation overlay for Combine extends `RunLoop` with new methods and nested
/// types.
/// If you import both OpenCombine and Foundation, you will not be able
/// to write `RunLoop.main.schedule { doThings() }`,
/// because Swift is unable to understand which `schedule` method
/// you're referring to.
///
/// So you have to write `RunLoop.main.ocombine.schedule { doThings() }`.
///
/// This bug is tracked [here](https://bugs.swift.org/browse/SR-11183).
///
/// You can omit this whenever Combine is not available (e. g. on Linux).
public var ocombine: OCombine {
return OCombine(self)
}
}
#if !canImport(Combine)
extension RunLoop: OpenCombine.Scheduler {
/// Options that affect the operation of the run loop scheduler.
public typealias SchedulerOptions = OCombine.SchedulerOptions
/// The scheduler time type used by the run loop.
public typealias SchedulerTimeType = OCombine.SchedulerTimeType
public func schedule(options: SchedulerOptions?, _ action: @escaping () -> Void) {
ocombine.schedule(options: options, action)
}
public func schedule(after date: SchedulerTimeType,
tolerance: SchedulerTimeType.Stride,
options: SchedulerOptions?,
_ action: @escaping () -> Void) {
ocombine.schedule(after: date, tolerance: tolerance, options: options, action)
}
public func schedule(after date: SchedulerTimeType,
interval: SchedulerTimeType.Stride,
tolerance: SchedulerTimeType.Stride,
options: SchedulerOptions?,
_ action: @escaping () -> Void) -> Cancellable {
return ocombine.schedule(after: date,
interval: interval,
tolerance: tolerance,
options: options,
action)
}
public var now: SchedulerTimeType {
return ocombine.now
}
public var minimumTolerance: SchedulerTimeType.Stride {
return ocombine.minimumTolerance
}
}
#endif