forked from ReactiveCocoa/ReactiveSwift
-
Notifications
You must be signed in to change notification settings - Fork 0
/
FoundationExtensions.swift
143 lines (130 loc) · 4.62 KB
/
FoundationExtensions.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//
// FoundationExtensions.swift
// ReactiveSwift
//
// Created by Justin Spahr-Summers on 2014-10-19.
// Copyright (c) 2014 GitHub. All rights reserved.
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
import Dispatch
#if os(Linux)
import let CDispatch.NSEC_PER_USEC
import let CDispatch.NSEC_PER_SEC
#endif
extension NotificationCenter: ReactiveExtensionsProvider {}
extension Reactive where Base: NotificationCenter {
/// Returns a Signal to observe posting of the specified notification.
///
/// - parameters:
/// - name: name of the notification to observe
/// - object: an instance which sends the notifications
///
/// - returns: A Signal of notifications posted that match the given criteria.
///
/// - note: The signal does not terminate naturally. Observers must be
/// explicitly disposed to avoid leaks.
public func notifications(forName name: Notification.Name?, object: AnyObject? = nil) -> Signal<Notification, Never> {
return Signal { [base = self.base] observer, lifetime in
let notificationObserver = base.addObserver(forName: name, object: object, queue: nil) { notification in
observer.send(value: notification)
}
lifetime.observeEnded {
base.removeObserver(notificationObserver)
}
}
}
}
private let defaultSessionError = NSError(domain: "org.reactivecocoa.ReactiveSwift.Reactivity.URLSession.dataWithRequest",
code: 1,
userInfo: nil)
extension URLSession: ReactiveExtensionsProvider {}
extension Reactive where Base: URLSession {
/// Returns a SignalProducer which performs the work associated with an
/// `NSURLSession`
///
/// - parameters:
/// - request: A request that will be performed when the producer is
/// started
///
/// - returns: A producer that will execute the given request once for each
/// invocation of `start()`.
///
/// - note: This method will not send an error event in the case of a server
/// side error (i.e. when a response with status code other than
/// 200...299 is received).
public func data(with request: URLRequest) -> SignalProducer<(Data, URLResponse), Error> {
return SignalProducer { [base = self.base] observer, lifetime in
let task = base.dataTask(with: request) { data, response, error in
if let data = data, let response = response {
observer.send(value: (data, response))
observer.sendCompleted()
} else {
observer.send(error: error ?? defaultSessionError)
}
}
lifetime.observeEnded(task.cancel)
task.resume()
}
}
}
extension Date {
internal func addingTimeInterval(_ interval: DispatchTimeInterval) -> Date {
return addingTimeInterval(interval.timeInterval)
}
}
extension DispatchTimeInterval {
internal var timeInterval: TimeInterval {
switch self {
case let .seconds(s):
return TimeInterval(s)
case let .milliseconds(ms):
return TimeInterval(TimeInterval(ms) / 1000.0)
case let .microseconds(us):
return TimeInterval(Int64(us)) * TimeInterval(NSEC_PER_USEC) / TimeInterval(NSEC_PER_SEC)
case let .nanoseconds(ns):
return TimeInterval(ns) / TimeInterval(NSEC_PER_SEC)
case .never:
return .infinity
@unknown default:
return .infinity
}
}
// This was added purely so that our test scheduler to "go backwards" in
// time. See `TestScheduler.rewind(by interval: DispatchTimeInterval)`.
internal static prefix func -(lhs: DispatchTimeInterval) -> DispatchTimeInterval {
switch lhs {
case let .seconds(s):
return .seconds(-s)
case let .milliseconds(ms):
return .milliseconds(-ms)
case let .microseconds(us):
return .microseconds(-us)
case let .nanoseconds(ns):
return .nanoseconds(-ns)
case .never:
return .never
@unknown default:
return .never
}
}
/// Scales a time interval by the given scalar specified in `rhs`.
///
/// - returns: Scaled interval in minimal appropriate unit
internal static func *(lhs: DispatchTimeInterval, rhs: Double) -> DispatchTimeInterval {
let seconds = lhs.timeInterval * rhs
var result: DispatchTimeInterval = .never
if let integerTimeInterval = Int(exactly: (seconds * 1000 * 1000 * 1000).rounded()) {
result = .nanoseconds(integerTimeInterval)
} else if let integerTimeInterval = Int(exactly: (seconds * 1000 * 1000).rounded()) {
result = .microseconds(integerTimeInterval)
} else if let integerTimeInterval = Int(exactly: (seconds * 1000).rounded()) {
result = .milliseconds(integerTimeInterval)
} else if let integerTimeInterval = Int(exactly: (seconds).rounded()) {
result = .seconds(integerTimeInterval)
}
return result
}
}