FoundationExtensions.swift 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. //
  2. // FoundationExtensions.swift
  3. // ReactiveSwift
  4. //
  5. // Created by Justin Spahr-Summers on 2014-10-19.
  6. // Copyright (c) 2014 GitHub. All rights reserved.
  7. //
  8. import Foundation
  9. import Dispatch
  10. import enum Result.NoError
  11. import struct Result.AnyError
  12. #if os(Linux)
  13. import let CDispatch.NSEC_PER_USEC
  14. import let CDispatch.NSEC_PER_SEC
  15. #endif
  16. extension NotificationCenter: ReactiveExtensionsProvider {}
  17. extension Reactive where Base: NotificationCenter {
  18. /// Returns a Signal to observe posting of the specified notification.
  19. ///
  20. /// - parameters:
  21. /// - name: name of the notification to observe
  22. /// - object: an instance which sends the notifications
  23. ///
  24. /// - returns: A Signal of notifications posted that match the given criteria.
  25. ///
  26. /// - note: The signal does not terminate naturally. Observers must be
  27. /// explicitly disposed to avoid leaks.
  28. public func notifications(forName name: Notification.Name?, object: AnyObject? = nil) -> Signal<Notification, NoError> {
  29. return Signal { [base = self.base] observer in
  30. let notificationObserver = base.addObserver(forName: name, object: object, queue: nil) { notification in
  31. observer.send(value: notification)
  32. }
  33. return ActionDisposable {
  34. base.removeObserver(notificationObserver)
  35. }
  36. }
  37. }
  38. }
  39. private let defaultSessionError = NSError(domain: "org.reactivecocoa.ReactiveSwift.Reactivity.URLSession.dataWithRequest",
  40. code: 1,
  41. userInfo: nil)
  42. extension URLSession: ReactiveExtensionsProvider {}
  43. extension Reactive where Base: URLSession {
  44. /// Returns a SignalProducer which performs the work associated with an
  45. /// `NSURLSession`
  46. ///
  47. /// - parameters:
  48. /// - request: A request that will be performed when the producer is
  49. /// started
  50. ///
  51. /// - returns: A producer that will execute the given request once for each
  52. /// invocation of `start()`.
  53. ///
  54. /// - note: This method will not send an error event in the case of a server
  55. /// side error (i.e. when a response with status code other than
  56. /// 200...299 is received).
  57. public func data(with request: URLRequest) -> SignalProducer<(Data, URLResponse), AnyError> {
  58. return SignalProducer { [base = self.base] observer, disposable in
  59. let task = base.dataTask(with: request) { data, response, error in
  60. if let data = data, let response = response {
  61. observer.send(value: (data, response))
  62. observer.sendCompleted()
  63. } else {
  64. observer.send(error: AnyError(error ?? defaultSessionError))
  65. }
  66. }
  67. disposable += {
  68. task.cancel()
  69. }
  70. task.resume()
  71. }
  72. }
  73. }
  74. extension Date {
  75. internal func addingTimeInterval(_ interval: DispatchTimeInterval) -> Date {
  76. return addingTimeInterval(interval.timeInterval)
  77. }
  78. }
  79. extension DispatchTimeInterval {
  80. internal var timeInterval: TimeInterval {
  81. switch self {
  82. case let .seconds(s):
  83. return TimeInterval(s)
  84. case let .milliseconds(ms):
  85. return TimeInterval(TimeInterval(ms) / 1000.0)
  86. case let .microseconds(us):
  87. return TimeInterval( UInt64(us) * NSEC_PER_USEC ) / TimeInterval(NSEC_PER_SEC)
  88. case let .nanoseconds(ns):
  89. return TimeInterval(ns) / TimeInterval(NSEC_PER_SEC)
  90. }
  91. }
  92. // This was added purely so that our test scheduler to "go backwards" in
  93. // time. See `TestScheduler.rewind(by interval: DispatchTimeInterval)`.
  94. internal static prefix func -(lhs: DispatchTimeInterval) -> DispatchTimeInterval {
  95. switch lhs {
  96. case let .seconds(s):
  97. return .seconds(-s)
  98. case let .milliseconds(ms):
  99. return .milliseconds(-ms)
  100. case let .microseconds(us):
  101. return .microseconds(-us)
  102. case let .nanoseconds(ns):
  103. return .nanoseconds(-ns)
  104. }
  105. }
  106. /// Scales a time interval by the given scalar specified in `rhs`.
  107. ///
  108. /// - note: This method is only used internally to "scale down" a time
  109. /// interval. Specifically it's used only to scale intervals to 10%
  110. /// of their original value for the default `leeway` parameter in
  111. /// `SchedulerProtocol.schedule(after:action:)` schedule and similar
  112. /// other methods.
  113. ///
  114. /// If seconds is over 200,000, 10% is ~2,000, and hence we end up
  115. /// with a value of ~2,000,000,000. Not quite overflowing a signed
  116. /// integer on 32-bit platforms, but close.
  117. ///
  118. /// Even still, 200,000 seconds should be a rarely (if ever)
  119. /// specified interval for our APIs. And even then, folks should be
  120. /// smart and specify their own `leeway` parameter.
  121. ///
  122. /// - returns: Scaled interval in microseconds
  123. internal static func *(lhs: DispatchTimeInterval, rhs: Double) -> DispatchTimeInterval {
  124. let seconds = lhs.timeInterval * rhs
  125. return .microseconds(Int(seconds * 1000 * 1000))
  126. }
  127. }