Skip to content

Commit c648c3e

Browse files
committed
add AnyScheduler
1 parent 9a375e1 commit c648c3e

File tree

10 files changed

+236
-14
lines changed

10 files changed

+236
-14
lines changed

Package.swift

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,38 @@
44
import PackageDescription
55

66
let package = Package(
7-
name: "scheduler-kit",
7+
name: "SchedulerKit",
88
platforms: [
99
.macOS(.v10_15),
1010
.iOS(.v13),
1111
.watchOS(.v6),
1212
.tvOS(.v13)
1313
],
1414
products: [
15-
.library(name: "scheduler-kit", targets: ["scheduler-kit"]),
15+
.library(
16+
name: "SchedulerKit",
17+
type: .dynamic,
18+
targets: ["SchedulerKit"]
19+
),
20+
.library(
21+
name: "SchedulerKitTestUtils",
22+
type: .dynamic,
23+
targets: ["SchedulerKitTestUtils"]
24+
),
1625
],
1726
dependencies: [],
1827
targets: [
1928
.target(
20-
name: "scheduler-kit",
29+
name: "SchedulerKit",
2130
dependencies: []
2231
),
32+
.target(
33+
name: "SchedulerKitTestUtils",
34+
dependencies: ["SchedulerKit"]
35+
),
2336
.testTarget(
24-
name: "scheduler-kitTests",
25-
dependencies: ["scheduler-kit"]
37+
name: "SchedulerKitTests",
38+
dependencies: ["SchedulerKit", "SchedulerKitTestUtils"]
2639
),
2740
]
2841
)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//
2+
// AnyScheduler+Extensions.swift
3+
//
4+
//
5+
// Created by Gleb Radchenko on 16.01.21.
6+
//
7+
8+
import Combine
9+
import Foundation
10+
11+
public extension AnyScheduler where S: DispatchQueue {
12+
static var main: AnyScheduler<DispatchQueue> {
13+
DispatchQueue.main.eraseToAnyScheduler()
14+
}
15+
16+
static func global(qos: DispatchQoS.QoSClass = .default) -> AnyScheduler<DispatchQueue> {
17+
DispatchQueue.global(qos: qos).eraseToAnyScheduler()
18+
}
19+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//
2+
// AnyScheduler.swift
3+
//
4+
//
5+
// Created by Gleb Radchenko on 16.01.21.
6+
//
7+
8+
import Combine
9+
import Foundation
10+
11+
open class AnyScheduler<S: Scheduler>: AnySchedulerBase<S.SchedulerTimeType, S.SchedulerOptions> {}
12+
public typealias DispatchQueueAnyScheduler = AnyScheduler<DispatchQueue>
13+
14+
/// Type erasure for Combine's Scheduler
15+
open class AnySchedulerBase<Time: Strideable, Options>: Scheduler where Time.Stride: SchedulerTimeIntervalConvertible {
16+
public typealias SchedulerTimeType = Time
17+
public typealias SchedulerOptions = Options
18+
19+
private var _now: () -> Time
20+
public var now: Time { _now() }
21+
22+
private var _minimumTolerance: () -> Time.Stride
23+
public var minimumTolerance: Time.Stride { _minimumTolerance() }
24+
25+
private var _schedule: (_ options: Options?, _ action: @escaping () -> Void) -> Void
26+
public func schedule(options: Options?, _ action: @escaping () -> Void) {
27+
_schedule(options, action)
28+
}
29+
30+
private var _scheduleAfter: (_ date: Time, _ tolerance: Time.Stride, _ options: SchedulerOptions?, _ action: @escaping () -> Void) -> Void
31+
public func schedule(after date: Time, tolerance: Time.Stride, options: SchedulerOptions?, _ action: @escaping () -> Void) {
32+
_scheduleAfter(date, tolerance, options, action)
33+
}
34+
35+
private var _scheduleAfterCancellable: (_ date: SchedulerTimeType, _ interval: SchedulerTimeType.Stride, _ tolerance: SchedulerTimeType.Stride, _ options: SchedulerOptions?, _ action: @escaping () -> Void) -> Cancellable
36+
public func schedule(after date: SchedulerTimeType, interval: SchedulerTimeType.Stride, tolerance: SchedulerTimeType.Stride, options: SchedulerOptions?, _ action: @escaping () -> Void) -> Cancellable {
37+
_scheduleAfterCancellable(date, interval, tolerance, options, action)
38+
}
39+
40+
public init<S: Scheduler>(scheduler: S) where S.SchedulerTimeType == Time, S.SchedulerOptions == Options {
41+
_now = { scheduler.now }
42+
_minimumTolerance = { scheduler.minimumTolerance }
43+
_schedule = { options, action in scheduler.schedule(options: options, action) }
44+
_scheduleAfter = { date, tolerance, options, action in
45+
scheduler.schedule(after: date, tolerance: tolerance, options: options, action)
46+
}
47+
_scheduleAfterCancellable = { date, interval, tolerance, options, action in
48+
scheduler.schedule(after: date, interval: interval, tolerance: tolerance, options: options, action)
49+
}
50+
}
51+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//
2+
// Scheduler+Extensions.swift
3+
//
4+
//
5+
// Created by Gleb Radchenko on 16.01.21.
6+
//
7+
8+
import Combine
9+
import Foundation
10+
11+
public extension Scheduler {
12+
func eraseToAnyScheduler<S: Scheduler>() -> AnyScheduler<S> where S.SchedulerOptions == SchedulerOptions, S.SchedulerTimeType == SchedulerTimeType {
13+
AnyScheduler(scheduler: self)
14+
}
15+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//
2+
// AnyScheduler+Extensions.swift
3+
//
4+
//
5+
// Created by Gleb Radchenko on 16.01.21.
6+
//
7+
8+
import Combine
9+
import Foundation
10+
import SchedulerKit
11+
12+
public extension AnyScheduler where S: DispatchQueue {
13+
static var test: AnyScheduler<DispatchQueue> {
14+
TestScheduler().eraseToAnyScheduler()
15+
}
16+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
//
2+
// TestScheduler.swift
3+
//
4+
//
5+
// Created by Gleb Radchenko on 16.01.21.
6+
//
7+
8+
import Combine
9+
import Foundation
10+
import SchedulerKit
11+
12+
open class TestScheduler: Scheduler {
13+
public typealias SchedulerTimeType = DispatchQueue.SchedulerTimeType
14+
public typealias SchedulerOptions = DispatchQueue.SchedulerOptions
15+
16+
public init() {}
17+
18+
/// For delayed actions with interval scheduler needs to be advanced before action execution
19+
public var enableImmediateExecutionIfPossible = true
20+
21+
public func advance(by interval: DispatchQueue.SchedulerTimeType.Stride) {
22+
_now = _now.advanced(by: interval)
23+
}
24+
25+
public var _now: DispatchQueue.SchedulerTimeType = .init(.now())
26+
public var now: DispatchQueue.SchedulerTimeType { _now }
27+
28+
public var _minimumTolerance: DispatchQueue.SchedulerTimeType.Stride = .zero
29+
public var minimumTolerance: DispatchQueue.SchedulerTimeType.Stride { _minimumTolerance }
30+
31+
public var immediateAction: [ImmediateAction] = []
32+
public func schedule(options: DispatchQueue.SchedulerOptions?, _ action: @escaping () -> Void) {
33+
immediateAction.append(
34+
ImmediateAction(options: options, action: action)
35+
)
36+
37+
if enableImmediateExecutionIfPossible {
38+
action()
39+
}
40+
}
41+
42+
public var delayedActions: [DelayedAction] = []
43+
public func schedule(
44+
after: SchedulerTimeType,
45+
tolerance: SchedulerTimeType.Stride,
46+
options: SchedulerOptions?,
47+
_ action: @escaping () -> Void
48+
) {
49+
delayedActions.append(
50+
DelayedAction(
51+
after: after,
52+
tolerance: tolerance,
53+
options: options,
54+
action: action
55+
)
56+
)
57+
58+
if enableImmediateExecutionIfPossible {
59+
action()
60+
}
61+
}
62+
63+
public var delayedIntervalActions: [DelayedIntervalAction] = []
64+
public func schedule(
65+
after: SchedulerTimeType,
66+
interval: SchedulerTimeType.Stride,
67+
tolerance: SchedulerTimeType.Stride,
68+
options : SchedulerOptions?,
69+
_ action: @escaping () -> Void
70+
) -> Cancellable {
71+
let delayedIntervalAction = DelayedIntervalAction(
72+
after: after,
73+
interval: interval,
74+
tolerance: tolerance,
75+
options: options,
76+
action: action
77+
)
78+
delayedIntervalActions.append(delayedIntervalAction)
79+
return AnyCancellable { [weak self] in
80+
self?.delayedIntervalActions.removeAll(where: { $0.uuid == delayedIntervalAction.uuid })
81+
}
82+
}
83+
}
84+
85+
public extension TestScheduler {
86+
struct ImmediateAction {
87+
fileprivate let uuid = UUID()
88+
89+
public let options: DispatchQueue.SchedulerOptions?
90+
public let action: () -> Void
91+
}
92+
93+
struct DelayedAction {
94+
fileprivate let uuid = UUID()
95+
96+
public let after: SchedulerTimeType
97+
public let tolerance: SchedulerTimeType.Stride
98+
public let options: SchedulerOptions?
99+
public let action: () -> Void
100+
}
101+
102+
struct DelayedIntervalAction {
103+
fileprivate let uuid = UUID()
104+
105+
public let after: SchedulerTimeType
106+
public let interval: SchedulerTimeType.Stride
107+
public let tolerance: SchedulerTimeType.Stride
108+
public let options: SchedulerOptions?
109+
public let action: () -> Void
110+
}
111+
}

Sources/scheduler-kit/scheduler_kit.swift

Lines changed: 0 additions & 3 deletions
This file was deleted.

Tests/LinuxMain.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import XCTest
22

3-
import scheduler_kitTests
3+
import SchedulerKitTests
44

55
var tests = [XCTestCaseEntry]()
6-
tests += scheduler_kitTests.allTests()
6+
tests += SchedulerKitTests.allTests()
77
XCTMain(tests)

Tests/scheduler-kitTests/scheduler_kitTests.swift renamed to Tests/SchedulerKitTests/SchedulerKitTests.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import XCTest
2-
@testable import scheduler_kit
2+
@testable import SchedulerKit
33

4-
final class scheduler_kitTests: XCTestCase {
4+
final class SchedulerKitTests: XCTestCase {
55
func testExample() {
66
// This is an example of a functional test case.
77
// Use XCTAssert and related functions to verify your tests produce the correct
88
// results.
9-
XCTAssertEqual(scheduler_kit().text, "Hello, World!")
9+
XCTAssertTrue(true)
1010
}
1111

1212
static var allTests = [

Tests/scheduler-kitTests/XCTestManifests.swift renamed to Tests/SchedulerKitTests/XCTestManifests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import XCTest
33
#if !canImport(ObjectiveC)
44
public func allTests() -> [XCTestCaseEntry] {
55
return [
6-
testCase(scheduler_kitTests.allTests),
6+
testCase(SchedulerKitTests.allTests),
77
]
88
}
99
#endif

0 commit comments

Comments
 (0)