diff --git a/CHANGELOG.md b/CHANGELOG.md index 186ba827f..db0771f76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,11 @@ # master *Please add new entries at the top.* +1. Fix some issues related to locking, bumped min OS versions to iOS 10, macOS 10.12, tvOS 10, watchOS 3 (#859, kudos to @mluisbrown) 1. Add `async` helpers to Schedulers (#857, kudos to @p4checo) 1. Add primary associated types to SignalProducerConvertible & SignalProducerProtocol (#855, kudos to @braker1nine) 2. Refactor Github Actions to cover more swift versions (#858, kudos to @braker1nine) -1.Use `OSAllocatedUnfairLock` instead of `os_unfair_lock` on supported Apple platforms (#856, kudos to @mluisbrown) +1. Use `OSAllocatedUnfairLock` instead of `os_unfair_lock` on supported Apple platforms (#856, kudos to @mluisbrown) # 7.0.0 1. The UnidirectionalBinding operator `<~` returns non optional values. (#834, kudos to @NicholasTD07) diff --git a/Package.swift b/Package.swift index 0eef252e6..99d47d23b 100644 --- a/Package.swift +++ b/Package.swift @@ -4,7 +4,7 @@ import PackageDescription let package = Package( name: "ReactiveSwift", platforms: [ - .macOS(.v10_10), .iOS(.v9), .tvOS(.v9), .watchOS(.v2) + .macOS(.v10_12), .iOS(.v10), .tvOS(.v10), .watchOS(.v3) ], products: [ .library(name: "ReactiveSwift", targets: ["ReactiveSwift"]), diff --git a/ReactiveSwift.podspec b/ReactiveSwift.podspec index e9c546295..a61c6d133 100644 --- a/ReactiveSwift.podspec +++ b/ReactiveSwift.podspec @@ -10,10 +10,10 @@ Pod::Spec.new do |s| s.license = { :type => "MIT", :file => "LICENSE.md" } s.author = "ReactiveCocoa" - s.ios.deployment_target = "9.0" - s.osx.deployment_target = "10.9" - s.watchos.deployment_target = "2.0" - s.tvos.deployment_target = "9.0" + s.ios.deployment_target = "10.0" + s.osx.deployment_target = "10.12" + s.watchos.deployment_target = "3.0" + s.tvos.deployment_target = "10.0" s.source = { :git => "https://github.com/ReactiveCocoa/ReactiveSwift.git", :tag => "#{s.version}" } # Directory glob for all Swift files s.source_files = ["Sources/*.{swift}", "Sources/**/*.{swift}"] @@ -21,5 +21,5 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = {"OTHER_SWIFT_FLAGS[config=Release]" => "$(inherited) -suppress-warnings" } s.cocoapods_version = ">= 1.7.0" - s.swift_versions = ["5.1", "5.2"] + s.swift_versions = ["5.2", "5.3" "5.4", "5.5", "5.6", "5.7"] end diff --git a/ReactiveSwift.xcodeproj/project.pbxproj b/ReactiveSwift.xcodeproj/project.pbxproj index 60a03c25d..fb6b0eb69 100644 --- a/ReactiveSwift.xcodeproj/project.pbxproj +++ b/ReactiveSwift.xcodeproj/project.pbxproj @@ -1516,16 +1516,16 @@ CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; ENABLE_TESTABILITY = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + MACOSX_DEPLOYMENT_TARGET = 10.12; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; SWIFT_TREAT_WARNINGS_AS_ERRORS = NO; - SWIFT_VERSION = 5.0; - TVOS_DEPLOYMENT_TARGET = 9.0; + SWIFT_VERSION = 5.2; + TVOS_DEPLOYMENT_TARGET = 10.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; + WATCHOS_DEPLOYMENT_TARGET = 3.0; }; name = Debug; }; @@ -1536,16 +1536,16 @@ BITCODE_GENERATION_MODE = bitcode; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + MACOSX_DEPLOYMENT_TARGET = 10.12; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; SWIFT_TREAT_WARNINGS_AS_ERRORS = NO; - SWIFT_VERSION = 5.0; - TVOS_DEPLOYMENT_TARGET = 9.0; + SWIFT_VERSION = 5.2; + TVOS_DEPLOYMENT_TARGET = 10.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; + WATCHOS_DEPLOYMENT_TARGET = 3.0; }; name = Release; }; @@ -1578,7 +1578,7 @@ baseConfigurationReference = E6124BA9267DF505005A3490 /* macOS-XCTest.xcconfig */; buildSettings = { INFOPLIST_FILE = Tests/ReactiveSwiftTests/Info.plist; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.12; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; }; name = Debug; @@ -1588,7 +1588,7 @@ baseConfigurationReference = E6124BA9267DF505005A3490 /* macOS-XCTest.xcconfig */; buildSettings = { INFOPLIST_FILE = Tests/ReactiveSwiftTests/Info.plist; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.12; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; }; name = Release; @@ -1638,16 +1638,16 @@ BITCODE_GENERATION_MODE = bitcode; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + MACOSX_DEPLOYMENT_TARGET = 10.12; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; SWIFT_TREAT_WARNINGS_AS_ERRORS = NO; - SWIFT_VERSION = 5.0; - TVOS_DEPLOYMENT_TARGET = 9.0; + SWIFT_VERSION = 5.2; + TVOS_DEPLOYMENT_TARGET = 10.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; + WATCHOS_DEPLOYMENT_TARGET = 3.0; }; name = Profile; }; @@ -1699,16 +1699,16 @@ BITCODE_GENERATION_MODE = bitcode; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + MACOSX_DEPLOYMENT_TARGET = 10.12; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)-Tests"; PRODUCT_NAME = "$(PROJECT_NAME)"; SWIFT_TREAT_WARNINGS_AS_ERRORS = NO; - SWIFT_VERSION = 5.0; - TVOS_DEPLOYMENT_TARGET = 9.0; + SWIFT_VERSION = 5.2; + TVOS_DEPLOYMENT_TARGET = 10.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; + WATCHOS_DEPLOYMENT_TARGET = 3.0; }; name = Test; }; diff --git a/Sources/Atomic.swift b/Sources/Atomic.swift index cafd01dd3..449315596 100644 --- a/Sources/Atomic.swift +++ b/Sources/Atomic.swift @@ -56,8 +56,8 @@ internal struct UnsafeAtomicState where State.RawValue /// - returns: `true` if the transition succeeds. `false` otherwise. internal func tryTransition(from expected: State, to next: State) -> Bool { return OSAtomicCompareAndSwap32Barrier(expected.rawValue, - next.rawValue, - value) + next.rawValue, + value) } #else private let value: Atomic @@ -108,10 +108,6 @@ internal struct UnsafeAtomicState where State.RawValue /// fallback. internal class Lock: LockProtocol { #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) - @available(iOS 10.0, *) - @available(macOS 10.12, *) - @available(tvOS 10.0, *) - @available(watchOS 3.0, *) internal final class UnfairLock: Lock { private let _lock: os_unfair_lock_t @@ -140,33 +136,30 @@ internal class Lock: LockProtocol { } #endif - #if compiler(>=5.7) - #if !targetEnvironment(macCatalyst) - #if os(iOS) || os(tvOS) || os(watchOS) - @available(iOS 16.0, *) - @available(tvOS 16.0, *) - @available(watchOS 9.0, *) - internal final class AllocatedUnfairLock: Lock { - private let _lock = OSAllocatedUnfairLock() - - override init() { - super.init() - } - - override func lock() { - _lock.lock() - } - - override func unlock() { - _lock.unlock() - } - - override func `try`() -> Bool { - _lock.lockIfAvailable() - } - } - #endif - #endif + #if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) + @available(iOS 16.0, *) + @available(macOS 13.0, *) + @available(tvOS 16.0, *) + @available(watchOS 9.0, *) + internal final class AllocatedUnfairLock: Lock { + private let _lock = OSAllocatedUnfairLock() + + override init() { + super.init() + } + + override func lock() { + _lock.lock() + } + + override func unlock() { + _lock.unlock() + } + + override func `try`() -> Bool { + _lock.lockIfAvailable() + } + } #endif internal final class PthreadLock: Lock { @@ -227,23 +220,15 @@ internal class Lock: LockProtocol { } static func make() -> Self { - #if compiler(>=5.7) - #if !targetEnvironment(macCatalyst) - #if os(iOS) || os(tvOS) || os(watchOS) - if #available(*, iOS 16.0, tvOS 16.0, watchOS 9.0) { - return AllocatedUnfairLock() as! Self - } - #endif - #endif - #endif - #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) - if #available(*, macOS 11.12, iOS 10.0, tvOS 10.0, watchOS 3.0) { - return UnfairLock() as! Self - } - #endif + guard #available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) else { + return UnfairLock() as! Self + } - return PthreadLock() as! Self + return AllocatedUnfairLock() as! Self + #else + return PthreadLock() as! Self + #endif } private init() {} diff --git a/Sources/Scheduler.swift b/Sources/Scheduler.swift index e0d5117f2..3b0722279 100644 --- a/Sources/Scheduler.swift +++ b/Sources/Scheduler.swift @@ -742,7 +742,7 @@ public final class TestScheduler: DateScheduler { @MainActor @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, *) public func advance(by interval: DispatchTimeInterval) async { - await advance(to: lock.withLock({ currentDate.addingTimeInterval(interval) })) + await advance(to: lock.sync({ currentDate.addingTimeInterval(interval) })) } /// Advances the virtualized clock by the given interval, dequeuing and @@ -753,7 +753,7 @@ public final class TestScheduler: DateScheduler { @MainActor @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, *) public func advance(by interval: TimeInterval) async { - await advance(to: lock.withLock({ currentDate.addingTimeInterval(interval) })) + await advance(to: lock.sync({ currentDate.addingTimeInterval(interval) })) } /// Advances the virtualized clock to the given future date, dequeuing and @@ -764,12 +764,12 @@ public final class TestScheduler: DateScheduler { @MainActor @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, *) public func advance(to newDate: Date) async { - assert(lock.withLock { _currentDate <= newDate }) + assert(lock.sync { _currentDate <= newDate }) - while lock.withLock({ _currentDate }) <= newDate { + while lock.sync({ _currentDate }) <= newDate { await Task.megaYield() - let `return` = lock.withLock { + let `return` = lock.sync { guard let next = scheduledActions.first, newDate >= next.date @@ -799,15 +799,11 @@ public final class TestScheduler: DateScheduler { #endif } -@available(macOS, obsoleted: 13, message: "`NSLocking` now provides `withLocking`, so this is no longer needed") -@available(iOS, obsoleted: 13, message: "`NSLocking` now provides `withLocking`, so this is no longer needed") -@available(watchOS, obsoleted: 9, message: "`NSLocking` now provides `withLocking`, so this is no longer needed") -@available(macCatalyst, obsoleted: 16, message: "`NSLocking` now provides `withLocking`, so this is no longer needed") extension NSRecursiveLock { - fileprivate func withLock(_ body: () throws -> T) rethrows -> T { + fileprivate func sync(_ operation: () -> T) -> T { self.lock() defer { self.unlock() } - return try body() + return operation() } } diff --git a/Tests/ReactiveSwiftTests/DateSchedulerAsyncTestCase.swift b/Tests/ReactiveSwiftTests/DateSchedulerAsyncTestCase.swift index 8734ad1ea..7c02cf271 100644 --- a/Tests/ReactiveSwiftTests/DateSchedulerAsyncTestCase.swift +++ b/Tests/ReactiveSwiftTests/DateSchedulerAsyncTestCase.swift @@ -25,35 +25,29 @@ final class DateSchedulerAsyncTestCase: XCTestCase { } func test_sleepFor_shouldSleepForTheDefinedIntervalBeforeReturning() async throws { - let expectation = self.expectation(description: "sleep") - - Task { + let task = Task { try await scheduler.sleep(for: .seconds(5)) XCTAssertEqual(scheduler.currentDate, startDate.addingTimeInterval(5)) - expectation.fulfill() } XCTAssertEqual(scheduler.currentDate, startDate) await scheduler.advance(by: .seconds(5)) - await waitForExpectations(timeout: 0.1) + let _ = await task.result } func test_sleepUntil_shouldSleepForTheDefinedIntervalBeforeReturning() async throws { - let expectation = self.expectation(description: "sleep") - let sleepDate = startDate.addingTimeInterval(5) - Task { + let task = Task { try await scheduler.sleep(until: sleepDate) XCTAssertEqual(scheduler.currentDate, sleepDate) - expectation.fulfill() } XCTAssertEqual(scheduler.currentDate, startDate) await scheduler.advance(by: .seconds(5)) - await waitForExpectations(timeout: 0.1) + let _ = await task.result } func test_timer_shouldSendTheCurrentDateAtTheGivenInterval() async throws {