diff --git a/Platform/RecursiveLock.swift b/Platform/RecursiveLock.swift index 10b9bbb0b..b7363105d 100644 --- a/Platform/RecursiveLock.swift +++ b/Platform/RecursiveLock.swift @@ -9,7 +9,7 @@ import Foundation #if TRACE_RESOURCES - class RecursiveLock: NSRecursiveLock { + class RecursiveLock: NSRecursiveLock, @unchecked Sendable { override init() { _ = Resources.incrementTotal() super.init() diff --git a/Rx.playground/Sources/SupportCode.swift b/Rx.playground/Sources/SupportCode.swift index 849a79f18..e9b6d2060 100644 --- a/Rx.playground/Sources/SupportCode.swift +++ b/Rx.playground/Sources/SupportCode.swift @@ -24,7 +24,7 @@ public enum TestError: Swift.Error { - parameter delay: time in seconds to wait before executing `closure` - parameter closure: `Void` closure */ -public func delay(_ delay: Double, closure: @escaping () -> Void) { +public func delay(_ delay: Double, closure: @escaping @Sendable () -> Void) { DispatchQueue.main.asyncAfter(deadline: .now() + delay) { closure() diff --git a/RxBlocking/BlockingObservable+Operators.swift b/RxBlocking/BlockingObservable+Operators.swift index 02104a25d..243c68c42 100644 --- a/RxBlocking/BlockingObservable+Operators.swift +++ b/RxBlocking/BlockingObservable+Operators.swift @@ -73,7 +73,7 @@ extension BlockingObservable { /// /// - parameter predicate: A function to test each source element for a condition. /// - returns: Returns the only element of an sequence that satisfies the condition in the predicate, and reports an error if there is not exactly one element in the sequence. - public func single(_ predicate: @escaping (Element) throws -> Bool) throws -> Element { + public func single(_ predicate: @escaping @Sendable (Element) throws -> Bool) throws -> Element { let results = self.materializeResult(max: 2, predicate: predicate) let elements = try self.elementsOrThrow(results) @@ -101,9 +101,9 @@ extension BlockingObservable { } extension BlockingObservable { - private func materializeResult(max: Int? = nil, predicate: @escaping (Element) throws -> Bool = { _ in true }) -> MaterializedSequenceResult { - var elements = [Element]() - var error: Swift.Error? + private func materializeResult(max: Int? = nil, predicate: @escaping @Sendable (Element) throws -> Bool = { _ in true }) -> MaterializedSequenceResult { + nonisolated(unsafe) var elements = [Element]() + nonisolated(unsafe) var error: Swift.Error? let lock = RunLoopLock(timeout: self.timeout) @@ -114,7 +114,7 @@ extension BlockingObservable { } lock.dispatch { - let subscription = self.source.subscribe { event in + let subscription = self.source.subscribe { @Sendable event in if d.isDisposed { return } diff --git a/RxBlocking/RunLoopLock.swift b/RxBlocking/RunLoopLock.swift index cc8c9e00d..242aeaa74 100644 --- a/RxBlocking/RunLoopLock.swift +++ b/RxBlocking/RunLoopLock.swift @@ -31,7 +31,7 @@ final class RunLoopLock { self.currentRunLoop = CFRunLoopGetCurrent() } - func dispatch(_ action: @escaping () -> Void) { + func dispatch(_ action: @escaping @Sendable () -> Void) { CFRunLoopPerformBlock(self.currentRunLoop, runLoopModeRaw) { if CurrentThreadScheduler.isScheduleRequired { _ = CurrentThreadScheduler.instance.schedule(()) { _ in diff --git a/RxCocoa/Common/ControlTarget.swift b/RxCocoa/Common/ControlTarget.swift index b09d5c72b..073458b1a 100644 --- a/RxCocoa/Common/ControlTarget.swift +++ b/RxCocoa/Common/ControlTarget.swift @@ -21,8 +21,9 @@ import RxSwift #endif // This should be only used from `MainScheduler` -final class ControlTarget: RxTarget { - typealias Callback = (Control) -> Void +@MainActor +final class ControlTarget: RxTarget, @unchecked Sendable { + typealias Callback = @Sendable @MainActor (Control) -> Void let selector: Selector = #selector(ControlTarget.eventHandler(_:)) @@ -72,16 +73,23 @@ final class ControlTarget: RxTarget { callback(control) } } - + + @Sendable override func dispose() { super.dispose() #if os(iOS) || os(tvOS) || os(visionOS) - self.control?.removeTarget(self, action: self.selector, for: self.controlEvents) + MainScheduler.assumeMainActor(execute: { + self.control?.removeTarget(self, action: self.selector, for: self.controlEvents) + }) #elseif os(macOS) - self.control?.target = nil - self.control?.action = nil + MainScheduler.assumeMainActor(execute: { + self.control?.target = nil + self.control?.action = nil + }) #endif - self.callback = nil + MainScheduler.assumeMainActor(execute: { + self.callback = nil + }) } } diff --git a/RxCocoa/Common/DelegateProxy.swift b/RxCocoa/Common/DelegateProxy.swift index d103448e7..55889438d 100644 --- a/RxCocoa/Common/DelegateProxy.swift +++ b/RxCocoa/Common/DelegateProxy.swift @@ -266,7 +266,7 @@ fileprivate let selector: Selector init(selector: Selector, delegateProxy _delegateProxy: DelegateProxy) { - weak var weakDelegateProxy = _delegateProxy + nonisolated(unsafe) weak var weakDelegateProxy = _delegateProxy let dispatcher = PublishSubject<[Any]>() self.dispatcher = dispatcher diff --git a/RxCocoa/Common/DelegateProxyType.swift b/RxCocoa/Common/DelegateProxyType.swift index 0e7448a54..32eecca9f 100644 --- a/RxCocoa/Common/DelegateProxyType.swift +++ b/RxCocoa/Common/DelegateProxyType.swift @@ -151,7 +151,7 @@ extension DelegateProxyType { /// When make 'Rx*DelegateProxy' subclass, call 'Rx*DelegateProxySubclass.register(for:_)' 1 time, or use it in DelegateProxyFactory /// 'Rx*DelegateProxy' can have one subclass implementation per concrete ParentObject type. /// Should call it from concrete DelegateProxy type, not generic. - public static func register(make: @escaping (Parent) -> Self) { + public static func register(make: @escaping @Sendable (Parent) -> Self) { self.factory.extend(make: make) } @@ -214,7 +214,7 @@ extension DelegateProxyType { /// - parameter onProxyForObject: Object that has `delegate` property. /// - returns: Disposable object that can be used to clear forward delegate. public static func installForwardDelegate(_ forwardDelegate: Delegate, retainDelegate: Bool, onProxyForObject object: ParentObject) -> Disposable { - weak var weakForwardDelegate: AnyObject? = forwardDelegate as AnyObject + nonisolated(unsafe) weak var weakForwardDelegate: AnyObject? = forwardDelegate as AnyObject let proxy = self.proxy(for: object) assert(proxy._forwardToDelegate() === nil, "This is a feature to warn you that there is already a delegate (or data source) set somewhere previously. The action you are trying to perform will clear that delegate (data source) and that means that some of your features that depend on that delegate (data source) being set will likely stop working.\n" + @@ -317,7 +317,7 @@ extension DelegateProxyType where ParentObject: HasPrefetchDataSource, Self.Dele import UIKit extension ObservableType { - func subscribeProxyDataSource(ofObject object: DelegateProxy.ParentObject, dataSource: DelegateProxy.Delegate, retainDataSource: Bool, binding: @escaping (DelegateProxy, Event) -> Void) + func subscribeProxyDataSource(ofObject object: DelegateProxy.ParentObject, dataSource: DelegateProxy.Delegate, retainDataSource: Bool, binding: @escaping @Sendable (DelegateProxy, Event) -> Void) -> Disposable where DelegateProxy.ParentObject: UIView , DelegateProxy.Delegate: AnyObject { @@ -360,10 +360,11 @@ extension DelegateProxyType where ParentObject: HasPrefetchDataSource, Self.Dele return Disposables.create { [weak object] in subscription.dispose() - - if object?.window != nil { - object?.layoutIfNeeded() - } + MainScheduler.assumeMainActor(execute: { + if object?.window != nil { + object?.layoutIfNeeded() + } + }) unregisterDelegate.dispose() } @@ -410,7 +411,7 @@ extension DelegateProxyType where ParentObject: HasPrefetchDataSource, Self.Dele self._identifier = proxyType.identifier } - fileprivate func extend(make: @escaping (ParentObject) -> DelegateProxy) { + fileprivate func extend(make: @escaping @Sendable (ParentObject) -> DelegateProxy) { MainScheduler.ensureRunningOnMainThread() precondition(self._identifier == DelegateProxy.identifier, "Delegate proxy has inconsistent identifier") guard self._factories[ObjectIdentifier(ParentObject.self)] == nil else { diff --git a/RxCocoa/Common/Infallible+Bind.swift b/RxCocoa/Common/Infallible+Bind.swift index 4849641a4..f9c1524ed 100644 --- a/RxCocoa/Common/Infallible+Bind.swift +++ b/RxCocoa/Common/Infallible+Bind.swift @@ -70,7 +70,7 @@ extension InfallibleType { - parameter onNext: Action to invoke for each element in the observable sequence. - returns: Subscription object used to unsubscribe from the observable sequence. */ - public func bind(onNext: @escaping (Element) -> Void) -> Disposable { + public func bind(onNext: @escaping @Sendable (Element) -> Void) -> Disposable { self.subscribe(onNext: onNext) } diff --git a/RxCocoa/Common/Observable+Bind.swift b/RxCocoa/Common/Observable+Bind.swift index 500154aaa..2ff6f1318 100644 --- a/RxCocoa/Common/Observable+Bind.swift +++ b/RxCocoa/Common/Observable+Bind.swift @@ -75,7 +75,7 @@ extension ObservableType { */ public func bind( with object: Object, - onNext: @escaping (Object, Element) -> Void + onNext: @escaping @Sendable (Object, Element) -> Void ) -> Disposable { self.subscribe(onNext: { [weak object] in guard let object = object else { return } @@ -94,7 +94,7 @@ extension ObservableType { - parameter onNext: Action to invoke for each element in the observable sequence. - returns: Subscription object used to unsubscribe from the observable sequence. */ - public func bind(onNext: @escaping (Element) -> Void) -> Disposable { + public func bind(onNext: @escaping @Sendable (Element) -> Void) -> Disposable { self.subscribe(onNext: onNext, onError: { error in rxFatalErrorInDebug("Binding error: \(error)") diff --git a/RxCocoa/Common/RxTarget.swift b/RxCocoa/Common/RxTarget.swift index 5a2c464b0..4a4efcb15 100644 --- a/RxCocoa/Common/RxTarget.swift +++ b/RxCocoa/Common/RxTarget.swift @@ -11,7 +11,8 @@ import Foundation import RxSwift class RxTarget : NSObject - , Disposable { + , Disposable + , @unchecked Sendable { private var retainSelf: RxTarget? diff --git a/RxCocoa/Foundation/NSObject+Rx+KVORepresentable.swift b/RxCocoa/Foundation/NSObject+Rx+KVORepresentable.swift index 1be5a212d..96fe0862f 100644 --- a/RxCocoa/Foundation/NSObject+Rx+KVORepresentable.swift +++ b/RxCocoa/Foundation/NSObject+Rx+KVORepresentable.swift @@ -38,7 +38,7 @@ extension Reactive where Base: NSObject { */ public func observe(_ type: Element.Type, _ keyPath: String, options: KeyValueObservingOptions = [.new, .initial], retainSelf: Bool = true) -> Observable { return self.observe(Element.KVOType.self, keyPath, options: options, retainSelf: retainSelf) - .map(Element.init) + .map({ Element(KVOValue: $0) }) } } @@ -52,7 +52,7 @@ extension Reactive where Base: NSObject { */ public func observeWeakly(_ type: Element.Type, _ keyPath: String, options: KeyValueObservingOptions = [.new, .initial]) -> Observable { return self.observeWeakly(Element.KVOType.self, keyPath, options: options) - .map(Element.init) + .map({ Element(KVOValue: $0) }) } } #endif diff --git a/RxCocoa/Foundation/NSObject+Rx+RawRepresentable.swift b/RxCocoa/Foundation/NSObject+Rx+RawRepresentable.swift index 6e47cb243..40dc9c31b 100644 --- a/RxCocoa/Foundation/NSObject+Rx+RawRepresentable.swift +++ b/RxCocoa/Foundation/NSObject+Rx+RawRepresentable.swift @@ -24,7 +24,7 @@ extension Reactive where Base: NSObject { */ public func observe(_ type: Element.Type, _ keyPath: String, options: KeyValueObservingOptions = [.new, .initial], retainSelf: Bool = true) -> Observable where Element.RawValue: KVORepresentable { return self.observe(Element.RawValue.KVOType.self, keyPath, options: options, retainSelf: retainSelf) - .map(Element.init) + .map({ Element(KVOValue: $0) }) } } @@ -44,7 +44,7 @@ extension Reactive where Base: NSObject { */ public func observeWeakly(_ type: Element.Type, _ keyPath: String, options: KeyValueObservingOptions = [.new, .initial]) -> Observable where Element.RawValue: KVORepresentable { return self.observeWeakly(Element.RawValue.KVOType.self, keyPath, options: options) - .map(Element.init) + .map({ Element(KVOValue: $0) }) } } #endif diff --git a/RxCocoa/Foundation/NSObject+Rx.swift b/RxCocoa/Foundation/NSObject+Rx.swift index 2f1572416..2e16f0a07 100644 --- a/RxCocoa/Foundation/NSObject+Rx.swift +++ b/RxCocoa/Foundation/NSObject+Rx.swift @@ -360,7 +360,8 @@ private protocol KVOObservableProtocol { private final class KVOObserver : _RXKVOObserver - , Disposable { + , Disposable + , @unchecked Sendable { typealias Callback = (Any?) -> Void var retainSelf: KVOObserver? @@ -373,7 +374,8 @@ private final class KVOObserver super.init(target: parent.target, retainTarget: parent.retainTarget, keyPath: parent.keyPath, options: parent.options.nsOptions, callback: callback) self.retainSelf = self } - + + @Sendable override func dispose() { super.dispose() self.retainSelf = nil @@ -388,7 +390,8 @@ private final class KVOObserver private final class KVOObservable : ObservableType - , KVOObservableProtocol { + , KVOObservableProtocol + , @unchecked Sendable { typealias Element = Element? unowned var target: AnyObject @@ -483,7 +486,7 @@ private extension KeyValueObservingOptions { options: KeyValueObservingOptions ) -> Observable { - weak var weakTarget: AnyObject? = target + nonisolated(unsafe) weak var weakTarget: AnyObject? = target let propertyName = keyPathSections[0] let remainingPaths = Array(keyPathSections[1..( with object: Object, - onNext: ((Object, Element) -> Void)? = nil, - onCompleted: ((Object) -> Void)? = nil, - onDisposed: ((Object) -> Void)? = nil + onNext: (@Sendable @MainActor (Object, Element) -> Void)? = nil, + onCompleted: (@Sendable @MainActor (Object) -> Void)? = nil, + onDisposed: (@Sendable (Object) -> Void)? = nil ) -> Disposable { MainScheduler.ensureRunningOnMainThread(errorMessage: errorMessage) - return self.asObservable().subscribe(with: object, onNext: onNext, onCompleted: onCompleted, onDisposed: onDisposed) + return self.asObservable().subscribe(with: object, onNext: onNext.flatMap({ MainScheduler.assumeMainActor($0) }), onCompleted: onCompleted.flatMap({ MainScheduler.assumeMainActor($0) }), onDisposed: onDisposed) } /** @@ -179,12 +179,12 @@ extension SharedSequenceConvertibleType where SharingStrategy == DriverSharingSt - returns: Subscription object used to unsubscribe from the observable sequence. */ public func drive( - onNext: ((Element) -> Void)? = nil, - onCompleted: (() -> Void)? = nil, - onDisposed: (() -> Void)? = nil + onNext: (@Sendable @MainActor (Element) -> Void)? = nil, + onCompleted: (@Sendable @MainActor () -> Void)? = nil, + onDisposed: (@Sendable () -> Void)? = nil ) -> Disposable { MainScheduler.ensureRunningOnMainThread(errorMessage: errorMessage) - return self.asObservable().subscribe(onNext: onNext, onCompleted: onCompleted, onDisposed: onDisposed) + return self.asObservable().subscribe(onNext: onNext.flatMap({ MainScheduler.assumeMainActor($0) }), onCompleted: onCompleted.flatMap({ MainScheduler.assumeMainActor($0) }), onDisposed: onDisposed) } /** diff --git a/RxCocoa/Traits/Driver/ObservableConvertibleType+Driver.swift b/RxCocoa/Traits/Driver/ObservableConvertibleType+Driver.swift index bcac66b66..16e79abb2 100644 --- a/RxCocoa/Traits/Driver/ObservableConvertibleType+Driver.swift +++ b/RxCocoa/Traits/Driver/ObservableConvertibleType+Driver.swift @@ -45,12 +45,12 @@ extension ObservableConvertibleType { - parameter onErrorRecover: Calculates driver that continues to drive the sequence in case of error. - returns: Driver trait. */ - public func asDriver(onErrorRecover: @escaping (_ error: Swift.Error) -> Driver) -> Driver { + public func asDriver(onErrorRecover: @escaping @Sendable @MainActor (_ error: Swift.Error) -> Driver) -> Driver { let source = self .asObservable() .observe(on:DriverSharingStrategy.scheduler) .catch { error in - onErrorRecover(error).asObservable() + MainScheduler.assumeMainActor(onErrorRecover)(error).asObservable() } return Driver(source) } diff --git a/RxCocoa/Traits/SharedSequence/ObservableConvertibleType+SharedSequence.swift b/RxCocoa/Traits/SharedSequence/ObservableConvertibleType+SharedSequence.swift index 20ddf868f..5f6f6ada1 100644 --- a/RxCocoa/Traits/SharedSequence/ObservableConvertibleType+SharedSequence.swift +++ b/RxCocoa/Traits/SharedSequence/ObservableConvertibleType+SharedSequence.swift @@ -45,7 +45,7 @@ extension ObservableConvertibleType { - parameter onErrorRecover: Calculates driver that continues to drive the sequence in case of error. - returns: Driving observable sequence. */ - public func asSharedSequence(sharingStrategy: S.Type = S.self, onErrorRecover: @escaping (_ error: Swift.Error) -> SharedSequence) -> SharedSequence { + public func asSharedSequence(sharingStrategy: S.Type = S.self, onErrorRecover: @escaping @Sendable (_ error: Swift.Error) -> SharedSequence) -> SharedSequence { let source = self .asObservable() .observe(on:S.scheduler) diff --git a/RxCocoa/Traits/SharedSequence/SchedulerType+SharedSequence.swift b/RxCocoa/Traits/SharedSequence/SchedulerType+SharedSequence.swift index 5111e06d8..d4d63016f 100644 --- a/RxCocoa/Traits/SharedSequence/SchedulerType+SharedSequence.swift +++ b/RxCocoa/Traits/SharedSequence/SchedulerType+SharedSequence.swift @@ -28,7 +28,7 @@ public enum SharingScheduler { **This shouldn't be used in normal release builds.** */ - static public func mock(makeScheduler: @escaping () -> SchedulerType, action: () throws -> Void) rethrows { + static public func mock(makeScheduler: @escaping @Sendable () -> SchedulerType, action: () throws -> Void) rethrows { let originalMake = make make = makeScheduler defer { diff --git a/RxCocoa/Traits/SharedSequence/SharedSequence+Operators+arity.swift b/RxCocoa/Traits/SharedSequence/SharedSequence+Operators+arity.swift index 4cc967cfb..df6b5e478 100644 --- a/RxCocoa/Traits/SharedSequence/SharedSequence+Operators+arity.swift +++ b/RxCocoa/Traits/SharedSequence/SharedSequence+Operators+arity.swift @@ -21,7 +21,7 @@ extension SharedSequence { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip - (_ source1: O1, _ source2: O2, resultSelector: @escaping (O1.Element, O2.Element) throws -> Element) + (_ source1: O1, _ source2: O2, resultSelector: @escaping @Sendable (O1.Element, O2.Element) throws -> Element) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy { let source = Observable.zip( @@ -59,7 +59,7 @@ extension SharedSequence { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest - (_ source1: O1, _ source2: O2, resultSelector: @escaping (O1.Element, O2.Element) throws -> Element) + (_ source1: O1, _ source2: O2, resultSelector: @escaping @Sendable (O1.Element, O2.Element) throws -> Element) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy { let source = Observable.combineLatest( @@ -101,7 +101,7 @@ extension SharedSequence { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip - (_ source1: O1, _ source2: O2, _ source3: O3, resultSelector: @escaping (O1.Element, O2.Element, O3.Element) throws -> Element) + (_ source1: O1, _ source2: O2, _ source3: O3, resultSelector: @escaping @Sendable (O1.Element, O2.Element, O3.Element) throws -> Element) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy { @@ -141,7 +141,7 @@ extension SharedSequence { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest - (_ source1: O1, _ source2: O2, _ source3: O3, resultSelector: @escaping (O1.Element, O2.Element, O3.Element) throws -> Element) + (_ source1: O1, _ source2: O2, _ source3: O3, resultSelector: @escaping @Sendable (O1.Element, O2.Element, O3.Element) throws -> Element) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy { @@ -185,7 +185,7 @@ extension SharedSequence { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip - (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element) throws -> Element) + (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: @escaping @Sendable (O1.Element, O2.Element, O3.Element, O4.Element) throws -> Element) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, @@ -227,7 +227,7 @@ extension SharedSequence { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest - (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element) throws -> Element) + (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: @escaping @Sendable (O1.Element, O2.Element, O3.Element, O4.Element) throws -> Element) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, @@ -273,7 +273,7 @@ extension SharedSequence { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip - (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element) throws -> Element) + (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: @escaping @Sendable (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element) throws -> Element) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, @@ -317,7 +317,7 @@ extension SharedSequence { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest - (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element) throws -> Element) + (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: @escaping @Sendable (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element) throws -> Element) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, @@ -365,7 +365,7 @@ extension SharedSequence { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip - (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element) throws -> Element) + (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: @escaping @Sendable (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element) throws -> Element) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, @@ -411,7 +411,7 @@ extension SharedSequence { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest - (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element) throws -> Element) + (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: @escaping @Sendable (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element) throws -> Element) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, @@ -461,7 +461,7 @@ extension SharedSequence { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip - (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element) throws -> Element) + (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: @escaping @Sendable (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element) throws -> Element) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, @@ -509,7 +509,7 @@ extension SharedSequence { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest - (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element) throws -> Element) + (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: @escaping @Sendable (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element) throws -> Element) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, @@ -561,7 +561,7 @@ extension SharedSequence { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip - (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element, O8.Element) throws -> Element) + (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: @escaping @Sendable (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element, O8.Element) throws -> Element) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, @@ -611,7 +611,7 @@ extension SharedSequence { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest - (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element, O8.Element) throws -> Element) + (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: @escaping @Sendable (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element, O8.Element) throws -> Element) -> SharedSequence where SharingStrategy == O1.SharingStrategy, SharingStrategy == O2.SharingStrategy, SharingStrategy == O3.SharingStrategy, diff --git a/RxCocoa/Traits/SharedSequence/SharedSequence+Operators.swift b/RxCocoa/Traits/SharedSequence/SharedSequence+Operators.swift index 1d53b03d9..e5180e444 100644 --- a/RxCocoa/Traits/SharedSequence/SharedSequence+Operators.swift +++ b/RxCocoa/Traits/SharedSequence/SharedSequence+Operators.swift @@ -17,7 +17,7 @@ extension SharedSequenceConvertibleType { - parameter selector: A transform function to apply to each source element. - returns: An observable sequence whose elements are the result of invoking the transform function on each element of source. */ - public func map(_ selector: @escaping (Element) -> Result) -> SharedSequence { + public func map(_ selector: @escaping @Sendable (Element) -> Result) -> SharedSequence { let source = self .asObservable() .map(selector) @@ -35,7 +35,7 @@ extension SharedSequenceConvertibleType { - returns: An observable sequence whose elements are the result of filtering the transform function for each element of the source. */ - public func compactMap(_ selector: @escaping (Element) -> Result?) -> SharedSequence { + public func compactMap(_ selector: @escaping @Sendable (Element) -> Result?) -> SharedSequence { let source = self .asObservable() .compactMap(selector) @@ -51,7 +51,7 @@ extension SharedSequenceConvertibleType { - parameter predicate: A function to test each source element for a condition. - returns: An observable sequence that contains elements from the input sequence that satisfy the condition. */ - public func filter(_ predicate: @escaping (Element) -> Bool) -> SharedSequence { + public func filter(_ predicate: @escaping @Sendable (Element) -> Bool) -> SharedSequence { let source = self .asObservable() .filter(predicate) @@ -92,7 +92,7 @@ extension SharedSequenceConvertibleType { - returns: An observable sequence whose elements are the result of invoking the transform function on each element of source producing an Observable of Observable sequences and that at any point in time produces the elements of the most recent inner observable sequence that has been received. */ - public func flatMapLatest(_ selector: @escaping (Element) -> SharedSequence) + public func flatMapLatest(_ selector: @escaping @Sendable (Element) -> SharedSequence) -> SharedSequence { let source: Observable = self .asObservable() @@ -111,7 +111,7 @@ extension SharedSequenceConvertibleType { - parameter selector: A transform function to apply to element that was observed while no observable is executing in parallel. - returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence that was received while no other sequence was being calculated. */ - public func flatMapFirst(_ selector: @escaping (Element) -> SharedSequence) + public func flatMapFirst(_ selector: @escaping @Sendable (Element) -> SharedSequence) -> SharedSequence { let source: Observable = self .asObservable() @@ -134,7 +134,7 @@ extension SharedSequenceConvertibleType { - parameter onDispose: Action to invoke after subscription to source observable has been disposed for any reason. It can be either because sequence terminates for some reason or observer subscription being disposed. - returns: The source sequence with the side-effecting behavior applied. */ - public func `do`(onNext: ((Element) -> Void)? = nil, afterNext: ((Element) -> Void)? = nil, onCompleted: (() -> Void)? = nil, afterCompleted: (() -> Void)? = nil, onSubscribe: (() -> Void)? = nil, onSubscribed: (() -> Void)? = nil, onDispose: (() -> Void)? = nil) + public func `do`(onNext: (@Sendable (Element) -> Void)? = nil, afterNext: (@Sendable (Element) -> Void)? = nil, onCompleted: (@Sendable () -> Void)? = nil, afterCompleted: (@Sendable () -> Void)? = nil, onSubscribe: (@Sendable () -> Void)? = nil, onSubscribed: (@Sendable () -> Void)? = nil, onDispose: (@Sendable () -> Void)? = nil) -> SharedSequence { let source = self.asObservable() .do(onNext: onNext, afterNext: afterNext, onCompleted: onCompleted, afterCompleted: afterCompleted, onSubscribe: onSubscribe, onSubscribed: onSubscribed, onDispose: onDispose) @@ -184,7 +184,7 @@ extension SharedSequenceConvertibleType { - parameter keySelector: A function to compute the comparison key for each element. - returns: An observable sequence only containing the distinct contiguous elements, based on a computed key value, from the source sequence. */ - public func distinctUntilChanged(_ keySelector: @escaping (Element) -> Key) -> SharedSequence { + public func distinctUntilChanged(_ keySelector: @escaping @Sendable (Element) -> Key) -> SharedSequence { let source = self.asObservable() .distinctUntilChanged(keySelector, comparer: { $0 == $1 }) return SharedSequence(source) @@ -196,7 +196,7 @@ extension SharedSequenceConvertibleType { - parameter comparer: Equality comparer for computed key values. - returns: An observable sequence only containing the distinct contiguous elements, based on `comparer`, from the source sequence. */ - public func distinctUntilChanged(_ comparer: @escaping (Element, Element) -> Bool) -> SharedSequence { + public func distinctUntilChanged(_ comparer: @escaping @Sendable (Element, Element) -> Bool) -> SharedSequence { let source = self.asObservable() .distinctUntilChanged({ $0 }, comparer: comparer) return SharedSequence(source) @@ -209,7 +209,7 @@ extension SharedSequenceConvertibleType { - parameter comparer: Equality comparer for computed key values. - returns: An observable sequence only containing the distinct contiguous elements, based on a computed key value and the comparer, from the source sequence. */ - public func distinctUntilChanged(_ keySelector: @escaping (Element) -> K, comparer: @escaping (K, K) -> Bool) -> SharedSequence { + public func distinctUntilChanged(_ keySelector: @escaping @Sendable (Element) -> K, comparer: @escaping @Sendable (K, K) -> Bool) -> SharedSequence { let source = self.asObservable() .distinctUntilChanged(keySelector, comparer: comparer) return SharedSequence(source) @@ -226,7 +226,7 @@ extension SharedSequenceConvertibleType { - parameter selector: A transform function to apply to each element. - returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence. */ - public func flatMap(_ selector: @escaping (Element) -> SharedSequence) -> SharedSequence { + public func flatMap(_ selector: @escaping @Sendable (Element) -> SharedSequence) -> SharedSequence { let source = self.asObservable() .flatMap(selector) @@ -355,7 +355,7 @@ extension SharedSequenceConvertibleType { - parameter accumulator: An accumulator function to be invoked on each element. - returns: An observable sequence containing the accumulated values. */ - public func scan(_ seed: A, accumulator: @escaping (A, Element) -> A) + public func scan(_ seed: A, accumulator: @escaping @Sendable (A, Element) -> A) -> SharedSequence { let source = self.asObservable() .scan(seed, accumulator: accumulator) @@ -398,7 +398,7 @@ extension SharedSequence { - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ - public static func zip(_ collection: Collection, resultSelector: @escaping ([Element]) throws -> Result) -> SharedSequence + public static func zip(_ collection: Collection, resultSelector: @escaping @Sendable ([Element]) throws -> Result) -> SharedSequence where Collection.Element == SharedSequence { let source = Observable.zip(collection.map { $0.asSharedSequence().asObservable() }, resultSelector: resultSelector) return SharedSequence(source) @@ -425,7 +425,7 @@ extension SharedSequence { - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ - public static func combineLatest(_ collection: Collection, resultSelector: @escaping ([Element]) throws -> Result) -> SharedSequence + public static func combineLatest(_ collection: Collection, resultSelector: @escaping @Sendable ([Element]) throws -> Result) -> SharedSequence where Collection.Element == SharedSequence { let source = Observable.combineLatest(collection.map { $0.asObservable() }, resultSelector: resultSelector) return SharedSequence(source) @@ -458,7 +458,7 @@ extension SharedSequenceConvertibleType where SharingStrategy == SignalSharingSt */ public func withUnretained( _ obj: Object, - resultSelector: @escaping (Object, Element) -> Out + resultSelector: @escaping @Sendable (Object, Element) -> Out ) -> SharedSequence { SharedSequence(self.asObservable().withUnretained(obj, resultSelector: resultSelector)) } @@ -482,7 +482,7 @@ extension SharedSequenceConvertibleType where SharingStrategy == DriverSharingSt @available(*, message: "withUnretained has been deprecated for Driver. Consider using `drive(with:onNext:onCompleted:onDisposed:)`, instead", unavailable) public func withUnretained( _ obj: Object, - resultSelector: @escaping (Object, Element) -> Out + resultSelector: @escaping @Sendable (Object, Element) -> Out ) -> SharedSequence { SharedSequence(self.asObservable().withUnretained(obj, resultSelector: resultSelector)) } @@ -503,7 +503,7 @@ extension SharedSequenceConvertibleType { - parameter resultSelector: Function to invoke for each element from the self combined with the latest element from the second source, if any. - returns: An observable sequence containing the result of combining each element of the self with the latest element from the second source, if any, using the specified result selector function. */ - public func withLatestFrom(_ second: SecondO, resultSelector: @escaping (Element, SecondO.Element) -> ResultType) -> SharedSequence where SecondO.SharingStrategy == SharingStrategy { + public func withLatestFrom(_ second: SecondO, resultSelector: @escaping @Sendable (Element, SecondO.Element) -> ResultType) -> SharedSequence where SecondO.SharingStrategy == SharingStrategy { let source = self.asObservable() .withLatestFrom(second.asSharedSequence(), resultSelector: resultSelector) diff --git a/RxCocoa/Traits/SharedSequence/SharedSequence.swift b/RxCocoa/Traits/SharedSequence/SharedSequence.swift index 4596c8ec0..328d9085e 100644 --- a/RxCocoa/Traits/SharedSequence/SharedSequence.swift +++ b/RxCocoa/Traits/SharedSequence/SharedSequence.swift @@ -136,7 +136,7 @@ extension SharedSequence { - parameter observableFactory: Observable factory function to invoke for each observer that subscribes to the resulting sequence. - returns: An observable sequence whose observers trigger an invocation of the given observable factory function. */ - public static func deferred(_ observableFactory: @escaping () -> SharedSequence) + public static func deferred(_ observableFactory: @escaping @Sendable () -> SharedSequence) -> SharedSequence { SharedSequence(Observable.deferred { observableFactory().asObservable() }) } diff --git a/RxCocoa/Traits/Signal/ObservableConvertibleType+Signal.swift b/RxCocoa/Traits/Signal/ObservableConvertibleType+Signal.swift index b962d4ed0..94b5c7d4e 100644 --- a/RxCocoa/Traits/Signal/ObservableConvertibleType+Signal.swift +++ b/RxCocoa/Traits/Signal/ObservableConvertibleType+Signal.swift @@ -45,12 +45,12 @@ extension ObservableConvertibleType { - parameter onErrorRecover: Calculates signal that continues to emit the sequence in case of error. - returns: Signal trait. */ - public func asSignal(onErrorRecover: @escaping (_ error: Swift.Error) -> Signal) -> Signal { + public func asSignal(onErrorRecover: @escaping @Sendable @MainActor (_ error: Swift.Error) -> Signal) -> Signal { let source = self .asObservable() .observe(on: SignalSharingStrategy.scheduler) .catch { error in - onErrorRecover(error).asObservable() + MainScheduler.assumeMainActor(onErrorRecover)(error).asObservable() } return Signal(source) } diff --git a/RxCocoa/Traits/Signal/Signal+Subscription.swift b/RxCocoa/Traits/Signal/Signal+Subscription.swift index 4a6add336..2e0d52f01 100644 --- a/RxCocoa/Traits/Signal/Signal+Subscription.swift +++ b/RxCocoa/Traits/Signal/Signal+Subscription.swift @@ -132,14 +132,14 @@ extension SharedSequenceConvertibleType where SharingStrategy == SignalSharingSt */ public func emit( with object: Object, - onNext: ((Object, Element) -> Void)? = nil, - onCompleted: ((Object) -> Void)? = nil, - onDisposed: ((Object) -> Void)? = nil + onNext: (@Sendable @MainActor (Object, Element) -> Void)? = nil, + onCompleted: (@Sendable @MainActor (Object) -> Void)? = nil, + onDisposed: (@Sendable (Object) -> Void)? = nil ) -> Disposable { self.asObservable().subscribe( with: object, - onNext: onNext, - onCompleted: onCompleted, + onNext: onNext.flatMap({ MainScheduler.assumeMainActor($0) }), + onCompleted: onCompleted.flatMap({ MainScheduler.assumeMainActor($0) }), onDisposed: onDisposed ) } @@ -157,11 +157,15 @@ extension SharedSequenceConvertibleType where SharingStrategy == SignalSharingSt - returns: Subscription object used to unsubscribe from the observable sequence. */ public func emit( - onNext: ((Element) -> Void)? = nil, - onCompleted: (() -> Void)? = nil, - onDisposed: (() -> Void)? = nil + onNext: (@Sendable @MainActor (Element) -> Void)? = nil, + onCompleted: (@Sendable @MainActor () -> Void)? = nil, + onDisposed: (@Sendable () -> Void)? = nil ) -> Disposable { - self.asObservable().subscribe(onNext: onNext, onCompleted: onCompleted, onDisposed: onDisposed) + self.asObservable().subscribe( + onNext: onNext.flatMap({ MainScheduler.assumeMainActor($0) }), + onCompleted: onCompleted.flatMap({ MainScheduler.assumeMainActor($0) }), + onDisposed: onDisposed + ) } /** diff --git a/RxCocoa/iOS/DataSources/RxCollectionViewReactiveArrayDataSource.swift b/RxCocoa/iOS/DataSources/RxCollectionViewReactiveArrayDataSource.swift index a5fcfd1b1..89c30690b 100644 --- a/RxCocoa/iOS/DataSources/RxCollectionViewReactiveArrayDataSource.swift +++ b/RxCocoa/iOS/DataSources/RxCollectionViewReactiveArrayDataSource.swift @@ -50,7 +50,9 @@ class RxCollectionViewReactiveArrayDataSourceSequenceWrapper) { Binder(self) { collectionViewDataSource, sectionModels in let sections = Array(sectionModels) - collectionViewDataSource.collectionView(collectionView, observedElements: sections) + MainScheduler.assumeMainActor(execute: { + collectionViewDataSource.collectionView(collectionView, observedElements: sections) + }) }.on(observedEvent) } } @@ -61,7 +63,7 @@ class RxCollectionViewReactiveArrayDataSource : _RxCollectionViewReactiveArrayDataSource , SectionedViewDataSourceType { - typealias CellFactory = (UICollectionView, Int, Element) -> UICollectionViewCell + typealias CellFactory = @MainActor (UICollectionView, Int, Element) -> UICollectionViewCell var itemModels: [Element]? diff --git a/RxCocoa/iOS/DataSources/RxPickerViewAdapter.swift b/RxCocoa/iOS/DataSources/RxPickerViewAdapter.swift index 9142e96da..67b7afa65 100644 --- a/RxCocoa/iOS/DataSources/RxPickerViewAdapter.swift +++ b/RxCocoa/iOS/DataSources/RxPickerViewAdapter.swift @@ -12,7 +12,7 @@ import UIKit import RxSwift class RxPickerViewArrayDataSource: NSObject, UIPickerViewDataSource, SectionedViewDataSourceType { - fileprivate var items: [T] = [] + nonisolated(unsafe) fileprivate var items: [T] = [] func model(at indexPath: IndexPath) throws -> Any { guard items.indices ~= indexPath.row else { @@ -38,7 +38,9 @@ class RxPickerViewSequenceDataSource func pickerView(_ pickerView: UIPickerView, observedEvent: Event) { Binder(self) { dataSource, items in dataSource.items = items - pickerView.reloadAllComponents() + MainScheduler.assumeMainActor(execute: { + pickerView.reloadAllComponents() + }) } .on(observedEvent.map(Array.init)) } @@ -48,7 +50,7 @@ final class RxStringPickerViewAdapter : RxPickerViewSequenceDataSource , UIPickerViewDelegate { - typealias TitleForRow = (Int, Sequence.Element) -> String? + typealias TitleForRow = @MainActor (Int, Sequence.Element) -> String? private let titleForRow: TitleForRow init(titleForRow: @escaping TitleForRow) { @@ -62,7 +64,7 @@ final class RxStringPickerViewAdapter } final class RxAttributedStringPickerViewAdapter: RxPickerViewSequenceDataSource, UIPickerViewDelegate { - typealias AttributedTitleForRow = (Int, Sequence.Element) -> NSAttributedString? + typealias AttributedTitleForRow = @MainActor (Int, Sequence.Element) -> NSAttributedString? private let attributedTitleForRow: AttributedTitleForRow init(attributedTitleForRow: @escaping AttributedTitleForRow) { @@ -76,7 +78,7 @@ final class RxAttributedStringPickerViewAdapter: RxPic } final class RxPickerViewAdapter: RxPickerViewSequenceDataSource, UIPickerViewDelegate { - typealias ViewForRow = (Int, Sequence.Element, UIView?) -> UIView + typealias ViewForRow = @MainActor (Int, Sequence.Element, UIView?) -> UIView private let viewForRow: ViewForRow init(viewForRow: @escaping ViewForRow) { diff --git a/RxCocoa/iOS/DataSources/RxTableViewReactiveArrayDataSource.swift b/RxCocoa/iOS/DataSources/RxTableViewReactiveArrayDataSource.swift index f77eec8f8..8699dfc32 100644 --- a/RxCocoa/iOS/DataSources/RxTableViewReactiveArrayDataSource.swift +++ b/RxCocoa/iOS/DataSources/RxTableViewReactiveArrayDataSource.swift @@ -50,7 +50,9 @@ class RxTableViewReactiveArrayDataSourceSequenceWrapper) { Binder(self) { tableViewDataSource, sectionModels in let sections = Array(sectionModels) - tableViewDataSource.tableView(tableView, observedElements: sections) + MainScheduler.assumeMainActor(execute: { + tableViewDataSource.tableView(tableView, observedElements: sections) + }) }.on(observedEvent) } } @@ -59,7 +61,7 @@ class RxTableViewReactiveArrayDataSourceSequenceWrapper : _RxTableViewReactiveArrayDataSource , SectionedViewDataSourceType { - typealias CellFactory = (UITableView, Int, Element) -> UITableViewCell + typealias CellFactory = @MainActor (UITableView, Int, Element) -> UITableViewCell var itemModels: [Element]? diff --git a/RxCocoa/iOS/Proxies/RxCollectionViewDelegateProxy.swift b/RxCocoa/iOS/Proxies/RxCollectionViewDelegateProxy.swift index 687e3b61b..c4967a9d0 100644 --- a/RxCocoa/iOS/Proxies/RxCollectionViewDelegateProxy.swift +++ b/RxCocoa/iOS/Proxies/RxCollectionViewDelegateProxy.swift @@ -21,6 +21,7 @@ open class RxCollectionViewDelegateProxy /// Initializes `RxCollectionViewDelegateProxy` /// /// - parameter collectionView: Parent object for delegate proxy. + nonisolated public init(collectionView: UICollectionView) { self.collectionView = collectionView super.init(scrollView: collectionView) diff --git a/RxCocoa/iOS/Proxies/RxTableViewDelegateProxy.swift b/RxCocoa/iOS/Proxies/RxTableViewDelegateProxy.swift index 51094e4cf..d7e42b412 100644 --- a/RxCocoa/iOS/Proxies/RxTableViewDelegateProxy.swift +++ b/RxCocoa/iOS/Proxies/RxTableViewDelegateProxy.swift @@ -19,6 +19,7 @@ open class RxTableViewDelegateProxy public weak private(set) var tableView: UITableView? /// - parameter tableView: Parent object for delegate proxy. + nonisolated public init(tableView: UITableView) { self.tableView = tableView super.init(scrollView: tableView) diff --git a/RxCocoa/iOS/Proxies/RxTextViewDelegateProxy.swift b/RxCocoa/iOS/Proxies/RxTextViewDelegateProxy.swift index 22b574ce1..8f3f4484a 100644 --- a/RxCocoa/iOS/Proxies/RxTextViewDelegateProxy.swift +++ b/RxCocoa/iOS/Proxies/RxTextViewDelegateProxy.swift @@ -19,6 +19,7 @@ open class RxTextViewDelegateProxy public weak private(set) var textView: UITextView? /// - parameter textview: Parent object for delegate proxy. + nonisolated public init(textView: UITextView) { self.textView = textView super.init(scrollView: textView) diff --git a/RxCocoa/iOS/UIActivityIndicatorView+Rx.swift b/RxCocoa/iOS/UIActivityIndicatorView+Rx.swift index 5f1876b8f..5e049abf7 100644 --- a/RxCocoa/iOS/UIActivityIndicatorView+Rx.swift +++ b/RxCocoa/iOS/UIActivityIndicatorView+Rx.swift @@ -14,13 +14,17 @@ import RxSwift extension Reactive where Base: UIActivityIndicatorView { /// Bindable sink for `startAnimating()`, `stopAnimating()` methods. public var isAnimating: Binder { - Binder(self.base) { activityIndicator, active in - if active { - activityIndicator.startAnimating() - } else { - activityIndicator.stopAnimating() + MainScheduler.assumeMainActor(execute: { + Binder(self.base) { activityIndicator, active in + MainScheduler.assumeMainActor(execute: { + if active { + activityIndicator.startAnimating() + } else { + activityIndicator.stopAnimating() + } + }) } - } + }) } } diff --git a/RxCocoa/iOS/UIApplication+Rx.swift b/RxCocoa/iOS/UIApplication+Rx.swift index a8a623e77..35e594aaf 100644 --- a/RxCocoa/iOS/UIApplication+Rx.swift +++ b/RxCocoa/iOS/UIApplication+Rx.swift @@ -17,7 +17,7 @@ extension Reactive where Base: UIApplication { /// Bindable sink for `isNetworkActivityIndicatorVisible`. public var isNetworkActivityIndicatorVisible: Binder { return Binder(self.base) { application, active in - application.isNetworkActivityIndicatorVisible = active + MainScheduler.assumeMainActor(execute: { application.isNetworkActivityIndicatorVisible = active }) } } } diff --git a/RxCocoa/iOS/UIBarButtonItem+Rx.swift b/RxCocoa/iOS/UIBarButtonItem+Rx.swift index 1c79390e9..85f0c17c7 100644 --- a/RxCocoa/iOS/UIBarButtonItem+Rx.swift +++ b/RxCocoa/iOS/UIBarButtonItem+Rx.swift @@ -22,9 +22,11 @@ extension Reactive where Base: UIBarButtonItem { observer.on(.completed) return Disposables.create() } - let target = BarButtonItemTarget(barButtonItem: control) { - observer.on(.next(())) - } + let target = MainScheduler.assumeMainActor(execute: { + return BarButtonItemTarget(barButtonItem: control) { + observer.on(.next(())) + } + }) return target } .take(until: self.deallocated) @@ -36,14 +38,14 @@ extension Reactive where Base: UIBarButtonItem { } -@objc -final class BarButtonItemTarget: RxTarget { +@objc @MainActor +final class BarButtonItemTarget: RxTarget, @unchecked Sendable { typealias Callback = () -> Void weak var barButtonItem: UIBarButtonItem? var callback: Callback! - init(barButtonItem: UIBarButtonItem, callback: @escaping () -> Void) { + init(barButtonItem: UIBarButtonItem, callback: @escaping @Sendable () -> Void) { self.barButtonItem = barButtonItem self.callback = callback super.init() @@ -56,11 +58,12 @@ final class BarButtonItemTarget: RxTarget { #if DEBUG MainScheduler.ensureRunningOnMainThread() #endif - - barButtonItem?.target = nil - barButtonItem?.action = nil - - callback = nil + MainScheduler.assumeMainActor(execute: { + barButtonItem?.target = nil + barButtonItem?.action = nil + + callback = nil + }) } @objc func action(_ sender: AnyObject) { diff --git a/RxCocoa/iOS/UIButton+Rx.swift b/RxCocoa/iOS/UIButton+Rx.swift index d525ec088..49df2fc2d 100644 --- a/RxCocoa/iOS/UIButton+Rx.swift +++ b/RxCocoa/iOS/UIButton+Rx.swift @@ -46,21 +46,27 @@ extension Reactive where Base: UIButton { /// Reactive wrapper for `setTitle(_:for:)` public func title(for controlState: UIControl.State = []) -> Binder { Binder(self.base) { button, title in - button.setTitle(title, for: controlState) + MainScheduler.assumeMainActor(execute: { + button.setTitle(title, for: controlState) + }) } } /// Reactive wrapper for `setImage(_:for:)` public func image(for controlState: UIControl.State = []) -> Binder { Binder(self.base) { button, image in - button.setImage(image, for: controlState) + MainScheduler.assumeMainActor(execute: { + button.setImage(image, for: controlState) + }) } } /// Reactive wrapper for `setBackgroundImage(_:for:)` public func backgroundImage(for controlState: UIControl.State = []) -> Binder { Binder(self.base) { button, image in - button.setBackgroundImage(image, for: controlState) + MainScheduler.assumeMainActor(execute: { + button.setBackgroundImage(image, for: controlState) + }) } } @@ -75,7 +81,9 @@ extension Reactive where Base: UIButton { /// Reactive wrapper for `setAttributedTitle(_:controlState:)` public func attributedTitle(for controlState: UIControl.State = []) -> Binder { return Binder(self.base) { button, attributedTitle -> Void in - button.setAttributedTitle(attributedTitle, for: controlState) + MainScheduler.assumeMainActor(execute: { + button.setAttributedTitle(attributedTitle, for: controlState) + }) } } } diff --git a/RxCocoa/iOS/UICollectionView+Rx.swift b/RxCocoa/iOS/UICollectionView+Rx.swift index 96d69c5bb..748fa14b0 100644 --- a/RxCocoa/iOS/UICollectionView+Rx.swift +++ b/RxCocoa/iOS/UICollectionView+Rx.swift @@ -41,7 +41,7 @@ extension Reactive where Base: UICollectionView { */ public func items (_ source: Source) - -> (_ cellFactory: @escaping (UICollectionView, Int, Sequence.Element) -> UICollectionViewCell) + -> (_ cellFactory: @escaping @Sendable (UICollectionView, Int, Sequence.Element) -> UICollectionViewCell) -> Disposable where Source.Element == Sequence { return { cellFactory in let dataSource = RxCollectionViewReactiveArrayDataSourceSequenceWrapper(cellFactory: cellFactory) @@ -76,7 +76,7 @@ extension Reactive where Base: UICollectionView { public func items (cellIdentifier: String, cellType: Cell.Type = Cell.self) -> (_ source: Source) - -> (_ configureCell: @escaping (Int, Sequence.Element, Cell) -> Void) + -> (_ configureCell: @escaping @Sendable (Int, Sequence.Element, Cell) -> Void) -> Disposable where Source.Element == Sequence { return { source in return { configureCell in diff --git a/RxCocoa/iOS/UIControl+Rx.swift b/RxCocoa/iOS/UIControl+Rx.swift index bf4d96882..1ef501edf 100644 --- a/RxCocoa/iOS/UIControl+Rx.swift +++ b/RxCocoa/iOS/UIControl+Rx.swift @@ -24,11 +24,13 @@ extension Reactive where Base: UIControl { return Disposables.create() } - let controlTarget = ControlTarget(control: control, controlEvents: controlEvents) { _ in - observer.on(.next(())) - } - - return Disposables.create(with: controlTarget.dispose) + return MainScheduler.assumeMainActor(execute: { + let controlTarget = ControlTarget(control: control, controlEvents: controlEvents) { _ in + observer.on(.next(())) + } + + return Disposables.create(with: controlTarget.dispose) + }) } .take(until: deallocated) @@ -42,8 +44,8 @@ extension Reactive where Base: UIControl { /// - parameter setter: Property value setter. public func controlProperty( editingEvents: UIControl.Event, - getter: @escaping (Base) -> T, - setter: @escaping (Base, T) -> Void + getter: @escaping @Sendable @MainActor (Base) -> T, + setter: @escaping @Sendable @MainActor (Base, T) -> Void ) -> ControlProperty { let source: Observable = Observable.create { [weak weakControl = base] observer in guard let control = weakControl else { @@ -51,19 +53,21 @@ extension Reactive where Base: UIControl { return Disposables.create() } - observer.on(.next(getter(control))) - - let controlTarget = ControlTarget(control: control, controlEvents: editingEvents) { _ in - if let control = weakControl { - observer.on(.next(getter(control))) - } - } + observer.on(.next(MainScheduler.assumeMainActor(execute: { getter(control) }))) - return Disposables.create(with: controlTarget.dispose) + return MainScheduler.assumeMainActor(execute: { + let controlTarget = ControlTarget(control: control, controlEvents: editingEvents) { [weak weakControl] _ in + if let control = weakControl { + observer.on(.next(MainScheduler.assumeMainActor(execute: { getter(control) }))) + } + } + + return Disposables.create(with: controlTarget.dispose) + }) } .take(until: deallocated) - let bindingObserver = Binder(base, binding: setter) + let bindingObserver = Binder(base, binding: MainScheduler.assumeMainActor(setter)) return ControlProperty(values: source, valueSink: bindingObserver) } @@ -72,8 +76,8 @@ extension Reactive where Base: UIControl { /// an `editingEvent` needs to fire for control property to be updated. internal func controlPropertyWithDefaultEvents( editingEvents: UIControl.Event = [.allEditingEvents, .valueChanged], - getter: @escaping (Base) -> T, - setter: @escaping (Base, T) -> Void + getter: @escaping @Sendable @MainActor (Base) -> T, + setter: @escaping @Sendable @MainActor (Base, T) -> Void ) -> ControlProperty { return controlProperty( editingEvents: editingEvents, diff --git a/RxCocoa/iOS/UIGestureRecognizer+Rx.swift b/RxCocoa/iOS/UIGestureRecognizer+Rx.swift index 7dba52024..0926a4ceb 100644 --- a/RxCocoa/iOS/UIGestureRecognizer+Rx.swift +++ b/RxCocoa/iOS/UIGestureRecognizer+Rx.swift @@ -12,8 +12,9 @@ import UIKit import RxSwift // This should be only used from `MainScheduler` -final class GestureTarget: RxTarget { - typealias Callback = (Recognizer) -> Void +@MainActor +final class GestureTarget: RxTarget, @unchecked Sendable { + typealias Callback = @Sendable @MainActor (Recognizer) -> Void let selector = #selector(GestureTarget.eventHandler(_:)) @@ -43,8 +44,10 @@ final class GestureTarget: RxTarget { override func dispose() { super.dispose() - self.gestureRecognizer?.removeTarget(self, action: self.selector) - self.callback = nil + MainScheduler.assumeMainActor(execute: { + self.gestureRecognizer?.removeTarget(self, action: self.selector) + self.callback = nil + }) } } @@ -60,9 +63,11 @@ extension Reactive where Base: UIGestureRecognizer { return Disposables.create() } - let observer = GestureTarget(control) { control in - observer.on(.next(control)) - } + let observer = MainScheduler.assumeMainActor(execute: { + return GestureTarget(control) { control in + observer.on(.next(control)) + } + }) return observer }.take(until: deallocated) diff --git a/RxCocoa/iOS/UIPickerView+Rx.swift b/RxCocoa/iOS/UIPickerView+Rx.swift index 92f998cf1..4704ca2cc 100644 --- a/RxCocoa/iOS/UIPickerView+Rx.swift +++ b/RxCocoa/iOS/UIPickerView+Rx.swift @@ -69,13 +69,14 @@ guard let view = view else { return Observable.empty() } - - let model: [T] = try (0 ..< view.numberOfComponents).map { component in - let row = view.selectedRow(inComponent: component) - return try view.rx.model(at: IndexPath(row: row, section: component)) - } - - return Observable.just(model) + return try MainScheduler.assumeMainActor(execute: { + let model: [T] = try (0 ..< view.numberOfComponents).map { component in + let row = view.selectedRow(inComponent: component) + return try view.rx.model(at: IndexPath(row: row, section: component)) + } + + return Observable.just(model) + }) } return ControlEvent(events: source) @@ -106,7 +107,7 @@ public func itemTitles (_ source: Source) - -> (_ titleForRow: @escaping (Int, Sequence.Element) -> String?) + -> (_ titleForRow: @escaping @Sendable (Int, Sequence.Element) -> String?) -> Disposable where Source.Element == Sequence { return { titleForRow in let adapter = RxStringPickerViewAdapter(titleForRow: titleForRow) @@ -139,7 +140,7 @@ public func itemAttributedTitles (_ source: Source) - -> (_ attributedTitleForRow: @escaping (Int, Sequence.Element) -> NSAttributedString?) + -> (_ attributedTitleForRow: @escaping @Sendable (Int, Sequence.Element) -> NSAttributedString?) -> Disposable where Source.Element == Sequence { return { attributedTitleForRow in let adapter = RxAttributedStringPickerViewAdapter(attributedTitleForRow: attributedTitleForRow) @@ -178,7 +179,7 @@ public func items (_ source: Source) - -> (_ viewForRow: @escaping (Int, Sequence.Element, UIView?) -> UIView) + -> (_ viewForRow: @escaping @Sendable (Int, Sequence.Element, UIView?) -> UIView) -> Disposable where Source.Element == Sequence { return { viewForRow in let adapter = RxPickerViewAdapter(viewForRow: viewForRow) diff --git a/RxCocoa/iOS/UIRefreshControl+Rx.swift b/RxCocoa/iOS/UIRefreshControl+Rx.swift index adb484f13..eff23021c 100644 --- a/RxCocoa/iOS/UIRefreshControl+Rx.swift +++ b/RxCocoa/iOS/UIRefreshControl+Rx.swift @@ -15,11 +15,13 @@ extension Reactive where Base: UIRefreshControl { /// Bindable sink for `beginRefreshing()`, `endRefreshing()` methods. public var isRefreshing: Binder { return Binder(self.base) { refreshControl, refresh in - if refresh { - refreshControl.beginRefreshing() - } else { - refreshControl.endRefreshing() - } + MainScheduler.assumeMainActor(execute: { + if refresh { + refreshControl.beginRefreshing() + } else { + refreshControl.endRefreshing() + } + }) } } diff --git a/RxCocoa/iOS/UIScrollView+Rx.swift b/RxCocoa/iOS/UIScrollView+Rx.swift index dc53c02fd..e7b400299 100644 --- a/RxCocoa/iOS/UIScrollView+Rx.swift +++ b/RxCocoa/iOS/UIScrollView+Rx.swift @@ -27,7 +27,9 @@ let proxy = RxScrollViewDelegateProxy.proxy(for: base) let bindingObserver = Binder(self.base) { scrollView, contentOffset in - scrollView.contentOffset = contentOffset + MainScheduler.assumeMainActor(execute: { + scrollView.contentOffset = contentOffset + }) } return ControlProperty(values: proxy.contentOffsetBehaviorSubject, valueSink: bindingObserver) diff --git a/RxCocoa/iOS/UISearchBar+Rx.swift b/RxCocoa/iOS/UISearchBar+Rx.swift index 1d0229677..6b614e726 100644 --- a/RxCocoa/iOS/UISearchBar+Rx.swift +++ b/RxCocoa/iOS/UISearchBar+Rx.swift @@ -28,18 +28,26 @@ extension Reactive where Base: UISearchBar { /// Reactive wrapper for `text` property. public var value: ControlProperty { let source: Observable = Observable.deferred { [weak searchBar = self.base as UISearchBar] () -> Observable in - let text = searchBar?.text - - let textDidChange = (searchBar?.rx.delegate.methodInvoked(#selector(UISearchBarDelegate.searchBar(_:textDidChange:))) ?? Observable.empty()) - let didEndEditing = (searchBar?.rx.delegate.methodInvoked(#selector(UISearchBarDelegate.searchBarTextDidEndEditing(_:))) ?? Observable.empty()) - - return Observable.merge(textDidChange, didEndEditing) - .map { _ in searchBar?.text ?? "" } + return MainScheduler.assumeMainActor(execute: { + let text = searchBar?.text + + let textDidChange = (searchBar?.rx.delegate.methodInvoked(#selector(UISearchBarDelegate.searchBar(_:textDidChange:))) ?? Observable.empty()) + let didEndEditing = (searchBar?.rx.delegate.methodInvoked(#selector(UISearchBarDelegate.searchBarTextDidEndEditing(_:))) ?? Observable.empty()) + + return Observable.merge(textDidChange, didEndEditing) + .map { [weak searchBar] _ in + MainScheduler.assumeMainActor(execute: { [weak searchBar] in + searchBar?.text ?? "" + }) + } .startWith(text) + }) } let bindingObserver = Binder(self.base) { (searchBar, text: String?) in - searchBar.text = text + MainScheduler.assumeMainActor(execute: { + searchBar.text = text + }) } return ControlProperty(values: source, valueSink: bindingObserver) @@ -48,7 +56,7 @@ extension Reactive where Base: UISearchBar { /// Reactive wrapper for `selectedScopeButtonIndex` property. public var selectedScopeButtonIndex: ControlProperty { let source: Observable = Observable.deferred { [weak source = self.base as UISearchBar] () -> Observable in - let index = source?.selectedScopeButtonIndex ?? 0 + let index = MainScheduler.assumeMainActor(execute: { source?.selectedScopeButtonIndex ?? 0 }) return (source?.rx.delegate.methodInvoked(#selector(UISearchBarDelegate.searchBar(_:selectedScopeButtonIndexDidChange:))) ?? Observable.empty()) .map { a in @@ -58,7 +66,7 @@ extension Reactive where Base: UISearchBar { } let bindingObserver = Binder(self.base) { (searchBar, index: Int) in - searchBar.selectedScopeButtonIndex = index + MainScheduler.assumeMainActor(execute: { searchBar.selectedScopeButtonIndex = index }) } return ControlProperty(values: source, valueSink: bindingObserver) diff --git a/RxCocoa/iOS/UISegmentedControl+Rx.swift b/RxCocoa/iOS/UISegmentedControl+Rx.swift index 613d2e179..d28bbb40c 100644 --- a/RxCocoa/iOS/UISegmentedControl+Rx.swift +++ b/RxCocoa/iOS/UISegmentedControl+Rx.swift @@ -31,21 +31,27 @@ extension Reactive where Base: UISegmentedControl { /// Reactive wrapper for `setEnabled(_:forSegmentAt:)` public func enabledForSegment(at index: Int) -> Binder { return Binder(self.base) { segmentedControl, segmentEnabled -> Void in - segmentedControl.setEnabled(segmentEnabled, forSegmentAt: index) + MainScheduler.assumeMainActor(execute: { + segmentedControl.setEnabled(segmentEnabled, forSegmentAt: index) + }) } } /// Reactive wrapper for `setTitle(_:forSegmentAt:)` public func titleForSegment(at index: Int) -> Binder { return Binder(self.base) { segmentedControl, title -> Void in - segmentedControl.setTitle(title, forSegmentAt: index) + MainScheduler.assumeMainActor(execute: { + segmentedControl.setTitle(title, forSegmentAt: index) + }) } } /// Reactive wrapper for `setImage(_:forSegmentAt:)` public func imageForSegment(at index: Int) -> Binder { return Binder(self.base) { segmentedControl, image -> Void in - segmentedControl.setImage(image, forSegmentAt: index) + MainScheduler.assumeMainActor(execute: { + segmentedControl.setImage(image, forSegmentAt: index) + }) } } diff --git a/RxCocoa/iOS/UITableView+Rx.swift b/RxCocoa/iOS/UITableView+Rx.swift index deae7ffe4..7ad6f68bf 100644 --- a/RxCocoa/iOS/UITableView+Rx.swift +++ b/RxCocoa/iOS/UITableView+Rx.swift @@ -41,7 +41,7 @@ extension Reactive where Base: UITableView { */ public func items (_ source: Source) - -> (_ cellFactory: @escaping (UITableView, Int, Sequence.Element) -> UITableViewCell) + -> (_ cellFactory: @escaping @Sendable (UITableView, Int, Sequence.Element) -> UITableViewCell) -> Disposable where Source.Element == Sequence { return { cellFactory in @@ -76,7 +76,7 @@ extension Reactive where Base: UITableView { public func items (cellIdentifier: String, cellType: Cell.Type = Cell.self) -> (_ source: Source) - -> (_ configureCell: @escaping (Int, Sequence.Element, Cell) -> Void) + -> (_ configureCell: @escaping @Sendable (Int, Sequence.Element, Cell) -> Void) -> Disposable where Source.Element == Sequence { return { source in diff --git a/RxCocoa/iOS/UITextView+Rx.swift b/RxCocoa/iOS/UITextView+Rx.swift index 35e219425..c0be60819 100644 --- a/RxCocoa/iOS/UITextView+Rx.swift +++ b/RxCocoa/iOS/UITextView+Rx.swift @@ -20,33 +20,39 @@ extension Reactive where Base: UITextView { /// Reactive wrapper for `text` property. public var value: ControlProperty { let source: Observable = Observable.deferred { [weak textView = self.base] in - let text = textView?.text - - let textChanged = textView?.textStorage + MainScheduler.assumeMainActor(execute: { [weak textView] in + let text = textView?.text + + let textChanged = textView?.textStorage // This project uses text storage notifications because // that's the only way to catch autocorrect changes // in all cases. Other suggestions are welcome. - .rx.didProcessEditingRangeChangeInLength + .rx.didProcessEditingRangeChangeInLength // This observe on is here because text storage // will emit event while process is not completely done, // so rebinding a value will cause an exception to be thrown. - .observe(on:MainScheduler.asyncInstance) - .map { _ in - return textView?.textStorage.string - } + .observe(on:MainScheduler.asyncInstance) + .map { [weak textView] _ in + MainScheduler.assumeMainActor(execute: { [weak textView] in + return textView?.textStorage.string + }) + } ?? Observable.empty() - - return textChanged - .startWith(text) + + return textChanged + .startWith(text) + }) } let bindingObserver = Binder(self.base) { (textView, text: String?) in // This check is important because setting text value always clears control state // including marked text selection which is important for proper input // when IME input method is used. - if textView.text != text { - textView.text = text - } + MainScheduler.assumeMainActor(execute: { + if textView.text != text { + textView.text = text + } + }) } return ControlProperty(values: source, valueSink: bindingObserver) @@ -56,33 +62,39 @@ extension Reactive where Base: UITextView { /// Reactive wrapper for `attributedText` property. public var attributedText: ControlProperty { let source: Observable = Observable.deferred { [weak textView = self.base] in - let attributedText = textView?.attributedText - - let textChanged: Observable = textView?.textStorage + return MainScheduler.assumeMainActor(execute: { + let attributedText = textView?.attributedText + + let textChanged: Observable = textView?.textStorage // This project uses text storage notifications because // that's the only way to catch autocorrect changes // in all cases. Other suggestions are welcome. - .rx.didProcessEditingRangeChangeInLength + .rx.didProcessEditingRangeChangeInLength // This observe on is here because attributedText storage // will emit event while process is not completely done, // so rebinding a value will cause an exception to be thrown. - .observe(on:MainScheduler.asyncInstance) - .map { _ in - return textView?.attributedText - } + .observe(on:MainScheduler.asyncInstance) + .map { [weak textView] _ in + MainScheduler.assumeMainActor(execute: { + return textView?.attributedText + }) + } ?? Observable.empty() - - return textChanged - .startWith(attributedText) + + return textChanged + .startWith(attributedText) + }) } let bindingObserver = Binder(self.base) { (textView, attributedText: NSAttributedString?) in // This check is important because setting text value always clears control state // including marked text selection which is important for proper input // when IME input method is used. - if textView.attributedText != attributedText { - textView.attributedText = attributedText - } + MainScheduler.assumeMainActor(execute: { + if textView.attributedText != attributedText { + textView.attributedText = attributedText + } + }) } return ControlProperty(values: source, valueSink: bindingObserver) diff --git a/RxCocoa/macOS/NSControl+Rx.swift b/RxCocoa/macOS/NSControl+Rx.swift index 534f40cab..fe3142a3f 100644 --- a/RxCocoa/macOS/NSControl+Rx.swift +++ b/RxCocoa/macOS/NSControl+Rx.swift @@ -47,8 +47,8 @@ extension Reactive where Base: NSControl { /// - parameter getter: Property value getter. /// - parameter setter: Property value setter. public func controlProperty( - getter: @escaping (Base) -> T, - setter: @escaping (Base, T) -> Void + getter: @escaping @Sendable (Base) -> T, + setter: @escaping @Sendable (Base, T) -> Void ) -> ControlProperty { MainScheduler.ensureRunningOnMainThread() diff --git a/RxExample/Extensions/RxCLLocationManagerDelegateProxy.swift b/RxExample/Extensions/RxCLLocationManagerDelegateProxy.swift index 3efa0940d..9607ea367 100644 --- a/RxExample/Extensions/RxCLLocationManagerDelegateProxy.swift +++ b/RxExample/Extensions/RxCLLocationManagerDelegateProxy.swift @@ -10,7 +10,7 @@ import CoreLocation import RxSwift import RxCocoa -extension CLLocationManager: HasDelegate { +extension CLLocationManager: @retroactive HasDelegate { public typealias Delegate = CLLocationManagerDelegate } diff --git a/RxExample/Extensions/RxImagePickerDelegateProxy.swift b/RxExample/Extensions/RxImagePickerDelegateProxy.swift index ebaab6ff0..5329602c3 100644 --- a/RxExample/Extensions/RxImagePickerDelegateProxy.swift +++ b/RxExample/Extensions/RxImagePickerDelegateProxy.swift @@ -15,6 +15,7 @@ import UIKit open class RxImagePickerDelegateProxy : RxNavigationControllerDelegateProxy, UIImagePickerControllerDelegate { + nonisolated public init(imagePicker: UIImagePickerController) { super.init(navigationController: imagePicker) } diff --git a/RxExample/RxDataSources/RxDataSources/TableViewSectionedDataSource.swift b/RxExample/RxDataSources/RxDataSources/TableViewSectionedDataSource.swift index e92da0bdd..91d0abe9d 100644 --- a/RxExample/RxDataSources/RxDataSources/TableViewSectionedDataSource.swift +++ b/RxExample/RxDataSources/RxDataSources/TableViewSectionedDataSource.swift @@ -19,21 +19,21 @@ open class TableViewSectionedDataSource public typealias Item = Section.Item - public typealias ConfigureCell = (TableViewSectionedDataSource
, UITableView, IndexPath, Item) -> UITableViewCell - public typealias TitleForHeaderInSection = (TableViewSectionedDataSource
, Int) -> String? - public typealias TitleForFooterInSection = (TableViewSectionedDataSource
, Int) -> String? - public typealias CanEditRowAtIndexPath = (TableViewSectionedDataSource
, IndexPath) -> Bool - public typealias CanMoveRowAtIndexPath = (TableViewSectionedDataSource
, IndexPath) -> Bool + public typealias ConfigureCell = @Sendable @MainActor (TableViewSectionedDataSource
, UITableView, IndexPath, Item) -> UITableViewCell + public typealias TitleForHeaderInSection = @Sendable @MainActor (TableViewSectionedDataSource
, Int) -> String? + public typealias TitleForFooterInSection = @Sendable @MainActor (TableViewSectionedDataSource
, Int) -> String? + public typealias CanEditRowAtIndexPath = @Sendable @MainActor (TableViewSectionedDataSource
, IndexPath) -> Bool + public typealias CanMoveRowAtIndexPath = @Sendable @MainActor (TableViewSectionedDataSource
, IndexPath) -> Bool #if os(iOS) - public typealias SectionIndexTitles = (TableViewSectionedDataSource
) -> [String]? - public typealias SectionForSectionIndexTitle = (TableViewSectionedDataSource
, _ title: String, _ index: Int) -> Int + public typealias SectionIndexTitles = @Sendable @MainActor (TableViewSectionedDataSource
) -> [String]? + public typealias SectionForSectionIndexTitle = @Sendable @MainActor (TableViewSectionedDataSource
, _ title: String, _ index: Int) -> Int #endif #if os(iOS) public init( configureCell: @escaping ConfigureCell, - titleForHeaderInSection: @escaping TitleForHeaderInSection = { _, _ in nil }, + titleForHeaderInSection: @escaping TitleForHeaderInSection = { _, _ in nil }, titleForFooterInSection: @escaping TitleForFooterInSection = { _, _ in nil }, canEditRowAtIndexPath: @escaping CanEditRowAtIndexPath = { _, _ in false }, canMoveRowAtIndexPath: @escaping CanMoveRowAtIndexPath = { _, _ in false }, @@ -173,47 +173,49 @@ open class TableViewSectionedDataSource // UITableViewDataSource - + @MainActor open func numberOfSections(in tableView: UITableView) -> Int { _sectionModels.count } - + @MainActor open func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { guard _sectionModels.count > section else { return 0 } return _sectionModels[section].items.count } - + @MainActor open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { precondition(indexPath.item < _sectionModels[indexPath.section].items.count) return configureCell(self, tableView, indexPath, self[indexPath]) } - + @MainActor open func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { titleForHeaderInSection(self, section) } - + @MainActor open func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? { titleForFooterInSection(self, section) } - + @MainActor open func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { canEditRowAtIndexPath(self, indexPath) } - + @MainActor open func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { canMoveRowAtIndexPath(self, indexPath) } - + @MainActor open func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { self._sectionModels.moveFromSourceIndexPath(sourceIndexPath, destinationIndexPath: destinationIndexPath) } #if os(iOS) + @MainActor open func sectionIndexTitles(for tableView: UITableView) -> [String]? { sectionIndexTitles(self) } + @MainActor open func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int { sectionForSectionIndexTitle(self, title, index) } diff --git a/RxExample/RxExample-iOSTests/Mocks/MockGitHubAPI.swift b/RxExample/RxExample-iOSTests/Mocks/MockGitHubAPI.swift index 0bbafc354..69ddf7aa4 100644 --- a/RxExample/RxExample-iOSTests/Mocks/MockGitHubAPI.swift +++ b/RxExample/RxExample-iOSTests/Mocks/MockGitHubAPI.swift @@ -9,12 +9,12 @@ import RxSwift class MockGitHubAPI : GitHubAPI { - let _usernameAvailable: (String) -> Observable - let _signup: ((String, String)) -> Observable + let _usernameAvailable: @Sendable (String) -> Observable + let _signup: @Sendable ((String, String)) -> Observable init( - usernameAvailable: @escaping (String) -> Observable = notImplemented(), - signup: @escaping ((String, String)) -> Observable = notImplemented() + usernameAvailable: @escaping @Sendable (String) -> Observable = notImplemented(), + signup: @escaping @Sendable ((String, String)) -> Observable = notImplemented() ) { _usernameAvailable = usernameAvailable _signup = signup diff --git a/RxExample/RxExample-iOSTests/Mocks/MockWireframe.swift b/RxExample/RxExample-iOSTests/Mocks/MockWireframe.swift index 6696a3c21..089e622df 100644 --- a/RxExample/RxExample-iOSTests/Mocks/MockWireframe.swift +++ b/RxExample/RxExample-iOSTests/Mocks/MockWireframe.swift @@ -10,11 +10,11 @@ import RxSwift import Foundation class MockWireframe: Wireframe { - let _openURL: (URL) -> Void - let _promptFor: (String, Any, [Any]) -> Observable + let _openURL: @Sendable (URL) -> Void + let _promptFor: @Sendable (String, Any, [Any]) -> Observable - init(openURL: @escaping (URL) -> Void = notImplementedSync(), - promptFor: @escaping (String, Any, [Any]) -> Observable = notImplemented()) { + init(openURL: @escaping @Sendable (URL) -> Void = notImplementedSync(), + promptFor: @escaping @Sendable (String, Any, [Any]) -> Observable = notImplemented()) { _openURL = openURL _promptFor = promptFor } diff --git a/RxExample/RxExample-iOSTests/Mocks/NotImplementedStubs.swift b/RxExample/RxExample-iOSTests/Mocks/NotImplementedStubs.swift index 7d81e6cc0..557819200 100644 --- a/RxExample/RxExample-iOSTests/Mocks/NotImplementedStubs.swift +++ b/RxExample/RxExample-iOSTests/Mocks/NotImplementedStubs.swift @@ -22,25 +22,25 @@ func genericFatal(_ message: String) -> T { // MARK: Not implemented stubs -func notImplemented() -> (T1) -> Observable { +func notImplemented() -> @Sendable (T1) -> Observable { return { _ -> Observable in return genericFatal("Not implemented") } } -func notImplemented() -> (T1, T2) -> Observable { +func notImplemented() -> @Sendable (T1, T2) -> Observable { return { _, _ -> Observable in return genericFatal("Not implemented") } } -func notImplemented() -> (T1, T2, T3) -> Observable { +func notImplemented() -> @Sendable (T1, T2, T3) -> Observable { return { _, _, _ -> Observable in return genericFatal("Not implemented") } } -func notImplementedSync() -> (T1) -> Void { +func notImplementedSync() -> @Sendable (T1) -> Void { return { _ in genericFatal("Not implemented") } diff --git a/RxExample/RxExample-iOSTests/TestScheduler+MarbleTests.swift b/RxExample/RxExample-iOSTests/TestScheduler+MarbleTests.swift index 41a1539a3..3ee35c9cc 100644 --- a/RxExample/RxExample-iOSTests/TestScheduler+MarbleTests.swift +++ b/RxExample/RxExample-iOSTests/TestScheduler+MarbleTests.swift @@ -137,10 +137,10 @@ extension TestScheduler { - returns: Observable sequence specified by timeline and values. */ func createObservable(_ events: [[Recorded>]]) -> Observable { - var attemptCount = 0 + nonisolated(unsafe) var attemptCount = 0 print("created for \(events)") - return Observable.create { observer in + return Observable.create { @Sendable observer in if attemptCount >= events.count { fatalError("This is attempt # \(attemptCount + 1), but timeline only allows \(events.count).\n\(events)") } @@ -172,7 +172,7 @@ extension TestScheduler { - returns: Implementation of method that accepts arguments with parameter `Arg` and returns observable sequence with parameter `Ret`. */ - func mock(values: [String: Ret], errors: [String: Swift.Error] = [:], timelineSelector: @escaping (Arg) -> String) -> (Arg) -> Observable { + func mock(values: [String: Ret], errors: [String: Swift.Error] = [:], timelineSelector: @escaping @Sendable (Arg) -> String) -> (Arg) -> Observable { return { (parameters: Arg) -> Observable in let timeline = timelineSelector(parameters) diff --git a/RxExample/RxExample/Examples/GitHubSearchRepositories/GitHubSearchRepositories.swift b/RxExample/RxExample/Examples/GitHubSearchRepositories/GitHubSearchRepositories.swift index 1f6b1339d..34ecc998f 100644 --- a/RxExample/RxExample/Examples/GitHubSearchRepositories/GitHubSearchRepositories.swift +++ b/RxExample/RxExample/Examples/GitHubSearchRepositories/GitHubSearchRepositories.swift @@ -31,7 +31,8 @@ struct GitHubSearchRepositoriesState { extension GitHubSearchRepositoriesState { static let initial = GitHubSearchRepositoriesState(searchText: "") - + + @Sendable static func reduce(state: GitHubSearchRepositoriesState, command: GitHubCommand) -> GitHubSearchRepositoriesState { switch command { case .changeSearch(let text): @@ -73,8 +74,8 @@ struct GithubQuery: Equatable { */ func githubSearchRepositories( searchText: Signal, - loadNextPageTrigger: @escaping (Driver) -> Signal<()>, - performSearch: @escaping (URL) -> Observable + loadNextPageTrigger: @escaping @Sendable (Driver) -> Signal<()>, + performSearch: @escaping @Sendable (URL) -> Observable ) -> Driver { @@ -98,14 +99,14 @@ func githubSearchRepositories( return performSearch(nextURL) .asSignal(onErrorJustReturn: .failure(GitHubServiceError.networkError)) - .map(GitHubCommand.gitHubResponseReceived) + .map({ GitHubCommand.gitHubResponseReceived($0) }) } ) // this is degenerated feedback loop that doesn't depend on output state let inputFeedbackLoop: (Driver) -> Signal = { state in let loadNextPage = loadNextPageTrigger(state).map { _ in GitHubCommand.loadMoreItems } - let searchText = searchText.map(GitHubCommand.changeSearch) + let searchText = searchText.map({ GitHubCommand.changeSearch(text: $0) }) return Signal.merge(loadNextPage, searchText) } diff --git a/RxExample/RxExample/Examples/ImagePicker/UIImagePickerController+RxCreate.swift b/RxExample/RxExample/Examples/ImagePicker/UIImagePickerController+RxCreate.swift index 3839bd0a9..1ee5151a2 100644 --- a/RxExample/RxExample/Examples/ImagePicker/UIImagePickerController+RxCreate.swift +++ b/RxExample/RxExample/Examples/ImagePicker/UIImagePickerController+RxCreate.swift @@ -25,37 +25,39 @@ func dismissViewController(_ viewController: UIViewController, animated: Bool) { } extension Reactive where Base: UIImagePickerController { - static func createWithParent(_ parent: UIViewController?, animated: Bool = true, configureImagePicker: @escaping (UIImagePickerController) throws -> Void = { x in }) -> Observable { + static func createWithParent(_ parent: UIViewController?, animated: Bool = true, configureImagePicker: @escaping @Sendable (UIImagePickerController) throws -> Void = { x in }) -> Observable { return Observable.create { [weak parent] observer in - let imagePicker = UIImagePickerController() - let dismissDisposable = imagePicker.rx - .didCancel - .subscribe(onNext: { [weak imagePicker] _ in - guard let imagePicker = imagePicker else { - return - } - dismissViewController(imagePicker, animated: animated) - }) - - do { - try configureImagePicker(imagePicker) - } - catch let error { - observer.on(.error(error)) - return Disposables.create() - } - - guard let parent = parent else { - observer.on(.completed) - return Disposables.create() - } - - parent.present(imagePicker, animated: animated, completion: nil) - observer.on(.next(imagePicker)) - - return Disposables.create(dismissDisposable, Disposables.create { + MainScheduler.assumeMainActor(execute: { + let imagePicker = UIImagePickerController() + let dismissDisposable = imagePicker.rx + .didCancel + .subscribe(onNext: { [weak imagePicker] _ in + guard let imagePicker = imagePicker else { + return + } + dismissViewController(imagePicker, animated: animated) + }) + + do { + try configureImagePicker(imagePicker) + } + catch let error { + observer.on(.error(error)) + return Disposables.create() + } + + guard let parent = parent else { + observer.on(.completed) + return Disposables.create() + } + + parent.present(imagePicker, animated: animated, completion: nil) + observer.on(.next(imagePicker)) + + return Disposables.create(dismissDisposable, Disposables.create { dismissViewController(imagePicker, animated: animated) }) + }) } } } diff --git a/RxExample/RxExample/Feedbacks.swift b/RxExample/RxExample/Feedbacks.swift index 8dba562a6..b38c2b344 100644 --- a/RxExample/RxExample/Feedbacks.swift +++ b/RxExample/RxExample/Feedbacks.swift @@ -6,8 +6,8 @@ // Copyright © 2017 Krunoslav Zaher. All rights reserved. // -import RxSwift -import RxCocoa +@preconcurrency import RxSwift +@preconcurrency import RxCocoa // Taken from RxFeedback repo @@ -26,9 +26,9 @@ import RxCocoa - returns: Feedback loop performing the effects. */ public func react( - query: @escaping (State) -> Query?, - areEqual: @escaping (Query, Query) -> Bool, - effects: @escaping (Query) -> Observable + query: @escaping @Sendable (State) -> Query?, + areEqual: @escaping @Sendable (Query, Query) -> Bool, + effects: @escaping @Sendable (Query) -> Observable ) -> (ObservableSchedulerContext) -> Observable { return { state in return state.map(query) @@ -65,8 +65,8 @@ public func react( - returns: Feedback loop performing the effects. */ public func react( - query: @escaping (State) -> Query?, - effects: @escaping (Query) -> Observable + query: @escaping @Sendable (State) -> Query?, + effects: @escaping @Sendable (Query) -> Observable ) -> (ObservableSchedulerContext) -> Observable { return react(query: query, areEqual: { $0 == $1 }, effects: effects) } @@ -86,9 +86,9 @@ public func react( - returns: Feedback loop performing the effects. */ public func react( - query: @escaping (State) -> Query?, - areEqual: @escaping (Query, Query) -> Bool, - effects: @escaping (Query) -> Signal + query: @escaping @Sendable (State) -> Query?, + areEqual: @escaping @Sendable (Query, Query) -> Bool, + effects: @escaping @Sendable (Query) -> Signal ) -> (Driver) -> Signal { return { state in let observableSchedulerContext = ObservableSchedulerContext( @@ -114,8 +114,8 @@ public func react( - returns: Feedback loop performing the effects. */ public func react( - query: @escaping (State) -> Query?, - effects: @escaping (Query) -> Signal + query: @escaping @Sendable (State) -> Query?, + effects: @escaping @Sendable (Query) -> Signal ) -> (Driver) -> Signal { return { state in let observableSchedulerContext = ObservableSchedulerContext( @@ -141,8 +141,8 @@ public func react( - returns: Feedback loop performing the effects. */ public func react( - query: @escaping (State) -> Query?, - effects: @escaping (Query) -> Observable + query: @escaping @Sendable (State) -> Query?, + effects: @escaping @Sendable (Query) -> Observable ) -> (ObservableSchedulerContext) -> Observable { return { state in return state.map(query) @@ -172,8 +172,8 @@ public func react( - returns: Feedback loop performing the effects. */ public func react( - query: @escaping (State) -> Query?, - effects: @escaping (Query) -> Signal + query: @escaping @Sendable (State) -> Query?, + effects: @escaping @Sendable (Query) -> Signal ) -> (Driver) -> Signal { return { state in let observableSchedulerContext = ObservableSchedulerContext( @@ -200,8 +200,8 @@ public func react( - returns: Feedback loop performing the effects. */ public func react( - query: @escaping (State) -> Set, - effects: @escaping (Query) -> Observable + query: @escaping @Sendable (State) -> Set, + effects: @escaping @Sendable (Query) -> Observable ) -> (ObservableSchedulerContext) -> Observable { return { state in let query = state.map(query) @@ -250,8 +250,8 @@ extension ObservableType { - returns: Feedback loop performing the effects. */ public func react( - query: @escaping (State) -> Set, - effects: @escaping (Query) -> Signal + query: @escaping @Sendable (State) -> Set, + effects: @escaping @Sendable (Query) -> Signal ) -> (Driver) -> Signal { return { (state: Driver) -> Signal in let observableSchedulerContext = ObservableSchedulerContext( @@ -314,7 +314,7 @@ public class Bindings: Disposable { /** Bi-directional binding of a system State to external state machine and events from it. */ -public func bind(_ bindings: @escaping (ObservableSchedulerContext) -> (Bindings)) -> (ObservableSchedulerContext) -> Observable { +public func bind(_ bindings: @escaping @Sendable (ObservableSchedulerContext) -> (Bindings)) -> @Sendable (ObservableSchedulerContext) -> Observable { return { (state: ObservableSchedulerContext) -> Observable in return Observable.using({ () -> Bindings in return bindings(state) @@ -329,7 +329,7 @@ public func bind(_ bindings: @escaping (ObservableSchedulerContext Bi-directional binding of a system State to external state machine and events from it. Strongify owner. */ -public func bind(_ owner: WeakOwner, _ bindings: @escaping (WeakOwner, ObservableSchedulerContext) -> (Bindings)) +public func bind(_ owner: WeakOwner, _ bindings: @escaping @Sendable (WeakOwner, ObservableSchedulerContext) -> (Bindings)) -> (ObservableSchedulerContext) -> Observable where WeakOwner: AnyObject { return bind(bindingsStrongify(owner, bindings)) } @@ -337,7 +337,7 @@ public func bind(_ owner: WeakOwner, _ bindings: @escap /** Bi-directional binding of a system State to external state machine and events from it. */ -public func bind(_ bindings: @escaping (Driver) -> (Bindings)) -> (Driver) -> Signal { +public func bind(_ bindings: @escaping @Sendable (Driver) -> (Bindings)) -> @Sendable (Driver) -> Signal { return { (state: Driver) -> Signal in return Observable.using({ () -> Bindings in return bindings(state) @@ -354,12 +354,13 @@ public func bind(_ bindings: @escaping (Driver) -> (Binding Bi-directional binding of a system State to external state machine and events from it. Strongify owner. */ -public func bind(_ owner: WeakOwner, _ bindings: @escaping (WeakOwner, Driver) -> (Bindings)) - -> (Driver) -> Signal where WeakOwner: AnyObject { +public func bind(_ owner: WeakOwner, _ bindings: @escaping @Sendable (WeakOwner, Driver) -> (Bindings)) + -> @Sendable (Driver) -> Signal where WeakOwner: AnyObject { return bind(bindingsStrongify(owner, bindings)) } -private func bindingsStrongify(_ owner: WeakOwner, _ bindings: @escaping (WeakOwner, O) -> (Bindings)) +@Sendable +private func bindingsStrongify(_ owner: WeakOwner, _ bindings: @escaping @Sendable (WeakOwner, O) -> (Bindings)) -> (O) -> (Bindings) where WeakOwner: AnyObject { return { [weak owner] state -> Bindings in guard let strongOwner = owner else { diff --git a/RxExample/RxExample/Observable+Extensions.swift b/RxExample/RxExample/Observable+Extensions.swift index 035702166..c0fe651e5 100644 --- a/RxExample/RxExample/Observable+Extensions.swift +++ b/RxExample/RxExample/Observable+Extensions.swift @@ -30,7 +30,7 @@ extension ObservableType where Element == Any { */ public static func system( initialState: State, - reduce: @escaping (State, Event) -> State, + reduce: @escaping @Sendable (State, Event) -> State, scheduler: ImmediateSchedulerType, scheduledFeedback: [Feedback] ) -> Observable { @@ -61,7 +61,7 @@ extension ObservableType where Element == Any { public static func system( initialState: State, - reduce: @escaping (State, Event) -> State, + reduce: @escaping @Sendable (State, Event) -> State, scheduler: ImmediateSchedulerType, scheduledFeedback: Feedback... ) -> Observable { @@ -86,7 +86,7 @@ extension SharedSequenceConvertibleType where Element == Any, SharingStrategy == */ public static func system( initialState: State, - reduce: @escaping (State, Event) -> State, + reduce: @escaping @Sendable (State, Event) -> State, feedback: [Feedback] ) -> Driver { let observableFeedbacks: [(ObservableSchedulerContext) -> Observable] = feedback.map { feedback in @@ -107,7 +107,7 @@ extension SharedSequenceConvertibleType where Element == Any, SharingStrategy == public static func system( initialState: State, - reduce: @escaping (State, Event) -> State, + reduce: @escaping @Sendable (State, Event) -> State, feedback: Feedback... ) -> Driver { system(initialState: initialState, reduce: reduce, feedback: feedback) diff --git a/RxExample/RxExample/Services/ActivityIndicator.swift b/RxExample/RxExample/Services/ActivityIndicator.swift index 54e47ff95..101943f46 100644 --- a/RxExample/RxExample/Services/ActivityIndicator.swift +++ b/RxExample/RxExample/Services/ActivityIndicator.swift @@ -13,7 +13,7 @@ private struct ActivityToken : ObservableConvertibleType, Disposable { private let _source: Observable private let _dispose: Cancelable - init(source: Observable, disposeAction: @escaping () -> Void) { + init(source: Observable, disposeAction: @escaping @Sendable () -> Void) { _source = source _dispose = Disposables.create(with: disposeAction) } @@ -33,7 +33,7 @@ Enables monitoring of sequence computation. If there is at least one sequence computation in progress, `true` will be sent. When all activities complete `false` will be sent. */ -public class ActivityIndicator : SharedSequenceConvertibleType { +public class ActivityIndicator : SharedSequenceConvertibleType, @unchecked Sendable { public typealias Element = Bool public typealias SharingStrategy = DriverSharingStrategy @@ -55,13 +55,15 @@ public class ActivityIndicator : SharedSequenceConvertibleType { return t.asObservable() } } - + + @Sendable private func increment() { _lock.lock() _relay.accept(_relay.value + 1) _lock.unlock() } - + + @Sendable private func decrement() { _lock.lock() _relay.accept(_relay.value - 1) diff --git a/RxExample/RxExample/Services/GeolocationService.swift b/RxExample/RxExample/Services/GeolocationService.swift index 18ebea627..cf0c5b607 100644 --- a/RxExample/RxExample/Services/GeolocationService.swift +++ b/RxExample/RxExample/Services/GeolocationService.swift @@ -13,8 +13,8 @@ import RxCocoa class GeolocationService { static let instance = GeolocationService() - private (set) var authorized: Driver - private (set) var location: Driver + private(set) var authorized: Driver + private(set) var location: Driver private let locationManager = CLLocationManager() diff --git a/RxExample/RxExample/Services/UIImageView+DownloadableImage.swift b/RxExample/RxExample/Services/UIImageView+DownloadableImage.swift index c800eba08..a92a56a1b 100644 --- a/RxExample/RxExample/Services/UIImageView+DownloadableImage.swift +++ b/RxExample/RxExample/Services/UIImageView+DownloadableImage.swift @@ -20,19 +20,21 @@ extension Reactive where Base: UIImageView { func downloadableImageAnimated(_ transitionType: String?) -> Binder { return Binder(base) { imageView, image in - for subview in imageView.subviews { - subview.removeFromSuperview() - } - switch image { - case .content(let image): - (imageView as UIImageView).rx.image.on(.next(image)) - case .offlinePlaceholder: - let label = UILabel(frame: imageView.bounds) - label.textAlignment = .center - label.font = UIFont.systemFont(ofSize: 35) - label.text = "⚠️" - imageView.addSubview(label) - } + MainScheduler.assumeMainActor(execute: { + for subview in imageView.subviews { + subview.removeFromSuperview() + } + switch image { + case .content(let image): + (imageView as UIImageView).rx.image.on(.next(image)) + case .offlinePlaceholder: + let label = UILabel(frame: imageView.bounds) + label.textAlignment = .center + label.font = UIFont.systemFont(ofSize: 35) + label.text = "⚠️" + imageView.addSubview(label) + } + }) } } } diff --git a/RxSwift/AnyObserver.swift b/RxSwift/AnyObserver.swift index e92cc816e..b54ce8ca1 100644 --- a/RxSwift/AnyObserver.swift +++ b/RxSwift/AnyObserver.swift @@ -11,7 +11,7 @@ /// Forwards operations to an arbitrary underlying observer with the same `Element` type, hiding the specifics of the underlying observer type. public struct AnyObserver : ObserverType { /// Anonymous event handler type. - public typealias EventHandler = (Event) -> Void + public typealias EventHandler = @Sendable (Event) -> Void private let observer: EventHandler @@ -61,7 +61,7 @@ extension ObserverType { /// Each event sent to result observer is transformed and sent to `self`. /// /// - returns: observer that transforms events. - public func mapObserver(_ transform: @escaping (Result) throws -> Element) -> AnyObserver { + public func mapObserver(_ transform: @escaping @Sendable (Result) throws -> Element) -> AnyObserver { AnyObserver { e in self.on(e.map(transform)) } diff --git a/RxSwift/Binder.swift b/RxSwift/Binder.swift index 1aae09592..c2c9349c4 100644 --- a/RxSwift/Binder.swift +++ b/RxSwift/Binder.swift @@ -15,18 +15,18 @@ By default it binds elements on main scheduler. */ -public struct Binder: ObserverType { +public struct Binder: ObserverType, @unchecked Sendable { public typealias Element = Value - private let binding: (Event) -> Void + private let binding: @Sendable (Event) -> Void /// Initializes `Binder` /// /// - parameter target: Target object. /// - parameter scheduler: Scheduler used to bind the events. /// - parameter binding: Binding logic. - public init(_ target: Target, scheduler: ImmediateSchedulerType = MainScheduler(), binding: @escaping (Target, Value) -> Void) { - weak var weakTarget = target + public init(_ target: Target, scheduler: ImmediateSchedulerType = MainScheduler(), binding: @escaping @Sendable (Target, Value) -> Void) { + nonisolated(unsafe) weak var weakTarget = target self.binding = { event in switch event { @@ -46,6 +46,7 @@ public struct Binder: ObserverType { } /// Binds next element to owner view as described in `binding`. + @Sendable public func on(_ event: Event) { self.binding(event) } diff --git a/RxSwift/Concurrency/AsyncLock.swift b/RxSwift/Concurrency/AsyncLock.swift index 6bee59c11..3f0b2d11b 100644 --- a/RxSwift/Concurrency/AsyncLock.swift +++ b/RxSwift/Concurrency/AsyncLock.swift @@ -19,10 +19,11 @@ That means that enqueued work could possibly be executed later on a different th final class AsyncLock : Disposable , Lock - , SynchronizedDisposeType { - typealias Action = () -> Void + , SynchronizedDisposeType + , @unchecked Sendable { + typealias Action = @Sendable () -> Void - private var _lock = SpinLock() + nonisolated(unsafe) private var _lock = SpinLock() private var queue: Queue = Queue(capacity: 0) diff --git a/RxSwift/Disposable.swift b/RxSwift/Disposable.swift index b79c77a75..9e26911df 100644 --- a/RxSwift/Disposable.swift +++ b/RxSwift/Disposable.swift @@ -7,7 +7,7 @@ // /// Represents a disposable resource. -public protocol Disposable { +public protocol Disposable: Sendable { /// Dispose resource. func dispose() } diff --git a/RxSwift/Disposables/AnonymousDisposable.swift b/RxSwift/Disposables/AnonymousDisposable.swift index 591aafa5e..4f008e01b 100644 --- a/RxSwift/Disposables/AnonymousDisposable.swift +++ b/RxSwift/Disposables/AnonymousDisposable.swift @@ -9,8 +9,8 @@ /// Represents an Action-based disposable. /// /// When dispose method is called, disposal action will be dereferenced. -private final class AnonymousDisposable : DisposeBase, Cancelable { - public typealias DisposeAction = () -> Void +private final class AnonymousDisposable : DisposeBase, Cancelable, @unchecked Sendable { + public typealias DisposeAction = @Sendable () -> Void private let disposed = AtomicInt(0) private var disposeAction: DisposeAction? @@ -52,7 +52,7 @@ extension Disposables { /// Constructs a new disposable with the given action used for disposal. /// /// - parameter dispose: Disposal action which will be run upon calling `dispose`. - public static func create(with dispose: @escaping () -> Void) -> Cancelable { + public static func create(with dispose: @escaping @Sendable () -> Void) -> Cancelable { AnonymousDisposable(disposeAction: dispose) } diff --git a/RxSwift/Disposables/BinaryDisposable.swift b/RxSwift/Disposables/BinaryDisposable.swift index a3d493708..1e727e569 100644 --- a/RxSwift/Disposables/BinaryDisposable.swift +++ b/RxSwift/Disposables/BinaryDisposable.swift @@ -7,7 +7,7 @@ // /// Represents two disposable resources that are disposed together. -private final class BinaryDisposable : DisposeBase, Cancelable { +private final class BinaryDisposable : DisposeBase, Cancelable, @unchecked Sendable { private let disposed = AtomicInt(0) diff --git a/RxSwift/Disposables/CompositeDisposable.swift b/RxSwift/Disposables/CompositeDisposable.swift index bb4efe6ac..66ca6005c 100644 --- a/RxSwift/Disposables/CompositeDisposable.swift +++ b/RxSwift/Disposables/CompositeDisposable.swift @@ -7,7 +7,7 @@ // /// Represents a group of disposable resources that are disposed together. -public final class CompositeDisposable : DisposeBase, Cancelable { +public final class CompositeDisposable : DisposeBase, Cancelable, @unchecked Sendable { /// Key used to remove disposable from composite disposable public struct DisposeKey { fileprivate let key: BagKey diff --git a/RxSwift/Disposables/DisposeBag.swift b/RxSwift/Disposables/DisposeBag.swift index 1a673bcce..36eb8e8f8 100644 --- a/RxSwift/Disposables/DisposeBag.swift +++ b/RxSwift/Disposables/DisposeBag.swift @@ -27,7 +27,7 @@ or create a new one in its place. In case explicit disposal is necessary, there is also `CompositeDisposable`. */ -public final class DisposeBag: DisposeBase { +public final class DisposeBag: DisposeBase, @unchecked Sendable { private var lock = SpinLock() diff --git a/RxSwift/Disposables/DisposeBase.swift b/RxSwift/Disposables/DisposeBase.swift index 0d4b2fb7f..7102574b7 100644 --- a/RxSwift/Disposables/DisposeBase.swift +++ b/RxSwift/Disposables/DisposeBase.swift @@ -7,7 +7,7 @@ // /// Base class for all disposables. -public class DisposeBase { +public class DisposeBase: @unchecked Sendable { init() { #if TRACE_RESOURCES _ = Resources.incrementTotal() diff --git a/RxSwift/Disposables/RefCountDisposable.swift b/RxSwift/Disposables/RefCountDisposable.swift index a59f7789d..50edc9419 100644 --- a/RxSwift/Disposables/RefCountDisposable.swift +++ b/RxSwift/Disposables/RefCountDisposable.swift @@ -7,7 +7,7 @@ // /// Represents a disposable resource that only disposes its underlying disposable resource when all dependent disposable objects have been disposed. -public final class RefCountDisposable : DisposeBase, Cancelable { +public final class RefCountDisposable : DisposeBase, Cancelable, @unchecked Sendable { private var lock = SpinLock() private var disposable = nil as Disposable? private var primaryDisposed = false @@ -93,7 +93,7 @@ public final class RefCountDisposable : DisposeBase, Cancelable { } } -internal final class RefCountInnerDisposable: DisposeBase, Disposable +internal final class RefCountInnerDisposable: DisposeBase, Disposable, @unchecked Sendable { private let parent: RefCountDisposable private let isDisposed = AtomicInt(0) diff --git a/RxSwift/Disposables/ScheduledDisposable.swift b/RxSwift/Disposables/ScheduledDisposable.swift index 2cf997bca..5bb072d7b 100644 --- a/RxSwift/Disposables/ScheduledDisposable.swift +++ b/RxSwift/Disposables/ScheduledDisposable.swift @@ -6,13 +6,13 @@ // Copyright © 2015 Krunoslav Zaher. All rights reserved. // -private let disposeScheduledDisposable: (ScheduledDisposable) -> Disposable = { sd in +private let disposeScheduledDisposable: @Sendable (ScheduledDisposable) -> Disposable = { sd in sd.disposeInner() return Disposables.create() } /// Represents a disposable resource whose disposal invocation will be scheduled on the specified scheduler. -public final class ScheduledDisposable : Cancelable { +public final class ScheduledDisposable : Cancelable, @unchecked Sendable { public let scheduler: ImmediateSchedulerType private let disposed = AtomicInt(0) diff --git a/RxSwift/Disposables/SerialDisposable.swift b/RxSwift/Disposables/SerialDisposable.swift index ef4690d32..77d29d859 100644 --- a/RxSwift/Disposables/SerialDisposable.swift +++ b/RxSwift/Disposables/SerialDisposable.swift @@ -7,7 +7,7 @@ // /// Represents a disposable resource whose underlying disposable resource can be replaced by another disposable resource, causing automatic disposal of the previous underlying disposable resource. -public final class SerialDisposable : DisposeBase, Cancelable { +public final class SerialDisposable : DisposeBase, Cancelable, @unchecked Sendable { private var lock = SpinLock() // state diff --git a/RxSwift/Disposables/SingleAssignmentDisposable.swift b/RxSwift/Disposables/SingleAssignmentDisposable.swift index 4a5fb8662..8e49e9b56 100644 --- a/RxSwift/Disposables/SingleAssignmentDisposable.swift +++ b/RxSwift/Disposables/SingleAssignmentDisposable.swift @@ -11,7 +11,7 @@ Represents a disposable resource which only allows a single assignment of its un If an underlying disposable resource has already been set, future attempts to set the underlying disposable resource will throw an exception. */ -public final class SingleAssignmentDisposable : DisposeBase, Cancelable { +public final class SingleAssignmentDisposable : DisposeBase, Cancelable, @unchecked Sendable { private struct DisposeState: OptionSet { let rawValue: Int32 diff --git a/RxSwift/Disposables/SubscriptionDisposable.swift b/RxSwift/Disposables/SubscriptionDisposable.swift index a406c84cb..f85b6febd 100644 --- a/RxSwift/Disposables/SubscriptionDisposable.swift +++ b/RxSwift/Disposables/SubscriptionDisposable.swift @@ -6,7 +6,7 @@ // Copyright © 2015 Krunoslav Zaher. All rights reserved. // -struct SubscriptionDisposable : Disposable { +struct SubscriptionDisposable : Disposable, @unchecked Sendable { private let key: T.DisposeKey private weak var owner: T? diff --git a/RxSwift/GroupedObservable.swift b/RxSwift/GroupedObservable.swift index c4ac44628..ae11142c8 100644 --- a/RxSwift/GroupedObservable.swift +++ b/RxSwift/GroupedObservable.swift @@ -36,7 +36,7 @@ /// next(Banana) /// next(Blueberry) /// ``` -public struct GroupedObservable : ObservableType { +public struct GroupedObservable : ObservableType, @unchecked Sendable { /// The key associated with this grouped observable sequence. /// All elements emitted by this observable share this common key. public let key: Key diff --git a/RxSwift/ImmediateSchedulerType.swift b/RxSwift/ImmediateSchedulerType.swift index 954fbf04b..f6acb0368 100644 --- a/RxSwift/ImmediateSchedulerType.swift +++ b/RxSwift/ImmediateSchedulerType.swift @@ -7,7 +7,7 @@ // /// Represents an object that immediately schedules units of work. -public protocol ImmediateSchedulerType { +public protocol ImmediateSchedulerType: Sendable { /** Schedules an action to be executed immediately. @@ -15,7 +15,7 @@ public protocol ImmediateSchedulerType { - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ - func schedule(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable + func schedule(_ state: StateType, action: @escaping @Sendable (StateType) -> Disposable) -> Disposable } extension ImmediateSchedulerType { @@ -26,7 +26,7 @@ extension ImmediateSchedulerType { - parameter action: Action to execute recursively. The last parameter passed to the action is used to trigger recursive scheduling of the action, passing in recursive invocation state. - returns: The disposable object used to cancel the scheduled action (best effort). */ - public func scheduleRecursive(_ state: State, action: @escaping (_ state: State, _ recurse: (State) -> Void) -> Void) -> Disposable { + public func scheduleRecursive(_ state: State, action: @escaping @Sendable (_ state: State, _ recurse: (State) -> Void) -> Void) -> Disposable { let recursiveScheduler = RecursiveImmediateScheduler(action: action, scheduler: self) recursiveScheduler.schedule(state) diff --git a/RxSwift/Observable+Concurrency.swift b/RxSwift/Observable+Concurrency.swift index af3ec72bc..f086c3a81 100644 --- a/RxSwift/Observable+Concurrency.swift +++ b/RxSwift/Observable+Concurrency.swift @@ -27,7 +27,7 @@ public extension ObservableConvertibleType { /// ``` var values: AsyncThrowingStream { AsyncThrowingStream { continuation in - var isFinished = false + nonisolated(unsafe) var isFinished = false let disposable = asObservable().subscribe( onNext: { value in continuation.yield(value) }, onError: { error in diff --git a/RxSwift/Observable.swift b/RxSwift/Observable.swift index 7a9221f0b..4122ff69b 100644 --- a/RxSwift/Observable.swift +++ b/RxSwift/Observable.swift @@ -12,7 +12,7 @@ public typealias RxObservable = RxSwift.Observable -public class Observable : ObservableType { +public class Observable : ObservableType, @unchecked Sendable { init() { #if TRACE_RESOURCES _ = Resources.incrementTotal() diff --git a/RxSwift/ObservableConvertibleType.swift b/RxSwift/ObservableConvertibleType.swift index 0f56a8165..b3a2df529 100644 --- a/RxSwift/ObservableConvertibleType.swift +++ b/RxSwift/ObservableConvertibleType.swift @@ -7,7 +7,7 @@ // /// Type that can be converted to observable sequence (`Observable`). -public protocol ObservableConvertibleType { +public protocol ObservableConvertibleType: Sendable { /// Type of elements in sequence. associatedtype Element diff --git a/RxSwift/ObservableType+Extensions.swift b/RxSwift/ObservableType+Extensions.swift index ac850c3ba..71bae2c13 100644 --- a/RxSwift/ObservableType+Extensions.swift +++ b/RxSwift/ObservableType+Extensions.swift @@ -17,7 +17,7 @@ extension ObservableType { - parameter on: Action to invoke for each event in the observable sequence. - returns: Subscription object used to unsubscribe from the observable sequence. */ - public func subscribe(_ on: @escaping (Event) -> Void) -> Disposable { + public func subscribe(_ on: @escaping @Sendable (Event) -> Void) -> Disposable { let observer = AnonymousObserver { e in on(e) } @@ -41,10 +41,10 @@ extension ObservableType { */ public func subscribe( with object: Object, - onNext: ((Object, Element) -> Void)? = nil, - onError: ((Object, Swift.Error) -> Void)? = nil, - onCompleted: ((Object) -> Void)? = nil, - onDisposed: ((Object) -> Void)? = nil + onNext: (@Sendable (Object, Element) -> Void)? = nil, + onError: (@Sendable (Object, Swift.Error) -> Void)? = nil, + onCompleted: (@Sendable (Object) -> Void)? = nil, + onDisposed: (@Sendable (Object) -> Void)? = nil ) -> Disposable { subscribe( onNext: { [weak object] in @@ -77,10 +77,10 @@ extension ObservableType { - returns: Subscription object used to unsubscribe from the observable sequence. */ public func subscribe( - onNext: ((Element) -> Void)? = nil, - onError: ((Swift.Error) -> Void)? = nil, - onCompleted: (() -> Void)? = nil, - onDisposed: (() -> Void)? = nil + onNext: (@Sendable (Element) -> Void)? = nil, + onError: (@Sendable (Swift.Error) -> Void)? = nil, + onCompleted: (@Sendable () -> Void)? = nil, + onDisposed: (@Sendable () -> Void)? = nil ) -> Disposable { let disposable: Disposable @@ -130,8 +130,8 @@ extension ObservableType { import Foundation extension Hooks { - public typealias DefaultErrorHandler = (_ subscriptionCallStack: [String], _ error: Error) -> Void - public typealias CustomCaptureSubscriptionCallstack = () -> [String] + public typealias DefaultErrorHandler = @Sendable (_ subscriptionCallStack: [String], _ error: Error) -> Void + public typealias CustomCaptureSubscriptionCallstack = @Sendable () -> [String] private static let lock = RecursiveLock() private static var _defaultErrorHandler: DefaultErrorHandler = { subscriptionCallStack, error in diff --git a/RxSwift/Observables/AddRef.swift b/RxSwift/Observables/AddRef.swift index a96dfcb03..a1d076579 100644 --- a/RxSwift/Observables/AddRef.swift +++ b/RxSwift/Observables/AddRef.swift @@ -6,7 +6,7 @@ // Copyright © 2015 Krunoslav Zaher. All rights reserved. // -final class AddRefSink : Sink, ObserverType { +final class AddRefSink : Sink, ObserverType, @unchecked Sendable { typealias Element = Observer.Element override init(observer: Observer, cancel: Cancelable) { @@ -24,7 +24,7 @@ final class AddRefSink : Sink, ObserverType { } } -final class AddRef : Producer { +final class AddRef : Producer, @unchecked Sendable { private let source: Observable private let refCount: RefCountDisposable diff --git a/RxSwift/Observables/Amb.swift b/RxSwift/Observables/Amb.swift index efc17a8d9..78209e9be 100644 --- a/RxSwift/Observables/Amb.swift +++ b/RxSwift/Observables/Amb.swift @@ -49,11 +49,11 @@ final private class AmbObserver: ObserverType { typealias Element = Observer.Element typealias Parent = AmbSink typealias This = AmbObserver - typealias Sink = (This, Event) -> Void + typealias Sink = @Sendable (This, Event) -> Void private let parent: Parent - fileprivate var sink: Sink - fileprivate var cancel: Disposable + nonisolated(unsafe) fileprivate var sink: Sink + nonisolated(unsafe) fileprivate var cancel: Disposable init(parent: Parent, cancel: Disposable, sink: @escaping Sink) { #if TRACE_RESOURCES @@ -79,7 +79,7 @@ final private class AmbObserver: ObserverType { } } -final private class AmbSink: Sink { +final private class AmbSink: Sink, @unchecked Sendable { typealias Element = Observer.Element typealias Parent = Amb typealias AmbObserverType = AmbObserver @@ -100,7 +100,7 @@ final private class AmbSink: Sink { let subscription2 = SingleAssignmentDisposable() let disposeAll = Disposables.create(subscription1, subscription2) - let forwardEvent = { (o: AmbObserverType, event: Event) -> Void in + let forwardEvent = { @Sendable (o: AmbObserverType, event: Event) -> Void in self.forwardOn(event) if event.isStopEvent { self.dispose() @@ -140,7 +140,7 @@ final private class AmbSink: Sink { } } -final private class Amb: Producer { +final private class Amb: Producer, @unchecked Sendable { fileprivate let left: Observable fileprivate let right: Observable diff --git a/RxSwift/Observables/AsMaybe.swift b/RxSwift/Observables/AsMaybe.swift index 6fa625784..4dcde701f 100644 --- a/RxSwift/Observables/AsMaybe.swift +++ b/RxSwift/Observables/AsMaybe.swift @@ -6,7 +6,7 @@ // Copyright © 2017 Krunoslav Zaher. All rights reserved. // -private final class AsMaybeSink : Sink, ObserverType { +private final class AsMaybeSink : Sink, ObserverType, @unchecked Sendable { typealias Element = Observer.Element private var element: Event? @@ -33,7 +33,7 @@ private final class AsMaybeSink : Sink, Observ } } -final class AsMaybe: Producer { +final class AsMaybe: Producer, @unchecked Sendable { private let source: Observable init(source: Observable) { diff --git a/RxSwift/Observables/AsSingle.swift b/RxSwift/Observables/AsSingle.swift index b39932f12..c22a6a694 100644 --- a/RxSwift/Observables/AsSingle.swift +++ b/RxSwift/Observables/AsSingle.swift @@ -6,7 +6,7 @@ // Copyright © 2017 Krunoslav Zaher. All rights reserved. // -private final class AsSingleSink : Sink, ObserverType { +private final class AsSingleSink : Sink, ObserverType, @unchecked Sendable { typealias Element = Observer.Element private var element: Event? @@ -36,7 +36,7 @@ private final class AsSingleSink : Sink, Obser } } -final class AsSingle: Producer { +final class AsSingle: Producer, @unchecked Sendable { private let source: Observable init(source: Observable) { diff --git a/RxSwift/Observables/Buffer.swift b/RxSwift/Observables/Buffer.swift index 61424e8bf..8d1f4d3f9 100644 --- a/RxSwift/Observables/Buffer.swift +++ b/RxSwift/Observables/Buffer.swift @@ -28,7 +28,7 @@ extension ObservableType { } } -final private class BufferTimeCount: Producer<[Element]> { +final private class BufferTimeCount: Producer<[Element]>, @unchecked Sendable { fileprivate let timeSpan: RxTimeInterval fileprivate let count: Int @@ -53,7 +53,8 @@ final private class BufferTimeCountSink : Sink , LockOwnerType , ObserverType - , SynchronizedOnType where Observer.Element == [Element] { + , SynchronizedOnType + , @unchecked Sendable where Observer.Element == [Element] { typealias Parent = BufferTimeCount private let parent: Parent diff --git a/RxSwift/Observables/Catch.swift b/RxSwift/Observables/Catch.swift index 2efd66062..b1046c81d 100644 --- a/RxSwift/Observables/Catch.swift +++ b/RxSwift/Observables/Catch.swift @@ -16,7 +16,7 @@ extension ObservableType { - parameter handler: Error handler function, producing another observable sequence. - returns: An observable sequence containing the source sequence's elements, followed by the elements produced by the handler's resulting observable sequence in case an error occurred. */ - public func `catch`(_ handler: @escaping (Swift.Error) throws -> Observable) + public func `catch`(_ handler: @escaping @Sendable (Swift.Error) throws -> Observable) -> Observable { Catch(source: self.asObservable(), handler: handler) } @@ -30,7 +30,7 @@ extension ObservableType { - returns: An observable sequence containing the source sequence's elements, followed by the elements produced by the handler's resulting observable sequence in case an error occurred. */ @available(*, deprecated, renamed: "catch(_:)") - public func catchError(_ handler: @escaping (Swift.Error) throws -> Observable) + public func catchError(_ handler: @escaping @Sendable (Swift.Error) throws -> Observable) -> Observable { `catch`(handler) } @@ -145,7 +145,7 @@ final private class CatchSinkProxy: ObserverType { } } -final private class CatchSink: Sink, ObserverType { +final private class CatchSink: Sink, ObserverType, @unchecked Sendable { typealias Element = Observer.Element typealias Parent = Catch @@ -188,8 +188,8 @@ final private class CatchSink: Sink, ObserverT } } -final private class Catch: Producer { - typealias Handler = (Swift.Error) throws -> Observable +final private class Catch: Producer, @unchecked Sendable { + typealias Handler = @Sendable (Swift.Error) throws -> Observable fileprivate let source: Observable fileprivate let handler: Handler @@ -210,7 +210,8 @@ final private class Catch: Producer { final private class CatchSequenceSink : TailRecursiveSink - , ObserverType where Sequence.Element: ObservableConvertibleType, Sequence.Element.Element == Observer.Element { + , ObserverType + , @unchecked Sendable where Sequence.Element: ObservableConvertibleType, Sequence.Element.Element == Observer.Element { typealias Element = Observer.Element typealias Parent = CatchSequence @@ -258,7 +259,7 @@ final private class CatchSequenceSink: Producer where Sequence.Element: ObservableConvertibleType { +final private class CatchSequence: Producer, @unchecked Sendable where Sequence.Element: ObservableConvertibleType { typealias Element = Sequence.Element.Element let sources: Sequence diff --git a/RxSwift/Observables/CombineLatest+Collection.swift b/RxSwift/Observables/CombineLatest+Collection.swift index b56d050bb..b0df24a6a 100644 --- a/RxSwift/Observables/CombineLatest+Collection.swift +++ b/RxSwift/Observables/CombineLatest+Collection.swift @@ -15,7 +15,7 @@ extension ObservableType { - parameter resultSelector: Function to invoke whenever any of the sources produces an element. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ - public static func combineLatest(_ collection: Collection, resultSelector: @escaping ([Collection.Element.Element]) throws -> Element) -> Observable + public static func combineLatest(_ collection: Collection, resultSelector: @escaping @Sendable ([Collection.Element.Element]) throws -> Element) -> Observable where Collection.Element: ObservableType { CombineLatestCollectionType(sources: collection, resultSelector: resultSelector) } @@ -34,7 +34,7 @@ extension ObservableType { } final class CombineLatestCollectionTypeSink - : Sink where Collection.Element: ObservableConvertibleType { + : Sink, @unchecked Sendable where Collection.Element: ObservableConvertibleType { typealias Result = Observer.Element typealias Parent = CombineLatestCollectionType typealias SourceElement = Collection.Element.Element @@ -144,8 +144,8 @@ final class CombineLatestCollectionTypeSink: Producer where Collection.Element: ObservableConvertibleType { - typealias ResultSelector = ([Collection.Element.Element]) throws -> Result +final class CombineLatestCollectionType: Producer, @unchecked Sendable where Collection.Element: ObservableConvertibleType { + typealias ResultSelector = @Sendable ([Collection.Element.Element]) throws -> Result let sources: Collection let resultSelector: ResultSelector diff --git a/RxSwift/Observables/CombineLatest+arity.swift b/RxSwift/Observables/CombineLatest+arity.swift index e69bbab4a..983827dab 100644 --- a/RxSwift/Observables/CombineLatest+arity.swift +++ b/RxSwift/Observables/CombineLatest+arity.swift @@ -21,7 +21,7 @@ extension ObservableType { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest - (_ source1: O1, _ source2: O2, resultSelector: @escaping (O1.Element, O2.Element) throws -> Element) + (_ source1: O1, _ source2: O2, resultSelector: @escaping @Sendable (O1.Element, O2.Element) throws -> Element) -> Observable { return CombineLatest2( source1: source1.asObservable(), source2: source2.asObservable(), @@ -48,7 +48,7 @@ extension ObservableType where Element == Any { } } -final class CombineLatestSink2_ : CombineLatestSink { +final class CombineLatestSink2_ : CombineLatestSink, @unchecked Sendable { typealias Result = Observer.Element typealias Parent = CombineLatest2 @@ -83,8 +83,8 @@ final class CombineLatestSink2_ : CombineLatestS } } -final class CombineLatest2 : Producer { - typealias ResultSelector = (E1, E2) throws -> Result +final class CombineLatest2 : Producer, @unchecked Sendable { + typealias ResultSelector = @Sendable (E1, E2) throws -> Result let source1: Observable let source2: Observable @@ -119,7 +119,7 @@ extension ObservableType { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest - (_ source1: O1, _ source2: O2, _ source3: O3, resultSelector: @escaping (O1.Element, O2.Element, O3.Element) throws -> Element) + (_ source1: O1, _ source2: O2, _ source3: O3, resultSelector: @escaping @Sendable (O1.Element, O2.Element, O3.Element) throws -> Element) -> Observable { return CombineLatest3( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), @@ -146,7 +146,7 @@ extension ObservableType where Element == Any { } } -final class CombineLatestSink3_ : CombineLatestSink { +final class CombineLatestSink3_ : CombineLatestSink, @unchecked Sendable { typealias Result = Observer.Element typealias Parent = CombineLatest3 @@ -186,8 +186,8 @@ final class CombineLatestSink3_ : CombineLat } } -final class CombineLatest3 : Producer { - typealias ResultSelector = (E1, E2, E3) throws -> Result +final class CombineLatest3 : Producer, @unchecked Sendable { + typealias ResultSelector = @Sendable (E1, E2, E3) throws -> Result let source1: Observable let source2: Observable @@ -224,7 +224,7 @@ extension ObservableType { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest - (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element) throws -> Element) + (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: @escaping @Sendable (O1.Element, O2.Element, O3.Element, O4.Element) throws -> Element) -> Observable { return CombineLatest4( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), @@ -251,7 +251,7 @@ extension ObservableType where Element == Any { } } -final class CombineLatestSink4_ : CombineLatestSink { +final class CombineLatestSink4_ : CombineLatestSink, @unchecked Sendable { typealias Result = Observer.Element typealias Parent = CombineLatest4 @@ -296,8 +296,8 @@ final class CombineLatestSink4_ : Combin } } -final class CombineLatest4 : Producer { - typealias ResultSelector = (E1, E2, E3, E4) throws -> Result +final class CombineLatest4 : Producer, @unchecked Sendable { + typealias ResultSelector = @Sendable (E1, E2, E3, E4) throws -> Result let source1: Observable let source2: Observable @@ -336,7 +336,7 @@ extension ObservableType { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest - (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element) throws -> Element) + (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: @escaping @Sendable (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element) throws -> Element) -> Observable { return CombineLatest5( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), @@ -363,7 +363,7 @@ extension ObservableType where Element == Any { } } -final class CombineLatestSink5_ : CombineLatestSink { +final class CombineLatestSink5_ : CombineLatestSink, @unchecked Sendable { typealias Result = Observer.Element typealias Parent = CombineLatest5 @@ -413,8 +413,8 @@ final class CombineLatestSink5_ : Co } } -final class CombineLatest5 : Producer { - typealias ResultSelector = (E1, E2, E3, E4, E5) throws -> Result +final class CombineLatest5 : Producer, @unchecked Sendable { + typealias ResultSelector = @Sendable (E1, E2, E3, E4, E5) throws -> Result let source1: Observable let source2: Observable @@ -455,7 +455,7 @@ extension ObservableType { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest - (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element) throws -> Element) + (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: @escaping @Sendable (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element) throws -> Element) -> Observable { return CombineLatest6( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), @@ -482,7 +482,7 @@ extension ObservableType where Element == Any { } } -final class CombineLatestSink6_ : CombineLatestSink { +final class CombineLatestSink6_ : CombineLatestSink, @unchecked Sendable { typealias Result = Observer.Element typealias Parent = CombineLatest6 @@ -537,8 +537,8 @@ final class CombineLatestSink6_ } } -final class CombineLatest6 : Producer { - typealias ResultSelector = (E1, E2, E3, E4, E5, E6) throws -> Result +final class CombineLatest6 : Producer, @unchecked Sendable { + typealias ResultSelector = @Sendable (E1, E2, E3, E4, E5, E6) throws -> Result let source1: Observable let source2: Observable @@ -581,7 +581,7 @@ extension ObservableType { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest - (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element) throws -> Element) + (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: @escaping @Sendable (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element) throws -> Element) -> Observable { return CombineLatest7( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), source7: source7.asObservable(), @@ -608,7 +608,7 @@ extension ObservableType where Element == Any { } } -final class CombineLatestSink7_ : CombineLatestSink { +final class CombineLatestSink7_ : CombineLatestSink, @unchecked Sendable { typealias Result = Observer.Element typealias Parent = CombineLatest7 @@ -668,8 +668,8 @@ final class CombineLatestSink7_ : Producer { - typealias ResultSelector = (E1, E2, E3, E4, E5, E6, E7) throws -> Result +final class CombineLatest7 : Producer, @unchecked Sendable { + typealias ResultSelector = @Sendable (E1, E2, E3, E4, E5, E6, E7) throws -> Result let source1: Observable let source2: Observable @@ -714,7 +714,7 @@ extension ObservableType { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func combineLatest - (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element, O8.Element) throws -> Element) + (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: @escaping @Sendable (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element, O8.Element) throws -> Element) -> Observable { return CombineLatest8( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), source7: source7.asObservable(), source8: source8.asObservable(), @@ -741,7 +741,7 @@ extension ObservableType where Element == Any { } } -final class CombineLatestSink8_ : CombineLatestSink { +final class CombineLatestSink8_ : CombineLatestSink, @unchecked Sendable { typealias Result = Observer.Element typealias Parent = CombineLatest8 @@ -806,8 +806,8 @@ final class CombineLatestSink8_ : Producer { - typealias ResultSelector = (E1, E2, E3, E4, E5, E6, E7, E8) throws -> Result +final class CombineLatest8 : Producer, @unchecked Sendable { + typealias ResultSelector = @Sendable (E1, E2, E3, E4, E5, E6, E7, E8) throws -> Result let source1: Observable let source2: Observable diff --git a/RxSwift/Observables/CombineLatest.swift b/RxSwift/Observables/CombineLatest.swift index caa025640..e278a8288 100644 --- a/RxSwift/Observables/CombineLatest.swift +++ b/RxSwift/Observables/CombineLatest.swift @@ -14,8 +14,9 @@ protocol CombineLatestProtocol: AnyObject { class CombineLatestSink : Sink - , CombineLatestProtocol { - typealias Element = Observer.Element + , CombineLatestProtocol + , @unchecked Sendable { + typealias Element = Observer.Element let lock = RecursiveLock() @@ -93,8 +94,9 @@ class CombineLatestSink final class CombineLatestObserver : ObserverType , LockOwnerType - , SynchronizedOnType { - typealias ValueSetter = (Element) -> Void + , SynchronizedOnType + , @unchecked Sendable { + typealias ValueSetter = @Sendable (Element) -> Void private let parent: CombineLatestProtocol diff --git a/RxSwift/Observables/CompactMap.swift b/RxSwift/Observables/CompactMap.swift index 7dbcdfbe9..a2bc49f3d 100644 --- a/RxSwift/Observables/CompactMap.swift +++ b/RxSwift/Observables/CompactMap.swift @@ -15,14 +15,14 @@ extension ObservableType { - returns: An observable sequence whose elements are the result of filtering the transform function for each element of the source. */ - public func compactMap(_ transform: @escaping (Element) throws -> Result?) + public func compactMap(_ transform: @escaping @Sendable (Element) throws -> Result?) -> Observable { CompactMap(source: self.asObservable(), transform: transform) } } -final private class CompactMapSink: Sink, ObserverType { - typealias Transform = (SourceType) throws -> ResultType? +final private class CompactMapSink: Sink, ObserverType, @unchecked Sendable { + typealias Transform = @Sendable (SourceType) throws -> ResultType? typealias ResultType = Observer.Element typealias Element = SourceType @@ -56,8 +56,8 @@ final private class CompactMapSink: Sink: Producer { - typealias Transform = (SourceType) throws -> ResultType? +final private class CompactMap: Producer, @unchecked Sendable { + typealias Transform = @Sendable (SourceType) throws -> ResultType? private let source: Observable diff --git a/RxSwift/Observables/Concat.swift b/RxSwift/Observables/Concat.swift index b2fb1a2e5..d5e25ca09 100644 --- a/RxSwift/Observables/Concat.swift +++ b/RxSwift/Observables/Concat.swift @@ -78,8 +78,9 @@ extension ObservableType { final private class ConcatSink : TailRecursiveSink - , ObserverType where Sequence.Element: ObservableConvertibleType, Sequence.Element.Element == Observer.Element { - typealias Element = Observer.Element + , ObserverType + , @unchecked Sendable where Sequence.Element: ObservableConvertibleType, Sequence.Element.Element == Observer.Element { + typealias Element = Observer.Element override init(observer: Observer, cancel: Cancelable) { super.init(observer: observer, cancel: cancel) @@ -111,7 +112,7 @@ final private class ConcatSink } } -final private class Concat: Producer where Sequence.Element: ObservableConvertibleType { +final private class Concat: Producer, @unchecked Sendable where Sequence.Element: ObservableConvertibleType { typealias Element = Sequence.Element.Element fileprivate let sources: Sequence diff --git a/RxSwift/Observables/Create.swift b/RxSwift/Observables/Create.swift index ca81442ce..495f8a120 100644 --- a/RxSwift/Observables/Create.swift +++ b/RxSwift/Observables/Create.swift @@ -17,12 +17,12 @@ extension ObservableType { - parameter subscribe: Implementation of the resulting observable sequence's `subscribe` method. - returns: The observable sequence with the specified implementation for the `subscribe` method. */ - public static func create(_ subscribe: @escaping (AnyObserver) -> Disposable) -> Observable { + public static func create(_ subscribe: @escaping @Sendable (AnyObserver) -> Disposable) -> Observable { AnonymousObservable(subscribe) } } -final private class AnonymousObservableSink: Sink, ObserverType { +final private class AnonymousObservableSink: Sink, ObserverType, @unchecked Sendable { typealias Element = Observer.Element typealias Parent = AnonymousObservable @@ -61,8 +61,8 @@ final private class AnonymousObservableSink: Sink: Producer { - typealias SubscribeHandler = (AnyObserver) -> Disposable +final private class AnonymousObservable: Producer, @unchecked Sendable { + typealias SubscribeHandler = @Sendable (AnyObserver) -> Disposable let subscribeHandler: SubscribeHandler diff --git a/RxSwift/Observables/Debounce.swift b/RxSwift/Observables/Debounce.swift index 581a5cc5e..b8a9132c8 100644 --- a/RxSwift/Observables/Debounce.swift +++ b/RxSwift/Observables/Debounce.swift @@ -29,8 +29,9 @@ final private class DebounceSink : Sink , ObserverType , LockOwnerType - , SynchronizedOnType { - typealias Element = Observer.Element + , SynchronizedOnType + , @unchecked Sendable { + typealias Element = Observer.Element typealias ParentType = Debounce private let parent: ParentType @@ -87,6 +88,7 @@ final private class DebounceSink } } + @Sendable func propagate(_ currentId: UInt64) -> Disposable { self.lock.performLocked { let originalValue = self.value @@ -101,7 +103,7 @@ final private class DebounceSink } } -final private class Debounce: Producer { +final private class Debounce: Producer, @unchecked Sendable { fileprivate let source: Observable fileprivate let dueTime: RxTimeInterval fileprivate let scheduler: SchedulerType diff --git a/RxSwift/Observables/Debug.swift b/RxSwift/Observables/Debug.swift index 762e394fc..10e863c5b 100644 --- a/RxSwift/Observables/Debug.swift +++ b/RxSwift/Observables/Debug.swift @@ -31,7 +31,7 @@ private func logEvent(_ identifier: String, dateFormat: DateFormatter, content: print("\(dateFormat.string(from: Date())): \(identifier) -> \(content)") } -final private class DebugSink: Sink, ObserverType where Observer.Element == Source.Element { +final private class DebugSink: Sink, ObserverType, @unchecked Sendable where Observer.Element == Source.Element { typealias Element = Observer.Element typealias Parent = Debug @@ -71,7 +71,7 @@ final private class DebugSink: S } } -final private class Debug: Producer { +final private class Debug: Producer, @unchecked Sendable { fileprivate let identifier: String fileprivate let trimOutput: Bool private let source: Source diff --git a/RxSwift/Observables/DefaultIfEmpty.swift b/RxSwift/Observables/DefaultIfEmpty.swift index 8d133d8f8..356bdbf33 100644 --- a/RxSwift/Observables/DefaultIfEmpty.swift +++ b/RxSwift/Observables/DefaultIfEmpty.swift @@ -21,7 +21,7 @@ extension ObservableType { } } -final private class DefaultIfEmptySink: Sink, ObserverType { +final private class DefaultIfEmptySink: Sink, ObserverType, @unchecked Sendable { typealias Element = Observer.Element private let `default`: Element private var isEmpty = true @@ -49,7 +49,7 @@ final private class DefaultIfEmptySink: Sink, } } -final private class DefaultIfEmpty: Producer { +final private class DefaultIfEmpty: Producer, @unchecked Sendable { private let source: Observable private let `default`: SourceType diff --git a/RxSwift/Observables/Deferred.swift b/RxSwift/Observables/Deferred.swift index 51e64d1c0..f86100f04 100644 --- a/RxSwift/Observables/Deferred.swift +++ b/RxSwift/Observables/Deferred.swift @@ -15,13 +15,13 @@ extension ObservableType { - parameter observableFactory: Observable factory function to invoke for each observer that subscribes to the resulting sequence. - returns: An observable sequence whose observers trigger an invocation of the given observable factory function. */ - public static func deferred(_ observableFactory: @escaping () throws -> Observable) + public static func deferred(_ observableFactory: @escaping @Sendable () throws -> Observable) -> Observable { Deferred(observableFactory: observableFactory) } } -final private class DeferredSink: Sink, ObserverType where Source.Element == Observer.Element { +final private class DeferredSink: Sink, ObserverType, @unchecked Sendable where Source.Element == Observer.Element { typealias Element = Observer.Element typealias Parent = Deferred @@ -55,8 +55,8 @@ final private class DeferredSink } } -final private class Deferred: Producer { - typealias Factory = () throws -> Source +final private class Deferred: Producer, @unchecked Sendable { + typealias Factory = @Sendable () throws -> Source let observableFactory : Factory diff --git a/RxSwift/Observables/Delay.swift b/RxSwift/Observables/Delay.swift index 9f71c32d0..f15779b17 100644 --- a/RxSwift/Observables/Delay.swift +++ b/RxSwift/Observables/Delay.swift @@ -27,8 +27,9 @@ extension ObservableType { final private class DelaySink : Sink - , ObserverType { - typealias Element = Observer.Element + , ObserverType + , @unchecked Sendable { + typealias Element = Observer.Element typealias Source = Observable typealias DisposeKey = Bag.KeyType @@ -60,6 +61,7 @@ final private class DelaySink // scheduler so this process needs to be synchronized somehow. // // Another complication is that scheduler is potentially concurrent so internal queue is used. + @Sendable func drainQueue(state: (), scheduler: AnyRecursiveScheduler<()>) { self.lock.lock() let hasFailed = self.errorEvent != nil @@ -155,7 +157,7 @@ final private class DelaySink } } -final private class Delay: Producer { +final private class Delay: Producer, @unchecked Sendable { private let source: Observable private let dueTime: RxTimeInterval private let scheduler: SchedulerType diff --git a/RxSwift/Observables/DelaySubscription.swift b/RxSwift/Observables/DelaySubscription.swift index 099c0ddcb..97154f46d 100644 --- a/RxSwift/Observables/DelaySubscription.swift +++ b/RxSwift/Observables/DelaySubscription.swift @@ -26,8 +26,9 @@ extension ObservableType { } final private class DelaySubscriptionSink - : Sink, ObserverType { - typealias Element = Observer.Element + : Sink, ObserverType + , @unchecked Sendable { + typealias Element = Observer.Element func on(_ event: Event) { self.forwardOn(event) @@ -38,7 +39,7 @@ final private class DelaySubscriptionSink } -final private class DelaySubscription: Producer { +final private class DelaySubscription: Producer, @unchecked Sendable { private let source: Observable private let dueTime: RxTimeInterval private let scheduler: SchedulerType diff --git a/RxSwift/Observables/Dematerialize.swift b/RxSwift/Observables/Dematerialize.swift index 2ccd54620..8e05c12aa 100644 --- a/RxSwift/Observables/Dematerialize.swift +++ b/RxSwift/Observables/Dematerialize.swift @@ -18,7 +18,7 @@ extension ObservableType where Element: EventConvertible { } -private final class DematerializeSink: Sink, ObserverType where Observer.Element == T.Element { +private final class DematerializeSink: Sink, ObserverType, @unchecked Sendable where Observer.Element == T.Element { fileprivate func on(_ event: Event) { switch event { case .next(let element): @@ -36,7 +36,7 @@ private final class DematerializeSink: Producer { +final private class Dematerialize: Producer, @unchecked Sendable { private let source: Observable init(source: Observable) { diff --git a/RxSwift/Observables/DistinctUntilChanged.swift b/RxSwift/Observables/DistinctUntilChanged.swift index c127b5aa5..0429fb0f6 100644 --- a/RxSwift/Observables/DistinctUntilChanged.swift +++ b/RxSwift/Observables/DistinctUntilChanged.swift @@ -30,7 +30,7 @@ extension ObservableType { - parameter keySelector: A function to compute the comparison key for each element. - returns: An observable sequence only containing the distinct contiguous elements, based on a computed key value, from the source sequence. */ - public func distinctUntilChanged(_ keySelector: @escaping (Element) throws -> Key) + public func distinctUntilChanged(_ keySelector: @escaping @Sendable (Element) throws -> Key) -> Observable { self.distinctUntilChanged(keySelector, comparer: { $0 == $1 }) } @@ -43,7 +43,7 @@ extension ObservableType { - parameter comparer: Equality comparer for computed key values. - returns: An observable sequence only containing the distinct contiguous elements, based on `comparer`, from the source sequence. */ - public func distinctUntilChanged(_ comparer: @escaping (Element, Element) throws -> Bool) + public func distinctUntilChanged(_ comparer: @escaping @Sendable (Element, Element) throws -> Bool) -> Observable { self.distinctUntilChanged({ $0 }, comparer: comparer) } @@ -57,7 +57,7 @@ extension ObservableType { - parameter comparer: Equality comparer for computed key values. - returns: An observable sequence only containing the distinct contiguous elements, based on a computed key value and the comparer, from the source sequence. */ - public func distinctUntilChanged(_ keySelector: @escaping (Element) throws -> K, comparer: @escaping (K, K) throws -> Bool) + public func distinctUntilChanged(_ keySelector: @escaping @Sendable (Element) throws -> K, comparer: @escaping @Sendable (K, K) throws -> Bool) -> Observable { return DistinctUntilChanged(source: self.asObservable(), selector: keySelector, comparer: comparer) } @@ -75,7 +75,7 @@ extension ObservableType { } } -final private class DistinctUntilChangedSink: Sink, ObserverType { +final private class DistinctUntilChangedSink: Sink, ObserverType, @unchecked Sendable { typealias Element = Observer.Element private let parent: DistinctUntilChanged @@ -115,9 +115,9 @@ final private class DistinctUntilChangedSink: Sink< } } -final private class DistinctUntilChanged: Producer { - typealias KeySelector = (Element) throws -> Key - typealias EqualityComparer = (Key, Key) throws -> Bool +final private class DistinctUntilChanged: Producer, @unchecked Sendable { + typealias KeySelector = @Sendable (Element) throws -> Key + typealias EqualityComparer = @Sendable (Key, Key) throws -> Bool private let source: Observable fileprivate let selector: KeySelector diff --git a/RxSwift/Observables/Do.swift b/RxSwift/Observables/Do.swift index 3c8c68b3d..0069c0dd7 100644 --- a/RxSwift/Observables/Do.swift +++ b/RxSwift/Observables/Do.swift @@ -23,7 +23,7 @@ extension ObservableType { - parameter onDispose: Action to invoke after subscription to source observable has been disposed for any reason. It can be either because sequence terminates for some reason or observer subscription being disposed. - returns: The source sequence with the side-effecting behavior applied. */ - public func `do`(onNext: ((Element) throws -> Void)? = nil, afterNext: ((Element) throws -> Void)? = nil, onError: ((Swift.Error) throws -> Void)? = nil, afterError: ((Swift.Error) throws -> Void)? = nil, onCompleted: (() throws -> Void)? = nil, afterCompleted: (() throws -> Void)? = nil, onSubscribe: (() -> Void)? = nil, onSubscribed: (() -> Void)? = nil, onDispose: (() -> Void)? = nil) + public func `do`(onNext: (@Sendable (Element) throws -> Void)? = nil, afterNext: (@Sendable (Element) throws -> Void)? = nil, onError: (@Sendable (Swift.Error) throws -> Void)? = nil, afterError: (@Sendable (Swift.Error) throws -> Void)? = nil, onCompleted: (@Sendable () throws -> Void)? = nil, afterCompleted: (@Sendable () throws -> Void)? = nil, onSubscribe: (@Sendable () -> Void)? = nil, onSubscribed: (@Sendable () -> Void)? = nil, onDispose: (@Sendable () -> Void)? = nil) -> Observable { return Do(source: self.asObservable(), eventHandler: { e in switch e { @@ -47,10 +47,10 @@ extension ObservableType { } } -final private class DoSink: Sink, ObserverType { +final private class DoSink: Sink, ObserverType, @unchecked Sendable { typealias Element = Observer.Element - typealias EventHandler = (Event) throws -> Void - typealias AfterEventHandler = (Event) throws -> Void + typealias EventHandler = @Sendable (Event) throws -> Void + typealias AfterEventHandler = @Sendable (Event) throws -> Void private let eventHandler: EventHandler private let afterEventHandler: AfterEventHandler @@ -77,18 +77,18 @@ final private class DoSink: Sink, ObserverType } } -final private class Do: Producer { - typealias EventHandler = (Event) throws -> Void - typealias AfterEventHandler = (Event) throws -> Void +final private class Do: Producer, @unchecked Sendable { + typealias EventHandler = @Sendable (Event) throws -> Void + typealias AfterEventHandler = @Sendable (Event) throws -> Void private let source: Observable private let eventHandler: EventHandler private let afterEventHandler: AfterEventHandler - private let onSubscribe: (() -> Void)? - private let onSubscribed: (() -> Void)? - private let onDispose: (() -> Void)? + private let onSubscribe: (@Sendable () -> Void)? + private let onSubscribed: (@Sendable () -> Void)? + private let onDispose: (@Sendable () -> Void)? - init(source: Observable, eventHandler: @escaping EventHandler, afterEventHandler: @escaping AfterEventHandler, onSubscribe: (() -> Void)?, onSubscribed: (() -> Void)?, onDispose: (() -> Void)?) { + init(source: Observable, eventHandler: @escaping EventHandler, afterEventHandler: @escaping AfterEventHandler, onSubscribe: (@Sendable () -> Void)?, onSubscribed: (@Sendable () -> Void)?, onDispose: (@Sendable () -> Void)?) { self.source = source self.eventHandler = eventHandler self.afterEventHandler = afterEventHandler diff --git a/RxSwift/Observables/ElementAt.swift b/RxSwift/Observables/ElementAt.swift index 3f09c7087..4fd6330b8 100644 --- a/RxSwift/Observables/ElementAt.swift +++ b/RxSwift/Observables/ElementAt.swift @@ -35,7 +35,7 @@ extension ObservableType { } } -final private class ElementAtSink: Sink, ObserverType { +final private class ElementAtSink: Sink, ObserverType, @unchecked Sendable { typealias SourceType = Observer.Element typealias Parent = ElementAt @@ -82,7 +82,7 @@ final private class ElementAtSink: Sink, Obser } } -final private class ElementAt: Producer { +final private class ElementAt: Producer, @unchecked Sendable { let source: Observable let throwOnEmpty: Bool let index: Int diff --git a/RxSwift/Observables/Empty.swift b/RxSwift/Observables/Empty.swift index 9ca596594..150be905f 100644 --- a/RxSwift/Observables/Empty.swift +++ b/RxSwift/Observables/Empty.swift @@ -19,7 +19,7 @@ extension ObservableType { } } -final private class EmptyProducer: Producer { +final private class EmptyProducer: Producer, @unchecked Sendable { override func subscribe(_ observer: Observer) -> Disposable where Observer.Element == Element { observer.on(.completed) return Disposables.create() diff --git a/RxSwift/Observables/Enumerated.swift b/RxSwift/Observables/Enumerated.swift index ef8a0ff02..ded154f78 100644 --- a/RxSwift/Observables/Enumerated.swift +++ b/RxSwift/Observables/Enumerated.swift @@ -21,7 +21,7 @@ extension ObservableType { } } -final private class EnumeratedSink: Sink, ObserverType where Observer.Element == (index: Int, element: Element) { +final private class EnumeratedSink: Sink, ObserverType, @unchecked Sendable where Observer.Element == (index: Int, element: Element) { var index = 0 func on(_ event: Event) { @@ -46,7 +46,7 @@ final private class EnumeratedSink: Sink: Producer<(index: Int, element: Element)> { +final private class Enumerated: Producer<(index: Int, element: Element)>, @unchecked Sendable { private let source: Observable init(source: Observable) { diff --git a/RxSwift/Observables/Error.swift b/RxSwift/Observables/Error.swift index 2e722d670..b15fa1773 100644 --- a/RxSwift/Observables/Error.swift +++ b/RxSwift/Observables/Error.swift @@ -19,7 +19,7 @@ extension ObservableType { } } -final private class ErrorProducer: Producer { +final private class ErrorProducer: Producer, @unchecked Sendable { private let error: Swift.Error init(error: Swift.Error) { diff --git a/RxSwift/Observables/Filter.swift b/RxSwift/Observables/Filter.swift index 61d3ce793..06a0bce1f 100644 --- a/RxSwift/Observables/Filter.swift +++ b/RxSwift/Observables/Filter.swift @@ -16,7 +16,7 @@ extension ObservableType { - parameter predicate: A function to test each source element for a condition. - returns: An observable sequence that contains elements from the input sequence that satisfy the condition. */ - public func filter(_ predicate: @escaping (Element) throws -> Bool) + public func filter(_ predicate: @escaping @Sendable (Element) throws -> Bool) -> Observable { Filter(source: self.asObservable(), predicate: predicate) } @@ -36,8 +36,8 @@ extension ObservableType { } } -final private class FilterSink: Sink, ObserverType { - typealias Predicate = (Element) throws -> Bool +final private class FilterSink: Sink, ObserverType, @unchecked Sendable { + typealias Predicate = @Sendable (Element) throws -> Bool typealias Element = Observer.Element private let predicate: Predicate @@ -67,8 +67,8 @@ final private class FilterSink: Sink, Observer } } -final private class Filter: Producer { - typealias Predicate = (Element) throws -> Bool +final private class Filter: Producer, @unchecked Sendable { + typealias Predicate = @Sendable (Element) throws -> Bool private let source: Observable private let predicate: Predicate diff --git a/RxSwift/Observables/First.swift b/RxSwift/Observables/First.swift index 001411485..e3242c28d 100644 --- a/RxSwift/Observables/First.swift +++ b/RxSwift/Observables/First.swift @@ -6,7 +6,7 @@ // Copyright © 2017 Krunoslav Zaher. All rights reserved. // -private final class FirstSink : Sink, ObserverType where Observer.Element == Element? { +private final class FirstSink : Sink, ObserverType, @unchecked Sendable where Observer.Element == Element? { typealias Parent = First func on(_ event: Event) { @@ -26,7 +26,7 @@ private final class FirstSink : Sink, } } -final class First: Producer { +final class First: Producer, @unchecked Sendable { private let source: Observable init(source: Observable) { diff --git a/RxSwift/Observables/Generate.swift b/RxSwift/Observables/Generate.swift index 7b924b381..8f0f3be63 100644 --- a/RxSwift/Observables/Generate.swift +++ b/RxSwift/Observables/Generate.swift @@ -19,12 +19,12 @@ extension ObservableType { - parameter scheduler: Scheduler on which to run the generator loop. - returns: The generated sequence. */ - public static func generate(initialState: Element, condition: @escaping (Element) throws -> Bool, scheduler: ImmediateSchedulerType = CurrentThreadScheduler.instance, iterate: @escaping (Element) throws -> Element) -> Observable { + public static func generate(initialState: Element, condition: @escaping @Sendable (Element) throws -> Bool, scheduler: ImmediateSchedulerType = CurrentThreadScheduler.instance, iterate: @escaping @Sendable (Element) throws -> Element) -> Observable { Generate(initialState: initialState, condition: condition, iterate: iterate, resultSelector: { $0 }, scheduler: scheduler) } } -final private class GenerateSink: Sink { +final private class GenerateSink: Sink, @unchecked Sendable { typealias Parent = Generate private let parent: Parent @@ -63,14 +63,14 @@ final private class GenerateSink: Sink: Producer { +final private class Generate: Producer, @unchecked Sendable { fileprivate let initialState: Sequence - fileprivate let condition: (Sequence) throws -> Bool - fileprivate let iterate: (Sequence) throws -> Sequence - fileprivate let resultSelector: (Sequence) throws -> Element + fileprivate let condition: @Sendable (Sequence) throws -> Bool + fileprivate let iterate: @Sendable (Sequence) throws -> Sequence + fileprivate let resultSelector: @Sendable (Sequence) throws -> Element fileprivate let scheduler: ImmediateSchedulerType - init(initialState: Sequence, condition: @escaping (Sequence) throws -> Bool, iterate: @escaping (Sequence) throws -> Sequence, resultSelector: @escaping (Sequence) throws -> Element, scheduler: ImmediateSchedulerType) { + init(initialState: Sequence, condition: @escaping @Sendable (Sequence) throws -> Bool, iterate: @escaping @Sendable (Sequence) throws -> Sequence, resultSelector: @escaping @Sendable (Sequence) throws -> Element, scheduler: ImmediateSchedulerType) { self.initialState = initialState self.condition = condition self.iterate = iterate diff --git a/RxSwift/Observables/GroupBy.swift b/RxSwift/Observables/GroupBy.swift index 88cbda767..1dec82bd4 100644 --- a/RxSwift/Observables/GroupBy.swift +++ b/RxSwift/Observables/GroupBy.swift @@ -15,13 +15,13 @@ extension ObservableType { - parameter keySelector: A function to extract the key for each element. - returns: A sequence of observable groups, each of which corresponds to a unique key value, containing all elements that share that same key value. */ - public func groupBy(keySelector: @escaping (Element) throws -> Key) + public func groupBy(keySelector: @escaping @Sendable (Element) throws -> Key) -> Observable> { GroupBy(source: self.asObservable(), selector: keySelector) } } -final private class GroupedObservableImpl: Observable { +final private class GroupedObservableImpl: Observable, @unchecked Sendable { private var subject: PublishSubject private var refCount: RefCountDisposable @@ -40,8 +40,9 @@ final private class GroupedObservableImpl: Observable { final private class GroupBySink : Sink - , ObserverType where Observer.Element == GroupedObservable { - typealias ResultType = Observer.Element + , ObserverType + , @unchecked Sendable where Observer.Element == GroupedObservable { + typealias ResultType = Observer.Element typealias Parent = GroupBy private let parent: Parent @@ -115,8 +116,8 @@ final private class GroupBySink } } -final private class GroupBy: Producer> { - typealias KeySelector = (Element) throws -> Key +final private class GroupBy: Producer>, @unchecked Sendable { + typealias KeySelector = @Sendable (Element) throws -> Key fileprivate let source: Observable fileprivate let selector: KeySelector diff --git a/RxSwift/Observables/Just.swift b/RxSwift/Observables/Just.swift index 93490837f..6a1397e42 100644 --- a/RxSwift/Observables/Just.swift +++ b/RxSwift/Observables/Just.swift @@ -33,7 +33,7 @@ extension ObservableType { } } -final private class JustScheduledSink: Sink { +final private class JustScheduledSink: Sink, @unchecked Sendable { typealias Parent = JustScheduled private let parent: Parent @@ -56,7 +56,7 @@ final private class JustScheduledSink: Sink { } } -final private class JustScheduled: Producer { +final private class JustScheduled: Producer, @unchecked Sendable { fileprivate let scheduler: ImmediateSchedulerType fileprivate let element: Element @@ -72,7 +72,7 @@ final private class JustScheduled: Producer { } } -final private class Just: Producer { +final private class Just: Producer, @unchecked Sendable { private let element: Element init(element: Element) { diff --git a/RxSwift/Observables/Map.swift b/RxSwift/Observables/Map.swift index 693794431..c13e32e6b 100644 --- a/RxSwift/Observables/Map.swift +++ b/RxSwift/Observables/Map.swift @@ -17,14 +17,14 @@ extension ObservableType { - returns: An observable sequence whose elements are the result of invoking the transform function on each element of source. */ - public func map(_ transform: @escaping (Element) throws -> Result) + public func map(_ transform: @escaping @Sendable (Element) throws -> Result) -> Observable { Map(source: self.asObservable(), transform: transform) } } -final private class MapSink: Sink, ObserverType { - typealias Transform = (SourceType) throws -> ResultType +final private class MapSink: Sink, ObserverType, @unchecked Sendable { + typealias Transform = @Sendable (SourceType) throws -> ResultType typealias ResultType = Observer.Element @@ -56,8 +56,8 @@ final private class MapSink: Sink, } } -final private class Map: Producer { - typealias Transform = (SourceType) throws -> ResultType +final private class Map: Producer, @unchecked Sendable { + typealias Transform = @Sendable (SourceType) throws -> ResultType private let source: Observable diff --git a/RxSwift/Observables/Materialize.swift b/RxSwift/Observables/Materialize.swift index e2577be5d..4b1fd82bd 100644 --- a/RxSwift/Observables/Materialize.swift +++ b/RxSwift/Observables/Materialize.swift @@ -17,7 +17,7 @@ extension ObservableType { } } -private final class MaterializeSink: Sink, ObserverType where Observer.Element == Event { +private final class MaterializeSink: Sink, ObserverType, @unchecked Sendable where Observer.Element == Event { func on(_ event: Event) { self.forwardOn(.next(event)) @@ -28,7 +28,7 @@ private final class MaterializeSink: Sink: Producer> { +final private class Materialize: Producer>, @unchecked Sendable { private let source: Observable init(source: Observable) { diff --git a/RxSwift/Observables/Merge.swift b/RxSwift/Observables/Merge.swift index 51a646291..e5a91b3d5 100644 --- a/RxSwift/Observables/Merge.swift +++ b/RxSwift/Observables/Merge.swift @@ -16,7 +16,7 @@ extension ObservableType { - parameter selector: A transform function to apply to each element. - returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence. */ - public func flatMap(_ selector: @escaping (Element) throws -> Source) + public func flatMap(_ selector: @escaping @Sendable (Element) throws -> Source) -> Observable { return FlatMap(source: self.asObservable(), selector: selector) } @@ -34,7 +34,7 @@ extension ObservableType { - parameter selector: A transform function to apply to element that was observed while no observable is executing in parallel. - returns: An observable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence that was received while no other sequence was being calculated. */ - public func flatMapFirst(_ selector: @escaping (Element) throws -> Source) + public func flatMapFirst(_ selector: @escaping @Sendable (Element) throws -> Source) -> Observable { return FlatMapFirst(source: self.asObservable(), selector: selector) } @@ -130,7 +130,7 @@ extension ObservableType { - returns: An observable sequence that contains the elements of each observed inner sequence, in sequential order. */ - public func concatMap(_ selector: @escaping (Element) throws -> Source) + public func concatMap(_ selector: @escaping @Sendable (Element) throws -> Source) -> Observable { return ConcatMap(source: self.asObservable(), selector: selector) } @@ -139,7 +139,8 @@ extension ObservableType { private final class MergeLimitedSinkIter : ObserverType , LockOwnerType - , SynchronizedOnType where SourceSequence.Element == Observer.Element { + , SynchronizedOnType + , @unchecked Sendable where SourceSequence.Element == Observer.Element { typealias Element = Observer.Element typealias DisposeKey = CompositeDisposable.DisposeKey typealias Parent = MergeLimitedSink @@ -184,8 +185,8 @@ private final class MergeLimitedSinkIter: MergeLimitedSink where Observer.Element == SourceSequence.Element { - typealias Selector = (SourceElement) throws -> SourceSequence +private final class ConcatMapSink: MergeLimitedSink, @unchecked Sendable where Observer.Element == SourceSequence.Element { + typealias Selector = @Sendable (SourceElement) throws -> SourceSequence private let selector: Selector @@ -199,7 +200,7 @@ private final class ConcatMapSink: MergeLimitedSink where Observer.Element == SourceSequence.Element { +private final class MergeLimitedBasicSink: MergeLimitedSink, @unchecked Sendable where Observer.Element == SourceSequence.Element { override func performMap(_ element: SourceSequence) throws -> SourceSequence { element @@ -208,7 +209,8 @@ private final class MergeLimitedBasicSink : Sink - , ObserverType where Observer.Element == SourceSequence.Element { + , ObserverType + , @unchecked Sendable where Observer.Element == SourceSequence.Element { typealias QueueType = Queue let maxConcurrent: Int @@ -312,7 +314,7 @@ private class MergeLimitedSink: Producer { +final private class MergeLimited: Producer, @unchecked Sendable { private let source: Observable private let maxConcurrent: Int @@ -330,7 +332,7 @@ final private class MergeLimited: Pro // MARK: Merge -private final class MergeBasicSink : MergeSink where Observer.Element == Source.Element { +private final class MergeBasicSink : MergeSink, @unchecked Sendable where Observer.Element == Source.Element { override func performMap(_ element: Source) throws -> Source { element } @@ -338,8 +340,8 @@ private final class MergeBasicSink : MergeSink where Observer.Element == SourceSequence.Element { - typealias Selector = (SourceElement) throws -> SourceSequence +private final class FlatMapSink : MergeSink, @unchecked Sendable where Observer.Element == SourceSequence.Element { + typealias Selector = @Sendable (SourceElement) throws -> SourceSequence private let selector: Selector @@ -355,8 +357,8 @@ private final class FlatMapSink : MergeSink where Observer.Element == SourceSequence.Element { - typealias Selector = (SourceElement) throws -> SourceSequence +private final class FlatMapFirstSink : MergeSink, @unchecked Sendable where Observer.Element == SourceSequence.Element { + typealias Selector = @Sendable (SourceElement) throws -> SourceSequence private let selector: Selector @@ -374,7 +376,7 @@ private final class FlatMapFirstSink : ObserverType where Observer.Element == SourceSequence.Element { +private final class MergeSinkIter : ObserverType, @unchecked Sendable where Observer.Element == SourceSequence.Element { typealias Parent = MergeSink typealias DisposeKey = CompositeDisposable.DisposeKey typealias Element = Observer.Element @@ -407,7 +409,8 @@ private final class MergeSinkIter : Sink - , ObserverType where Observer.Element == SourceSequence.Element { + , ObserverType + , @unchecked Sendable where Observer.Element == SourceSequence.Element { typealias ResultType = Observer.Element typealias Element = SourceElement @@ -515,8 +518,8 @@ private class MergeSink: Producer { - typealias Selector = (SourceElement) throws -> SourceSequence +final private class FlatMap: Producer, @unchecked Sendable { + typealias Selector = @Sendable (SourceElement) throws -> SourceSequence private let source: Observable @@ -534,8 +537,8 @@ final private class FlatMap: Producer { - typealias Selector = (SourceElement) throws -> SourceSequence +final private class FlatMapFirst: Producer, @unchecked Sendable { + typealias Selector = @Sendable (SourceElement) throws -> SourceSequence private let source: Observable @@ -553,8 +556,8 @@ final private class FlatMapFirst: Producer { - typealias Selector = (SourceElement) throws -> SourceSequence +final class ConcatMap: Producer, @unchecked Sendable { + typealias Selector = @Sendable (SourceElement) throws -> SourceSequence private let source: Observable private let selector: Selector @@ -571,7 +574,7 @@ final class ConcatMap: } } -final class Merge : Producer { +final class Merge : Producer, @unchecked Sendable { private let source: Observable init(source: Observable) { @@ -585,7 +588,7 @@ final class Merge : Producer: Producer { +final private class MergeArray: Producer, @unchecked Sendable { private let sources: [Observable] init(sources: [Observable]) { diff --git a/RxSwift/Observables/Multicast.swift b/RxSwift/Observables/Multicast.swift index 1ebcf0212..8ad07775e 100644 --- a/RxSwift/Observables/Multicast.swift +++ b/RxSwift/Observables/Multicast.swift @@ -11,7 +11,8 @@ */ public class ConnectableObservable : Observable - , ConnectableObservableType { + , ConnectableObservableType + , @unchecked Sendable { /** Connects the observable wrapper to its source. All subscribed observers will receive values from the underlying observable sequence as long as the connection is established. @@ -38,7 +39,7 @@ extension ObservableType { - parameter selector: Selector function which can use the multicasted source sequence subject to the policies enforced by the created subject. - returns: An observable sequence that contains the elements of a sequence produced by multicasting the source sequence within a selector function. */ - public func multicast(_ subjectSelector: @escaping () throws -> Subject, selector: @escaping (Observable) throws -> Observable) + public func multicast(_ subjectSelector: @escaping @Sendable () throws -> Subject, selector: @escaping @Sendable (Observable) throws -> Observable) -> Observable where Subject.Observer.Element == Element { return Multicast( source: self.asObservable(), @@ -141,13 +142,13 @@ extension ObservableType { - parameter makeSubject: Factory function used to instantiate a subject for each connection. - returns: A connectable observable sequence that upon connection causes the source sequence to push results into the specified subject. */ - public func multicast(makeSubject: @escaping () -> Subject) + public func multicast(makeSubject: @escaping @Sendable () -> Subject) -> ConnectableObservable where Subject.Observer.Element == Element { ConnectableObservableAdapter(source: self.asObservable(), makeSubject: makeSubject) } } -final private class Connection: ObserverType, Disposable { +final private class Connection: ObserverType, Disposable, @unchecked Sendable { typealias Element = Subject.Observer.Element private var lock: RecursiveLock @@ -194,11 +195,12 @@ final private class Connection: ObserverType, Disposable { } final private class ConnectableObservableAdapter - : ConnectableObservable { + : ConnectableObservable + , @unchecked Sendable { typealias ConnectionType = Connection private let source: Observable - private let makeSubject: () -> Subject + private let makeSubject: @Sendable () -> Subject fileprivate let lock = RecursiveLock() fileprivate var subject: Subject? @@ -206,7 +208,7 @@ final private class ConnectableObservableAdapter // state fileprivate var connection: ConnectionType? - init(source: Observable, makeSubject: @escaping () -> Subject) { + init(source: Observable, makeSubject: @escaping @Sendable () -> Subject) { self.source = source self.makeSubject = makeSubject self.subject = nil @@ -245,8 +247,9 @@ final private class ConnectableObservableAdapter final private class RefCountSink : Sink - , ObserverType where ConnectableSource.Element == Observer.Element { - typealias Element = Observer.Element + , ObserverType + , @unchecked Sendable where ConnectableSource.Element == Observer.Element { + typealias Element = Observer.Element typealias Parent = RefCount private let parent: Parent @@ -320,7 +323,7 @@ final private class RefCountSink: Producer { +final private class RefCount: Producer, @unchecked Sendable { fileprivate let lock = RecursiveLock() // state @@ -342,8 +345,8 @@ final private class RefCount: Prod } } -final private class MulticastSink: Sink, ObserverType { - typealias Element = Observer.Element +final private class MulticastSink: Sink, ObserverType, @unchecked Sendable { + typealias Element = Observer.Element typealias ResultType = Element typealias MutlicastType = Multicast @@ -383,9 +386,9 @@ final private class MulticastSink: } } -final private class Multicast: Producer { - typealias SubjectSelectorType = () throws -> Subject - typealias SelectorType = (Observable) throws -> Observable +final private class Multicast: Producer, @unchecked Sendable { + typealias SubjectSelectorType = @Sendable () throws -> Subject + typealias SelectorType = @Sendable (Observable) throws -> Observable fileprivate let source: Observable fileprivate let subjectSelector: SubjectSelectorType diff --git a/RxSwift/Observables/Never.swift b/RxSwift/Observables/Never.swift index 7b456eed9..f700c476a 100644 --- a/RxSwift/Observables/Never.swift +++ b/RxSwift/Observables/Never.swift @@ -20,7 +20,7 @@ extension ObservableType { } } -final private class NeverProducer: Producer { +final private class NeverProducer: Producer, @unchecked Sendable { override func subscribe(_ observer: Observer) -> Disposable where Observer.Element == Element { Disposables.create() } diff --git a/RxSwift/Observables/ObserveOn.swift b/RxSwift/Observables/ObserveOn.swift index 6b8689e1e..a8e976440 100644 --- a/RxSwift/Observables/ObserveOn.swift +++ b/RxSwift/Observables/ObserveOn.swift @@ -46,7 +46,7 @@ extension ObservableType { } } -final private class ObserveOn: Producer { +final private class ObserveOn: Producer, @unchecked Sendable { let scheduler: ImmediateSchedulerType let source: Observable @@ -79,17 +79,17 @@ enum ObserveOnState : Int32 { case running = 1 } -final private class ObserveOnSink: ObserverBase { +final private class ObserveOnSink: ObserverBase, @unchecked Sendable { typealias Element = Observer.Element let scheduler: ImmediateSchedulerType - var lock = SpinLock() + let lock = SpinLock() let observer: Observer // state - var state = ObserveOnState.stopped - var queue = Queue>(capacity: 10) + nonisolated(unsafe) var state = ObserveOnState.stopped + nonisolated(unsafe) var queue = Queue>(capacity: 10) let scheduleDisposable = SerialDisposable() let cancel: Cancelable @@ -118,6 +118,7 @@ final private class ObserveOnSink: ObserverBase Void) { let (nextEvent, observer) = self.lock.performLocked { () -> (Event?, Observer) in if !self.queue.isEmpty { @@ -176,13 +177,13 @@ final private class ObserveOnSink: ObserverBase: ObserverBase { +final private class ObserveOnSerialDispatchQueueSink: ObserverBase, @unchecked Sendable { let scheduler: SerialDispatchQueueScheduler let observer: Observer let cancel: Cancelable - var cachedScheduleLambda: (((sink: ObserveOnSerialDispatchQueueSink, event: Event)) -> Disposable)! + nonisolated(unsafe) var cachedScheduleLambda: (@Sendable ((sink: ObserveOnSerialDispatchQueueSink, event: Event)) -> Disposable)! init(scheduler: SerialDispatchQueueScheduler, observer: Observer, cancel: Cancelable) { self.scheduler = scheduler @@ -190,7 +191,7 @@ final private class ObserveOnSerialDispatchQueueSink: Ob self.cancel = cancel super.init() - self.cachedScheduleLambda = { pair in + self.cachedScheduleLambda = { @Sendable pair in guard !cancel.isDisposed else { return Disposables.create() } pair.sink.observer.on(pair.event) @@ -214,7 +215,7 @@ final private class ObserveOnSerialDispatchQueueSink: Ob } } -final private class ObserveOnSerialDispatchQueue: Producer { +final private class ObserveOnSerialDispatchQueue: Producer, @unchecked Sendable { let scheduler: SerialDispatchQueueScheduler let source: Observable diff --git a/RxSwift/Observables/Optional.swift b/RxSwift/Observables/Optional.swift index 64f8f1fdd..a12e34cff 100644 --- a/RxSwift/Observables/Optional.swift +++ b/RxSwift/Observables/Optional.swift @@ -33,7 +33,7 @@ extension ObservableType { } } -final private class ObservableOptionalScheduledSink: Sink { +final private class ObservableOptionalScheduledSink: Sink, @unchecked Sendable { typealias Element = Observer.Element typealias Parent = ObservableOptionalScheduled @@ -62,7 +62,7 @@ final private class ObservableOptionalScheduledSink: Sin } } -final private class ObservableOptionalScheduled: Producer { +final private class ObservableOptionalScheduled: Producer, @unchecked Sendable { fileprivate let optional: Element? fileprivate let scheduler: ImmediateSchedulerType @@ -78,7 +78,7 @@ final private class ObservableOptionalScheduled: Producer { } } -final private class ObservableOptional: Producer { +final private class ObservableOptional: Producer, @unchecked Sendable { private let optional: Element? init(optional: Element?) { diff --git a/RxSwift/Observables/Producer.swift b/RxSwift/Observables/Producer.swift index e611930a5..0286ef952 100644 --- a/RxSwift/Observables/Producer.swift +++ b/RxSwift/Observables/Producer.swift @@ -6,7 +6,7 @@ // Copyright © 2015 Krunoslav Zaher. All rights reserved. // -class Producer: Observable { +class Producer: Observable, @unchecked Sendable { override init() { super.init() } @@ -36,7 +36,7 @@ class Producer: Observable { } } -private final class SinkDisposer: Cancelable { +private final class SinkDisposer: Cancelable, @unchecked Sendable { private enum DisposeState: Int32 { case disposed = 1 case sinkAndSubscriptionSet = 2 diff --git a/RxSwift/Observables/Range.swift b/RxSwift/Observables/Range.swift index 245353249..f736ace1f 100644 --- a/RxSwift/Observables/Range.swift +++ b/RxSwift/Observables/Range.swift @@ -22,7 +22,7 @@ extension ObservableType where Element: RxAbstractInteger { } } -final private class RangeProducer: Producer { +final private class RangeProducer: Producer, @unchecked Sendable { fileprivate let start: Element fileprivate let count: Element fileprivate let scheduler: ImmediateSchedulerType @@ -48,7 +48,7 @@ final private class RangeProducer: Producer } } -final private class RangeSink: Sink where Observer.Element: RxAbstractInteger { +final private class RangeSink: Sink, @unchecked Sendable where Observer.Element: RxAbstractInteger { typealias Parent = RangeProducer private let parent: Parent diff --git a/RxSwift/Observables/Reduce.swift b/RxSwift/Observables/Reduce.swift index d5fab3478..016c0309f 100644 --- a/RxSwift/Observables/Reduce.swift +++ b/RxSwift/Observables/Reduce.swift @@ -20,7 +20,7 @@ extension ObservableType { - parameter mapResult: A function to transform the final accumulator value into the result value. - returns: An observable sequence containing a single element with the final accumulator value. */ - public func reduce(_ seed: A, accumulator: @escaping (A, Element) throws -> A, mapResult: @escaping (A) throws -> Result) + public func reduce(_ seed: A, accumulator: @escaping @Sendable (A, Element) throws -> A, mapResult: @escaping @Sendable (A) throws -> Result) -> Observable { Reduce(source: self.asObservable(), seed: seed, accumulator: accumulator, mapResult: mapResult) } @@ -36,13 +36,13 @@ extension ObservableType { - parameter accumulator: A accumulator function to be invoked on each element. - returns: An observable sequence containing a single element with the final accumulator value. */ - public func reduce(_ seed: A, accumulator: @escaping (A, Element) throws -> A) + public func reduce(_ seed: A, accumulator: @escaping @Sendable (A, Element) throws -> A) -> Observable { Reduce(source: self.asObservable(), seed: seed, accumulator: accumulator, mapResult: { $0 }) } } -final private class ReduceSink: Sink, ObserverType { +final private class ReduceSink: Sink, ObserverType, @unchecked Sendable { typealias ResultType = Observer.Element typealias Parent = Reduce @@ -84,9 +84,9 @@ final private class ReduceSink: Producer { - typealias AccumulatorType = (AccumulateType, SourceType) throws -> AccumulateType - typealias ResultSelectorType = (AccumulateType) throws -> ResultType +final private class Reduce: Producer, @unchecked Sendable { + typealias AccumulatorType = @Sendable (AccumulateType, SourceType) throws -> AccumulateType + typealias ResultSelectorType = @Sendable (AccumulateType) throws -> ResultType private let source: Observable fileprivate let seed: AccumulateType diff --git a/RxSwift/Observables/Repeat.swift b/RxSwift/Observables/Repeat.swift index 69a3ba02e..272c739a5 100644 --- a/RxSwift/Observables/Repeat.swift +++ b/RxSwift/Observables/Repeat.swift @@ -21,7 +21,7 @@ extension ObservableType { } } -final private class RepeatElement: Producer { +final private class RepeatElement: Producer, @unchecked Sendable { fileprivate let element: Element fileprivate let scheduler: ImmediateSchedulerType @@ -38,7 +38,7 @@ final private class RepeatElement: Producer { } } -final private class RepeatElementSink: Sink { +final private class RepeatElementSink: Sink, @unchecked Sendable { typealias Parent = RepeatElement private let parent: Parent diff --git a/RxSwift/Observables/RetryWhen.swift b/RxSwift/Observables/RetryWhen.swift index e05c1599e..4e0ef284d 100644 --- a/RxSwift/Observables/RetryWhen.swift +++ b/RxSwift/Observables/RetryWhen.swift @@ -16,7 +16,7 @@ extension ObservableType { - parameter notificationHandler: A handler that is passed an observable sequence of errors raised by the source observable and returns and observable that either continues, completes or errors. This behavior is then applied to the source observable. - returns: An observable sequence producing the elements of the given sequence repeatedly until it terminates successfully or is notified to error or complete. */ - public func retry(when notificationHandler: @escaping (Observable) -> TriggerObservable) + public func retry(when notificationHandler: @escaping @Sendable (Observable) -> TriggerObservable) -> Observable { RetryWhenSequence(sources: InfiniteSequence(repeatedValue: self.asObservable()), notificationHandler: notificationHandler) } @@ -31,7 +31,7 @@ extension ObservableType { - returns: An observable sequence producing the elements of the given sequence repeatedly until it terminates successfully or is notified to error or complete. */ @available(*, deprecated, renamed: "retry(when:)") - public func retryWhen(_ notificationHandler: @escaping (Observable) -> TriggerObservable) + public func retryWhen(_ notificationHandler: @escaping @Sendable (Observable) -> TriggerObservable) -> Observable { retry(when: notificationHandler) } @@ -45,7 +45,7 @@ extension ObservableType { - parameter notificationHandler: A handler that is passed an observable sequence of errors raised by the source observable and returns and observable that either continues, completes or errors. This behavior is then applied to the source observable. - returns: An observable sequence producing the elements of the given sequence repeatedly until it terminates successfully or is notified to error or complete. */ - public func retry(when notificationHandler: @escaping (Observable) -> TriggerObservable) + public func retry(when notificationHandler: @escaping @Sendable (Observable) -> TriggerObservable) -> Observable { RetryWhenSequence(sources: InfiniteSequence(repeatedValue: self.asObservable()), notificationHandler: notificationHandler) } @@ -60,7 +60,7 @@ extension ObservableType { - returns: An observable sequence producing the elements of the given sequence repeatedly until it terminates successfully or is notified to error or complete. */ @available(*, deprecated, renamed: "retry(when:)") - public func retryWhen(_ notificationHandler: @escaping (Observable) -> TriggerObservable) + public func retryWhen(_ notificationHandler: @escaping @Sendable (Observable) -> TriggerObservable) -> Observable { RetryWhenSequence(sources: InfiniteSequence(repeatedValue: self.asObservable()), notificationHandler: notificationHandler) } @@ -140,8 +140,9 @@ final private class RetryWhenSequenceSinkIter - : TailRecursiveSink where Sequence.Element: ObservableType, Sequence.Element.Element == Observer.Element { - typealias Element = Observer.Element + : TailRecursiveSink + , @unchecked Sendable where Sequence.Element: ObservableType, Sequence.Element.Element == Observer.Element { + typealias Element = Observer.Element typealias Parent = RetryWhenSequence let lock = RecursiveLock() @@ -192,13 +193,13 @@ final private class RetryWhenSequenceSink: Producer where Sequence.Element: ObservableType { +final private class RetryWhenSequence: Producer, @unchecked Sendable where Sequence.Element: ObservableType { typealias Element = Sequence.Element.Element private let sources: Sequence - fileprivate let notificationHandler: (Observable) -> TriggerObservable + fileprivate let notificationHandler: @Sendable (Observable) -> TriggerObservable - init(sources: Sequence, notificationHandler: @escaping (Observable) -> TriggerObservable) { + init(sources: Sequence, notificationHandler: @escaping @Sendable (Observable) -> TriggerObservable) { self.sources = sources self.notificationHandler = notificationHandler } diff --git a/RxSwift/Observables/Sample.swift b/RxSwift/Observables/Sample.swift index 9301c9af7..3c8bb9b17 100644 --- a/RxSwift/Observables/Sample.swift +++ b/RxSwift/Observables/Sample.swift @@ -73,8 +73,9 @@ final private class SampleSequenceSink : Sink , ObserverType , LockOwnerType - , SynchronizedOnType { - typealias Element = Observer.Element + , SynchronizedOnType + , @unchecked Sendable { + typealias Element = Observer.Element typealias Parent = Sample fileprivate let parent: Parent @@ -120,7 +121,7 @@ final private class SampleSequenceSink } -final private class Sample: Producer { +final private class Sample: Producer, @unchecked Sendable { fileprivate let source: Observable fileprivate let sampler: Observable fileprivate let defaultValue: Element? diff --git a/RxSwift/Observables/Scan.swift b/RxSwift/Observables/Scan.swift index ecf69c80c..3aef6782c 100644 --- a/RxSwift/Observables/Scan.swift +++ b/RxSwift/Observables/Scan.swift @@ -19,7 +19,7 @@ extension ObservableType { - parameter accumulator: An accumulator function to be invoked on each element. - returns: An observable sequence containing the accumulated values. */ - public func scan(into seed: A, accumulator: @escaping (inout A, Element) throws -> Void) + public func scan(into seed: A, accumulator: @escaping @Sendable (inout A, Element) throws -> Void) -> Observable { Scan(source: self.asObservable(), seed: seed, accumulator: accumulator) } @@ -35,7 +35,7 @@ extension ObservableType { - parameter accumulator: An accumulator function to be invoked on each element. - returns: An observable sequence containing the accumulated values. */ - public func scan(_ seed: A, accumulator: @escaping (A, Element) throws -> A) + public func scan(_ seed: A, accumulator: @escaping @Sendable (A, Element) throws -> A) -> Observable { return Scan(source: self.asObservable(), seed: seed) { acc, element in let currentAcc = acc @@ -44,7 +44,7 @@ extension ObservableType { } } -final private class ScanSink: Sink, ObserverType { +final private class ScanSink: Sink, ObserverType, @unchecked Sendable { typealias Accumulate = Observer.Element typealias Parent = Scan @@ -79,8 +79,8 @@ final private class ScanSink: Sink, O } -final private class Scan: Producer { - typealias Accumulator = (inout Accumulate, Element) throws -> Void +final private class Scan: Producer, @unchecked Sendable { + typealias Accumulator = @Sendable (inout Accumulate, Element) throws -> Void private let source: Observable fileprivate let seed: Accumulate diff --git a/RxSwift/Observables/Sequence.swift b/RxSwift/Observables/Sequence.swift index c6d10896e..29ca51943 100644 --- a/RxSwift/Observables/Sequence.swift +++ b/RxSwift/Observables/Sequence.swift @@ -47,7 +47,7 @@ extension ObservableType { } } -final private class ObservableSequenceSink: Sink where Sequence.Element == Observer.Element { +final private class ObservableSequenceSink: Sink, @unchecked Sendable where Sequence.Element == Observer.Element { typealias Parent = ObservableSequence private let parent: Parent @@ -72,7 +72,7 @@ final private class ObservableSequenceSink: Producer { +final private class ObservableSequence: Producer, @unchecked Sendable { fileprivate let elements: Sequence fileprivate let scheduler: ImmediateSchedulerType diff --git a/RxSwift/Observables/ShareReplayScope.swift b/RxSwift/Observables/ShareReplayScope.swift index 59d440dc3..68a2bb2e9 100644 --- a/RxSwift/Observables/ShareReplayScope.swift +++ b/RxSwift/Observables/ShareReplayScope.swift @@ -158,7 +158,8 @@ extension ObservableType { private final class ShareReplay1WhileConnectedConnection : ObserverType - , SynchronizedUnsubscribeType { + , SynchronizedUnsubscribeType + , @unchecked Sendable { typealias Observers = AnyObserver.s typealias DisposeKey = Observers.KeyType @@ -255,7 +256,8 @@ private final class ShareReplay1WhileConnectedConnection // optimized version of share replay for most common case final private class ShareReplay1WhileConnected - : Observable { + : Observable + , @unchecked Sendable { fileprivate typealias Connection = ShareReplay1WhileConnectedConnection @@ -304,7 +306,8 @@ final private class ShareReplay1WhileConnected private final class ShareWhileConnectedConnection : ObserverType - , SynchronizedUnsubscribeType { + , SynchronizedUnsubscribeType + , @unchecked Sendable { typealias Observers = AnyObserver.s typealias DisposeKey = Observers.KeyType @@ -395,7 +398,8 @@ private final class ShareWhileConnectedConnection // optimized version of share replay for most common case final private class ShareWhileConnected - : Observable { + : Observable + , @unchecked Sendable { fileprivate typealias Connection = ShareWhileConnectedConnection diff --git a/RxSwift/Observables/SingleAsync.swift b/RxSwift/Observables/SingleAsync.swift index 04e221764..9dc23ae6b 100644 --- a/RxSwift/Observables/SingleAsync.swift +++ b/RxSwift/Observables/SingleAsync.swift @@ -30,13 +30,13 @@ extension ObservableType { - parameter predicate: A function to test each source element for a condition. - returns: An observable sequence that emits a single element or throws an exception if more (or none) of them are emitted. */ - public func single(_ predicate: @escaping (Element) throws -> Bool) + public func single(_ predicate: @escaping @Sendable (Element) throws -> Bool) -> Observable { SingleAsync(source: self.asObservable(), predicate: predicate) } } -private final class SingleAsyncSink : Sink, ObserverType { +private final class SingleAsyncSink : Sink, ObserverType, @unchecked Sendable { typealias Element = Observer.Element typealias Parent = SingleAsync @@ -85,8 +85,8 @@ private final class SingleAsyncSink : Sink, Ob } } -final class SingleAsync: Producer { - typealias Predicate = (Element) throws -> Bool +final class SingleAsync: Producer, @unchecked Sendable { + typealias Predicate = @Sendable (Element) throws -> Bool private let source: Observable fileprivate let predicate: Predicate? diff --git a/RxSwift/Observables/Sink.swift b/RxSwift/Observables/Sink.swift index ed4fec01e..f04eda75b 100644 --- a/RxSwift/Observables/Sink.swift +++ b/RxSwift/Observables/Sink.swift @@ -6,7 +6,7 @@ // Copyright © 2015 Krunoslav Zaher. All rights reserved. // -class Sink: Disposable { +class Sink: Disposable, @unchecked Sendable { fileprivate let observer: Observer fileprivate let cancel: Cancelable private let disposed = AtomicInt(0) diff --git a/RxSwift/Observables/Skip.swift b/RxSwift/Observables/Skip.swift index 1d7361e8a..15bb7dc98 100644 --- a/RxSwift/Observables/Skip.swift +++ b/RxSwift/Observables/Skip.swift @@ -43,7 +43,7 @@ extension ObservableType { // count version -final private class SkipCountSink: Sink, ObserverType { +final private class SkipCountSink: Sink, ObserverType, @unchecked Sendable { typealias Element = Observer.Element typealias Parent = SkipCount @@ -78,7 +78,7 @@ final private class SkipCountSink: Sink, Obser } -final private class SkipCount: Producer { +final private class SkipCount: Producer, @unchecked Sendable { let source: Observable let count: Int @@ -97,7 +97,7 @@ final private class SkipCount: Producer { // time version -final private class SkipTimeSink: Sink, ObserverType where Observer.Element == Element { +final private class SkipTimeSink: Sink, ObserverType, @unchecked Sendable where Observer.Element == Element { typealias Parent = SkipTime let parent: Parent @@ -141,7 +141,7 @@ final private class SkipTimeSink: Sink: Producer { +final private class SkipTime: Producer, @unchecked Sendable { let source: Observable let duration: RxTimeInterval let scheduler: SchedulerType diff --git a/RxSwift/Observables/SkipUntil.swift b/RxSwift/Observables/SkipUntil.swift index c8fe19340..5a86f264d 100644 --- a/RxSwift/Observables/SkipUntil.swift +++ b/RxSwift/Observables/SkipUntil.swift @@ -87,8 +87,9 @@ final private class SkipUntilSink : Sink , ObserverType , LockOwnerType - , SynchronizedOnType { - typealias Element = Observer.Element + , SynchronizedOnType + , @unchecked Sendable { + typealias Element = Observer.Element typealias Parent = SkipUntil let lock = RecursiveLock() @@ -134,7 +135,7 @@ final private class SkipUntilSink } } -final private class SkipUntil: Producer { +final private class SkipUntil: Producer, @unchecked Sendable { fileprivate let source: Observable fileprivate let other: Observable diff --git a/RxSwift/Observables/SkipWhile.swift b/RxSwift/Observables/SkipWhile.swift index a62f88ddd..adb0b62d1 100644 --- a/RxSwift/Observables/SkipWhile.swift +++ b/RxSwift/Observables/SkipWhile.swift @@ -15,7 +15,7 @@ extension ObservableType { - parameter predicate: A function to test each element for a condition. - returns: An observable sequence that contains the elements from the input sequence starting at the first element in the linear series that does not pass the test specified by predicate. */ - public func skip(while predicate: @escaping (Element) throws -> Bool) -> Observable { + public func skip(while predicate: @escaping @Sendable (Element) throws -> Bool) -> Observable { SkipWhile(source: self.asObservable(), predicate: predicate) } @@ -28,12 +28,12 @@ extension ObservableType { - returns: An observable sequence that contains the elements from the input sequence starting at the first element in the linear series that does not pass the test specified by predicate. */ @available(*, deprecated, renamed: "skip(while:)") - public func skipWhile(_ predicate: @escaping (Element) throws -> Bool) -> Observable { + public func skipWhile(_ predicate: @escaping @Sendable (Element) throws -> Bool) -> Observable { SkipWhile(source: self.asObservable(), predicate: predicate) } } -final private class SkipWhileSink: Sink, ObserverType { +final private class SkipWhileSink: Sink, ObserverType, @unchecked Sendable { typealias Element = Observer.Element typealias Parent = SkipWhile @@ -68,8 +68,8 @@ final private class SkipWhileSink: Sink, Obser } } -final private class SkipWhile: Producer { - typealias Predicate = (Element) throws -> Bool +final private class SkipWhile: Producer, @unchecked Sendable { + typealias Predicate = @Sendable (Element) throws -> Bool private let source: Observable fileprivate let predicate: Predicate diff --git a/RxSwift/Observables/StartWith.swift b/RxSwift/Observables/StartWith.swift index 13fb31d33..4758c1e40 100644 --- a/RxSwift/Observables/StartWith.swift +++ b/RxSwift/Observables/StartWith.swift @@ -22,7 +22,7 @@ extension ObservableType { } } -final private class StartWith: Producer { +final private class StartWith: Producer, @unchecked Sendable { let elements: [Element] let source: Observable diff --git a/RxSwift/Observables/SubscribeOn.swift b/RxSwift/Observables/SubscribeOn.swift index e8e41d9cf..9aba1146d 100644 --- a/RxSwift/Observables/SubscribeOn.swift +++ b/RxSwift/Observables/SubscribeOn.swift @@ -49,7 +49,7 @@ extension ObservableType { } } -final private class SubscribeOnSink: Sink, ObserverType where Ob.Element == Observer.Element { +final private class SubscribeOnSink: Sink, ObserverType, @unchecked Sendable where Ob.Element == Observer.Element { typealias Element = Observer.Element typealias Parent = SubscribeOn @@ -86,7 +86,7 @@ final private class SubscribeOnSink: } } -final private class SubscribeOn: Producer { +final private class SubscribeOn: Producer, @unchecked Sendable { let source: Ob let scheduler: ImmediateSchedulerType diff --git a/RxSwift/Observables/Switch.swift b/RxSwift/Observables/Switch.swift index 5aa60b97a..d0097a552 100644 --- a/RxSwift/Observables/Switch.swift +++ b/RxSwift/Observables/Switch.swift @@ -19,7 +19,7 @@ extension ObservableType { - returns: An observable sequence whose elements are the result of invoking the transform function on each element of source producing an Observable of Observable sequences and that at any point in time produces the elements of the most recent inner observable sequence that has been received. */ - public func flatMapLatest(_ selector: @escaping (Element) throws -> Source) + public func flatMapLatest(_ selector: @escaping @Sendable (Element) throws -> Source) -> Observable { return FlatMapLatest(source: self.asObservable(), selector: selector) } @@ -36,7 +36,7 @@ extension ObservableType { - returns: An observable sequence whose elements are the result of invoking the transform function on each element of source producing an Observable of Observable sequences and that at any point in time produces the elements of the most recent inner observable sequence that has been received. */ - public func flatMapLatest(_ selector: @escaping (Element) throws -> Source) + public func flatMapLatest(_ selector: @escaping @Sendable (Element) throws -> Source) -> Infallible { return Infallible(flatMapLatest(selector)) } @@ -62,7 +62,8 @@ extension ObservableType where Element: ObservableConvertibleType { private class SwitchSink : Sink - , ObserverType where Source.Element == Observer.Element { + , ObserverType + , @unchecked Sendable where Source.Element == Observer.Element { typealias Element = SourceType private let subscriptions: SingleAssignmentDisposable = SingleAssignmentDisposable() @@ -190,7 +191,7 @@ final private class SwitchSinkIter: SwitchSink +final private class SwitchIdentitySink: SwitchSink, @unchecked Sendable where Observer.Element == Source.Element { override init(observer: Observer, cancel: Cancelable) { super.init(observer: observer, cancel: cancel) @@ -201,8 +202,8 @@ final private class SwitchIdentitySink: SwitchSink where Observer.Element == Source.Element { - typealias Selector = (SourceType) throws -> Source +final private class MapSwitchSink: SwitchSink, @unchecked Sendable where Observer.Element == Source.Element { + typealias Selector = @Sendable (SourceType) throws -> Source private let selector: Selector @@ -218,7 +219,7 @@ final private class MapSwitchSink: Producer { +final private class Switch: Producer, @unchecked Sendable { private let source: Observable init(source: Observable) { @@ -232,8 +233,8 @@ final private class Switch: Producer: Producer { - typealias Selector = (SourceType) throws -> Source +final private class FlatMapLatest: Producer, @unchecked Sendable { + typealias Selector = @Sendable (SourceType) throws -> Source private let source: Observable private let selector: Selector diff --git a/RxSwift/Observables/SwitchIfEmpty.swift b/RxSwift/Observables/SwitchIfEmpty.swift index 037bf6970..2500f5f6a 100644 --- a/RxSwift/Observables/SwitchIfEmpty.swift +++ b/RxSwift/Observables/SwitchIfEmpty.swift @@ -20,7 +20,7 @@ extension ObservableType { } } -final private class SwitchIfEmpty: Producer { +final private class SwitchIfEmpty: Producer, @unchecked Sendable { private let source: Observable private let ifEmpty: Observable @@ -41,7 +41,8 @@ final private class SwitchIfEmpty: Producer { } final private class SwitchIfEmptySink: Sink - , ObserverType { + , ObserverType + , @unchecked Sendable { typealias Element = Observer.Element private let ifEmpty: Observable diff --git a/RxSwift/Observables/Take.swift b/RxSwift/Observables/Take.swift index acf3aaebd..784fd8973 100644 --- a/RxSwift/Observables/Take.swift +++ b/RxSwift/Observables/Take.swift @@ -62,7 +62,7 @@ extension ObservableType { // count version -final private class TakeCountSink: Sink, ObserverType { +final private class TakeCountSink: Sink, ObserverType, @unchecked Sendable { typealias Element = Observer.Element typealias Parent = TakeCount @@ -101,7 +101,7 @@ final private class TakeCountSink: Sink, Obser } -final private class TakeCount: Producer { +final private class TakeCount: Producer, @unchecked Sendable { private let source: Observable fileprivate let count: Int @@ -126,7 +126,8 @@ final private class TakeTimeSink : Sink , LockOwnerType , ObserverType - , SynchronizedOnType where Observer.Element == Element { + , SynchronizedOnType + , @unchecked Sendable where Observer.Element == Element { typealias Parent = TakeTime private let parent: Parent @@ -174,7 +175,7 @@ final private class TakeTimeSink } } -final private class TakeTime: Producer { +final private class TakeTime: Producer, @unchecked Sendable { typealias TimeInterval = RxTimeInterval fileprivate let source: Observable diff --git a/RxSwift/Observables/TakeLast.swift b/RxSwift/Observables/TakeLast.swift index 91833872d..19dfc5d37 100644 --- a/RxSwift/Observables/TakeLast.swift +++ b/RxSwift/Observables/TakeLast.swift @@ -24,7 +24,7 @@ extension ObservableType { } } -final private class TakeLastSink: Sink, ObserverType { +final private class TakeLastSink: Sink, ObserverType, @unchecked Sendable { typealias Element = Observer.Element typealias Parent = TakeLast @@ -58,7 +58,7 @@ final private class TakeLastSink: Sink, Observ } } -final private class TakeLast: Producer { +final private class TakeLast: Producer, @unchecked Sendable { private let source: Observable fileprivate let count: Int diff --git a/RxSwift/Observables/TakeWithPredicate.swift b/RxSwift/Observables/TakeWithPredicate.swift index 6c28d19ba..bcf294ca8 100644 --- a/RxSwift/Observables/TakeWithPredicate.swift +++ b/RxSwift/Observables/TakeWithPredicate.swift @@ -30,7 +30,7 @@ extension ObservableType { - returns: An observable sequence that contains the elements from the input sequence that occur before the element at which the test passes. */ - public func take(until predicate: @escaping (Element) throws -> Bool, + public func take(until predicate: @escaping @Sendable (Element) throws -> Bool, behavior: TakeBehavior = .exclusive) -> Observable { TakeUntilPredicate(source: self.asObservable(), @@ -46,7 +46,7 @@ extension ObservableType { - parameter predicate: A function to test each element for a condition. - returns: An observable sequence that contains the elements from the input sequence that occur before the element at which the test no longer passes. */ - public func take(while predicate: @escaping (Element) throws -> Bool, + public func take(while predicate: @escaping @Sendable (Element) throws -> Bool, behavior: TakeBehavior = .exclusive) -> Observable { take(until: { try !predicate($0) }, behavior: behavior) @@ -77,7 +77,7 @@ extension ObservableType { */ @available(*, deprecated, renamed: "take(until:behavior:)") public func takeUntil(_ behavior: TakeBehavior, - predicate: @escaping (Element) throws -> Bool) + predicate: @escaping @Sendable (Element) throws -> Bool) -> Observable { take(until: predicate, behavior: behavior) } @@ -91,7 +91,7 @@ extension ObservableType { - returns: An observable sequence that contains the elements from the input sequence that occur before the element at which the test no longer passes. */ @available(*, deprecated, renamed: "take(while:)") - public func takeWhile(_ predicate: @escaping (Element) throws -> Bool) + public func takeWhile(_ predicate: @escaping @Sendable (Element) throws -> Bool) -> Observable { take(until: { try !predicate($0) }, behavior: .exclusive) } @@ -157,8 +157,9 @@ final private class TakeUntilSink : Sink , LockOwnerType , ObserverType - , SynchronizedOnType { - typealias Element = Observer.Element + , SynchronizedOnType + , @unchecked Sendable { + typealias Element = Observer.Element typealias Parent = TakeUntil private let parent: Parent @@ -198,7 +199,7 @@ final private class TakeUntilSink } } -final private class TakeUntil: Producer { +final private class TakeUntil: Producer, @unchecked Sendable { fileprivate let source: Observable fileprivate let other: Observable @@ -217,7 +218,7 @@ final private class TakeUntil: Producer { // MARK: - TakeUntil Predicate final private class TakeUntilPredicateSink - : Sink, ObserverType { + : Sink, ObserverType, @unchecked Sendable { typealias Element = Observer.Element typealias Parent = TakeUntilPredicate @@ -262,8 +263,8 @@ final private class TakeUntilPredicateSink } -final private class TakeUntilPredicate: Producer { - typealias Predicate = (Element) throws -> Bool +final private class TakeUntilPredicate: Producer, @unchecked Sendable { + typealias Predicate = @Sendable (Element) throws -> Bool private let source: Observable fileprivate let predicate: Predicate diff --git a/RxSwift/Observables/Throttle.swift b/RxSwift/Observables/Throttle.swift index 86152a837..fe9d6bc19 100644 --- a/RxSwift/Observables/Throttle.swift +++ b/RxSwift/Observables/Throttle.swift @@ -32,8 +32,9 @@ final private class ThrottleSink : Sink , ObserverType , LockOwnerType - , SynchronizedOnType { - typealias Element = Observer.Element + , SynchronizedOnType + , @unchecked Sendable { + typealias Element = Observer.Element typealias ParentType = Throttle private let parent: ParentType @@ -122,6 +123,7 @@ final private class ThrottleSink self.lastSentTime = self.parent.scheduler.now } + @Sendable func propagate(_: Int) -> Disposable { self.lock.performLocked { if let lastUnsentElement = self.lastUnsentElement { @@ -138,7 +140,7 @@ final private class ThrottleSink } } -final private class Throttle: Producer { +final private class Throttle: Producer, @unchecked Sendable { fileprivate let source: Observable fileprivate let dueTime: RxTimeInterval fileprivate let latest: Bool diff --git a/RxSwift/Observables/Timeout.swift b/RxSwift/Observables/Timeout.swift index 7531eeeae..8d5f0f436 100644 --- a/RxSwift/Observables/Timeout.swift +++ b/RxSwift/Observables/Timeout.swift @@ -40,7 +40,7 @@ extension ObservableType { } } -final private class TimeoutSink: Sink, LockOwnerType, ObserverType { +final private class TimeoutSink: Sink, LockOwnerType, ObserverType, @unchecked Sendable { typealias Element = Observer.Element typealias Parent = Timeout @@ -132,7 +132,7 @@ final private class TimeoutSink: Sink, LockOwn } -final private class Timeout: Producer { +final private class Timeout: Producer, @unchecked Sendable { fileprivate let source: Observable fileprivate let dueTime: RxTimeInterval fileprivate let other: Observable diff --git a/RxSwift/Observables/Timer.swift b/RxSwift/Observables/Timer.swift index 30bbdf67d..4ebd051c5 100644 --- a/RxSwift/Observables/Timer.swift +++ b/RxSwift/Observables/Timer.swift @@ -49,7 +49,7 @@ extension ObservableType where Element: RxAbstractInteger { import Foundation -final private class TimerSink : Sink where Observer.Element : RxAbstractInteger { +final private class TimerSink : Sink, @unchecked Sendable where Observer.Element : RxAbstractInteger { typealias Parent = Timer private let parent: Parent @@ -70,7 +70,7 @@ final private class TimerSink : Sink where Obs } } -final private class TimerOneOffSink: Sink where Observer.Element: RxAbstractInteger { +final private class TimerOneOffSink: Sink, @unchecked Sendable where Observer.Element: RxAbstractInteger { typealias Parent = Timer private let parent: Parent @@ -91,7 +91,7 @@ final private class TimerOneOffSink: Sink wher } } -final private class Timer: Producer { +final private class Timer: Producer, @unchecked Sendable { fileprivate let scheduler: SchedulerType fileprivate let dueTime: RxTimeInterval fileprivate let period: RxTimeInterval? diff --git a/RxSwift/Observables/ToArray.swift b/RxSwift/Observables/ToArray.swift index 76c7370aa..0a2ba0011 100644 --- a/RxSwift/Observables/ToArray.swift +++ b/RxSwift/Observables/ToArray.swift @@ -22,7 +22,7 @@ extension ObservableType { } } -final private class ToArraySink: Sink, ObserverType where Observer.Element == [SourceType] { +final private class ToArraySink: Sink, ObserverType, @unchecked Sendable where Observer.Element == [SourceType] { typealias Parent = ToArray let parent: Parent @@ -49,7 +49,7 @@ final private class ToArraySink: Sink: Producer<[SourceType]> { +final private class ToArray: Producer<[SourceType]>, @unchecked Sendable { let source: Observable init(source: Observable) { diff --git a/RxSwift/Observables/Using.swift b/RxSwift/Observables/Using.swift index ca48d2fb8..8ec59fe17 100644 --- a/RxSwift/Observables/Using.swift +++ b/RxSwift/Observables/Using.swift @@ -16,12 +16,12 @@ extension ObservableType { - parameter observableFactory: Factory function to obtain an observable sequence that depends on the obtained resource. - returns: An observable sequence whose lifetime controls the lifetime of the dependent resource object. */ - public static func using(_ resourceFactory: @escaping () throws -> Resource, observableFactory: @escaping (Resource) throws -> Observable) -> Observable { + public static func using(_ resourceFactory: @escaping @Sendable () throws -> Resource, observableFactory: @escaping @Sendable (Resource) throws -> Observable) -> Observable { Using(resourceFactory: resourceFactory, observableFactory: observableFactory) } } -final private class UsingSink: Sink, ObserverType { +final private class UsingSink: Sink, ObserverType, @unchecked Sendable { typealias SourceType = Observer.Element typealias Parent = Using @@ -66,12 +66,12 @@ final private class UsingSink: } } -final private class Using: Producer { +final private class Using: Producer, @unchecked Sendable { typealias Element = SourceType - typealias ResourceFactory = () throws -> ResourceType - typealias ObservableFactory = (ResourceType) throws -> Observable + typealias ResourceFactory = @Sendable () throws -> ResourceType + typealias ObservableFactory = @Sendable (ResourceType) throws -> Observable fileprivate let resourceFactory: ResourceFactory fileprivate let observableFactory: ObservableFactory diff --git a/RxSwift/Observables/Window.swift b/RxSwift/Observables/Window.swift index c52dd3f49..d1092e121 100644 --- a/RxSwift/Observables/Window.swift +++ b/RxSwift/Observables/Window.swift @@ -30,7 +30,8 @@ final private class WindowTimeCountSink : Sink , ObserverType , LockOwnerType - , SynchronizedOnType where Observer.Element == Observable { + , SynchronizedOnType + , @unchecked Sendable where Observer.Element == Observable { typealias Parent = WindowTimeCount private let parent: Parent @@ -149,7 +150,7 @@ final private class WindowTimeCountSink } } -final private class WindowTimeCount: Producer> { +final private class WindowTimeCount: Producer>, @unchecked Sendable { fileprivate let timeSpan: RxTimeInterval fileprivate let count: Int fileprivate let scheduler: SchedulerType diff --git a/RxSwift/Observables/WithLatestFrom.swift b/RxSwift/Observables/WithLatestFrom.swift index d2868121a..358492507 100644 --- a/RxSwift/Observables/WithLatestFrom.swift +++ b/RxSwift/Observables/WithLatestFrom.swift @@ -18,7 +18,7 @@ extension ObservableType { - parameter resultSelector: Function to invoke for each element from the self combined with the latest element from the second source, if any. - returns: An observable sequence containing the result of combining each element of the self with the latest element from the second source, if any, using the specified result selector function. */ - public func withLatestFrom(_ second: Source, resultSelector: @escaping (Element, Source.Element) throws -> ResultType) -> Observable { + public func withLatestFrom(_ second: Source, resultSelector: @escaping @Sendable (Element, Source.Element) throws -> ResultType) -> Observable { WithLatestFrom(first: self.asObservable(), second: second.asObservable(), resultSelector: resultSelector) } @@ -40,7 +40,8 @@ final private class WithLatestFromSink , ObserverType , LockOwnerType - , SynchronizedOnType { + , SynchronizedOnType + , @unchecked Sendable { typealias ResultType = Observer.Element typealias Parent = WithLatestFrom typealias Element = FirstType @@ -130,8 +131,8 @@ final private class WithLatestFromSecond: Producer { - typealias ResultSelector = (FirstType, SecondType) throws -> ResultType +final private class WithLatestFrom: Producer, @unchecked Sendable { + typealias ResultSelector = @Sendable (FirstType, SecondType) throws -> ResultType fileprivate let first: Observable fileprivate let second: Observable diff --git a/RxSwift/Observables/WithUnretained.swift b/RxSwift/Observables/WithUnretained.swift index 8466e89c5..2201ad635 100644 --- a/RxSwift/Observables/WithUnretained.swift +++ b/RxSwift/Observables/WithUnretained.swift @@ -20,7 +20,7 @@ extension ObservableType { */ public func withUnretained( _ obj: Object, - resultSelector: @escaping (Object, Element) -> Out + resultSelector: @escaping @Sendable (Object, Element) -> Out ) -> Observable { map { [weak obj] element -> Out in guard let obj = obj else { throw UnretainedError.failedRetaining } diff --git a/RxSwift/Observables/Zip+Collection.swift b/RxSwift/Observables/Zip+Collection.swift index 2d7f8877c..719d948cb 100644 --- a/RxSwift/Observables/Zip+Collection.swift +++ b/RxSwift/Observables/Zip+Collection.swift @@ -15,7 +15,7 @@ extension ObservableType { - parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources. - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ - public static func zip(_ collection: Collection, resultSelector: @escaping ([Collection.Element.Element]) throws -> Element) -> Observable + public static func zip(_ collection: Collection, resultSelector: @escaping @Sendable ([Collection.Element.Element]) throws -> Element) -> Observable where Collection.Element: ObservableType { ZipCollectionType(sources: collection, resultSelector: resultSelector) } @@ -35,8 +35,9 @@ extension ObservableType { } final private class ZipCollectionTypeSink - : Sink where Collection.Element: ObservableConvertibleType { - typealias Result = Observer.Element + : Sink + , @unchecked Sendable where Collection.Element: ObservableConvertibleType { + typealias Result = Observer.Element typealias Parent = ZipCollectionType typealias SourceElement = Collection.Element.Element @@ -147,8 +148,8 @@ final private class ZipCollectionTypeSink: Producer where Collection.Element: ObservableConvertibleType { - typealias ResultSelector = ([Collection.Element.Element]) throws -> Result +final private class ZipCollectionType: Producer, @unchecked Sendable where Collection.Element: ObservableConvertibleType { + typealias ResultSelector = @Sendable ([Collection.Element.Element]) throws -> Result let sources: Collection let resultSelector: ResultSelector diff --git a/RxSwift/Observables/Zip+arity.swift b/RxSwift/Observables/Zip+arity.swift index b6d876d1d..941e560e1 100644 --- a/RxSwift/Observables/Zip+arity.swift +++ b/RxSwift/Observables/Zip+arity.swift @@ -21,7 +21,7 @@ extension ObservableType { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip - (_ source1: O1, _ source2: O2, resultSelector: @escaping (O1.Element, O2.Element) throws -> Element) + (_ source1: O1, _ source2: O2, resultSelector: @escaping @Sendable (O1.Element, O2.Element) throws -> Element) -> Observable { return Zip2( source1: source1.asObservable(), source2: source2.asObservable(), @@ -48,8 +48,8 @@ extension ObservableType where Element == Any { } } -final class ZipSink2_ : ZipSink { - typealias Result = Observer.Element +final class ZipSink2_ : ZipSink, @unchecked Sendable { + typealias Result = Observer.Element typealias Parent = Zip2 let parent: Parent @@ -93,8 +93,8 @@ final class ZipSink2_ : ZipSink { } } -final class Zip2 : Producer { - typealias ResultSelector = (E1, E2) throws -> Result +final class Zip2 : Producer, @unchecked Sendable { + typealias ResultSelector = @Sendable (E1, E2) throws -> Result let source1: Observable let source2: Observable @@ -129,7 +129,7 @@ extension ObservableType { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip - (_ source1: O1, _ source2: O2, _ source3: O3, resultSelector: @escaping (O1.Element, O2.Element, O3.Element) throws -> Element) + (_ source1: O1, _ source2: O2, _ source3: O3, resultSelector: @escaping @Sendable (O1.Element, O2.Element, O3.Element) throws -> Element) -> Observable { return Zip3( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), @@ -156,8 +156,8 @@ extension ObservableType where Element == Any { } } -final class ZipSink3_ : ZipSink { - typealias Result = Observer.Element +final class ZipSink3_ : ZipSink, @unchecked Sendable { + typealias Result = Observer.Element typealias Parent = Zip3 let parent: Parent @@ -207,8 +207,8 @@ final class ZipSink3_ : ZipSink { } } -final class Zip3 : Producer { - typealias ResultSelector = (E1, E2, E3) throws -> Result +final class Zip3 : Producer, @unchecked Sendable { + typealias ResultSelector = @Sendable (E1, E2, E3) throws -> Result let source1: Observable let source2: Observable @@ -245,7 +245,7 @@ extension ObservableType { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip - (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element) throws -> Element) + (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: @escaping @Sendable (O1.Element, O2.Element, O3.Element, O4.Element) throws -> Element) -> Observable { return Zip4( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), @@ -272,7 +272,7 @@ extension ObservableType where Element == Any { } } -final class ZipSink4_ : ZipSink { +final class ZipSink4_ : ZipSink, @unchecked Sendable { typealias Result = Observer.Element typealias Parent = Zip4 @@ -329,8 +329,8 @@ final class ZipSink4_ : ZipSink : Producer { - typealias ResultSelector = (E1, E2, E3, E4) throws -> Result +final class Zip4 : Producer, @unchecked Sendable { + typealias ResultSelector = @Sendable (E1, E2, E3, E4) throws -> Result let source1: Observable let source2: Observable @@ -369,7 +369,7 @@ extension ObservableType { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip - (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element) throws -> Element) + (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: @escaping @Sendable (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element) throws -> Element) -> Observable { return Zip5( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), @@ -396,8 +396,8 @@ extension ObservableType where Element == Any { } } -final class ZipSink5_ : ZipSink { - typealias Result = Observer.Element +final class ZipSink5_ : ZipSink, @unchecked Sendable { + typealias Result = Observer.Element typealias Parent = Zip5 let parent: Parent @@ -459,8 +459,8 @@ final class ZipSink5_ : ZipSink : Producer { - typealias ResultSelector = (E1, E2, E3, E4, E5) throws -> Result +final class Zip5 : Producer, @unchecked Sendable { + typealias ResultSelector = @Sendable (E1, E2, E3, E4, E5) throws -> Result let source1: Observable let source2: Observable @@ -501,7 +501,7 @@ extension ObservableType { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip - (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element) throws -> Element) + (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: @escaping @Sendable (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element) throws -> Element) -> Observable { return Zip6( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), @@ -528,8 +528,8 @@ extension ObservableType where Element == Any { } } -final class ZipSink6_ : ZipSink { - typealias Result = Observer.Element +final class ZipSink6_ : ZipSink, @unchecked Sendable { + typealias Result = Observer.Element typealias Parent = Zip6 let parent: Parent @@ -597,8 +597,8 @@ final class ZipSink6_ : ZipSink< } } -final class Zip6 : Producer { - typealias ResultSelector = (E1, E2, E3, E4, E5, E6) throws -> Result +final class Zip6 : Producer, @unchecked Sendable { + typealias ResultSelector = @Sendable (E1, E2, E3, E4, E5, E6) throws -> Result let source1: Observable let source2: Observable @@ -641,7 +641,7 @@ extension ObservableType { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip - (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element) throws -> Element) + (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: @escaping @Sendable (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element) throws -> Element) -> Observable { return Zip7( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), source7: source7.asObservable(), @@ -668,8 +668,8 @@ extension ObservableType where Element == Any { } } -final class ZipSink7_ : ZipSink { - typealias Result = Observer.Element +final class ZipSink7_ : ZipSink, @unchecked Sendable { + typealias Result = Observer.Element typealias Parent = Zip7 let parent: Parent @@ -743,8 +743,8 @@ final class ZipSink7_ : ZipS } } -final class Zip7 : Producer { - typealias ResultSelector = (E1, E2, E3, E4, E5, E6, E7) throws -> Result +final class Zip7 : Producer, @unchecked Sendable { + typealias ResultSelector = @Sendable (E1, E2, E3, E4, E5, E6, E7) throws -> Result let source1: Observable let source2: Observable @@ -789,7 +789,7 @@ extension ObservableType { - returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function. */ public static func zip - (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: @escaping (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element, O8.Element) throws -> Element) + (_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: @escaping @Sendable (O1.Element, O2.Element, O3.Element, O4.Element, O5.Element, O6.Element, O7.Element, O8.Element) throws -> Element) -> Observable { return Zip8( source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), source7: source7.asObservable(), source8: source8.asObservable(), @@ -816,7 +816,7 @@ extension ObservableType where Element == Any { } } -final class ZipSink8_ : ZipSink { +final class ZipSink8_ : ZipSink, @unchecked Sendable { typealias Result = Observer.Element typealias Parent = Zip8 @@ -897,8 +897,8 @@ final class ZipSink8_ : } } -final class Zip8 : Producer { - typealias ResultSelector = (E1, E2, E3, E4, E5, E6, E7, E8) throws -> Result +final class Zip8 : Producer, @unchecked Sendable { + typealias ResultSelector = @Sendable (E1, E2, E3, E4, E5, E6, E7, E8) throws -> Result let source1: Observable let source2: Observable diff --git a/RxSwift/Observables/Zip.swift b/RxSwift/Observables/Zip.swift index 328a40ef7..300e4fc42 100644 --- a/RxSwift/Observables/Zip.swift +++ b/RxSwift/Observables/Zip.swift @@ -12,7 +12,7 @@ protocol ZipSinkProtocol: AnyObject { func done(_ index: Int) } -class ZipSink : Sink, ZipSinkProtocol { +class ZipSink : Sink, ZipSinkProtocol, @unchecked Sendable { typealias Element = Observer.Element let arity: Int @@ -84,8 +84,9 @@ class ZipSink : Sink, ZipSinkProtocol { final class ZipObserver : ObserverType , LockOwnerType - , SynchronizedOnType { - typealias ValueSetter = (Element) -> Void + , SynchronizedOnType + , @unchecked Sendable { + typealias ValueSetter = @Sendable (Element) -> Void private var parent: ZipSinkProtocol? diff --git a/RxSwift/ObserverType.swift b/RxSwift/ObserverType.swift index b7a66480e..e6817dc2f 100644 --- a/RxSwift/ObserverType.swift +++ b/RxSwift/ObserverType.swift @@ -7,13 +7,14 @@ // /// Supports push-style iteration over an observable sequence. -public protocol ObserverType { +public protocol ObserverType: Sendable { /// The type of elements in sequence that observer can observe. associatedtype Element /// Notify observer about sequence event. /// /// - parameter event: Event that occurred. + @Sendable func on(_ event: Event) } diff --git a/RxSwift/Observers/AnonymousObserver.swift b/RxSwift/Observers/AnonymousObserver.swift index cbb42dc82..4851207b7 100644 --- a/RxSwift/Observers/AnonymousObserver.swift +++ b/RxSwift/Observers/AnonymousObserver.swift @@ -6,8 +6,8 @@ // Copyright © 2015 Krunoslav Zaher. All rights reserved. // -final class AnonymousObserver: ObserverBase { - typealias EventHandler = (Event) -> Void +final class AnonymousObserver: ObserverBase, @unchecked Sendable { + typealias EventHandler = @Sendable (Event) -> Void private let eventHandler : EventHandler diff --git a/RxSwift/Observers/ObserverBase.swift b/RxSwift/Observers/ObserverBase.swift index 48e09868b..625f2e859 100644 --- a/RxSwift/Observers/ObserverBase.swift +++ b/RxSwift/Observers/ObserverBase.swift @@ -6,7 +6,7 @@ // Copyright © 2015 Krunoslav Zaher. All rights reserved. // -class ObserverBase : Disposable, ObserverType { +class ObserverBase : Disposable, ObserverType, @unchecked Sendable { private let isStopped = AtomicInt(0) func on(_ event: Event) { diff --git a/RxSwift/Observers/TailRecursiveSink.swift b/RxSwift/Observers/TailRecursiveSink.swift index fd527001d..7f01ffcf2 100644 --- a/RxSwift/Observers/TailRecursiveSink.swift +++ b/RxSwift/Observers/TailRecursiveSink.swift @@ -18,7 +18,8 @@ enum TailRecursiveSinkCommand { /// This class is usually used with `Generator` version of the operators. class TailRecursiveSink : Sink - , InvocableWithValueType where Sequence.Element: ObservableConvertibleType, Sequence.Element.Element == Observer.Element { + , InvocableWithValueType + , @unchecked Sendable where Sequence.Element: ObservableConvertibleType, Sequence.Element.Element == Observer.Element { typealias Value = TailRecursiveSinkCommand typealias Element = Observer.Element typealias SequenceGenerator = (generator: Sequence.Iterator, remaining: IntMax?) diff --git a/RxSwift/SchedulerType.swift b/RxSwift/SchedulerType.swift index 6c8fe4a38..478b62bff 100644 --- a/RxSwift/SchedulerType.swift +++ b/RxSwift/SchedulerType.swift @@ -31,7 +31,7 @@ public protocol SchedulerType: ImmediateSchedulerType { - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ - func scheduleRelative(_ state: StateType, dueTime: RxTimeInterval, action: @escaping (StateType) -> Disposable) -> Disposable + func scheduleRelative(_ state: StateType, dueTime: RxTimeInterval, action: @escaping @Sendable (StateType) -> Disposable) -> Disposable /** Schedules a periodic piece of work. @@ -42,7 +42,7 @@ public protocol SchedulerType: ImmediateSchedulerType { - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ - func schedulePeriodic(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping (StateType) -> StateType) -> Disposable + func schedulePeriodic(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping @Sendable (StateType) -> StateType) -> Disposable } extension SchedulerType { @@ -55,13 +55,13 @@ extension SchedulerType { - parameter period: Period for running the work periodically. - returns: The disposable object used to cancel the scheduled recurring action (best effort). */ - public func schedulePeriodic(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping (StateType) -> StateType) -> Disposable { + public func schedulePeriodic(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping @Sendable (StateType) -> StateType) -> Disposable { let schedule = SchedulePeriodicRecursive(scheduler: self, startAfter: startAfter, period: period, action: action, state: state) return schedule.start() } - func scheduleRecursive(_ state: State, dueTime: RxTimeInterval, action: @escaping (State, AnyRecursiveScheduler) -> Void) -> Disposable { + func scheduleRecursive(_ state: State, dueTime: RxTimeInterval, action: @escaping @Sendable (State, AnyRecursiveScheduler) -> Void) -> Disposable { let scheduler = AnyRecursiveScheduler(scheduler: self, action: action) scheduler.schedule(state, dueTime: dueTime) diff --git a/RxSwift/Schedulers/ConcurrentDispatchQueueScheduler.swift b/RxSwift/Schedulers/ConcurrentDispatchQueueScheduler.swift index 02c47c917..51fc07432 100644 --- a/RxSwift/Schedulers/ConcurrentDispatchQueueScheduler.swift +++ b/RxSwift/Schedulers/ConcurrentDispatchQueueScheduler.swift @@ -12,7 +12,7 @@ import Foundation /// Abstracts the work that needs to be performed on a specific `dispatch_queue_t`. You can also pass a serial dispatch queue, it shouldn't cause any problems. /// /// This scheduler is suitable when some work needs to be performed in background. -public class ConcurrentDispatchQueueScheduler: SchedulerType { +public class ConcurrentDispatchQueueScheduler: SchedulerType, @unchecked Sendable { public typealias TimeInterval = Foundation.TimeInterval public typealias Time = Date @@ -51,7 +51,7 @@ public class ConcurrentDispatchQueueScheduler: SchedulerType { - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ - public final func schedule(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable { + public final func schedule(_ state: StateType, action: @escaping @Sendable (StateType) -> Disposable) -> Disposable { self.configuration.schedule(state, action: action) } @@ -63,7 +63,7 @@ public class ConcurrentDispatchQueueScheduler: SchedulerType { - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ - public final func scheduleRelative(_ state: StateType, dueTime: RxTimeInterval, action: @escaping (StateType) -> Disposable) -> Disposable { + public final func scheduleRelative(_ state: StateType, dueTime: RxTimeInterval, action: @escaping @Sendable (StateType) -> Disposable) -> Disposable { self.configuration.scheduleRelative(state, dueTime: dueTime, action: action) } @@ -76,7 +76,7 @@ public class ConcurrentDispatchQueueScheduler: SchedulerType { - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ - public func schedulePeriodic(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping (StateType) -> StateType) -> Disposable { + public func schedulePeriodic(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping @Sendable (StateType) -> StateType) -> Disposable { self.configuration.schedulePeriodic(state, startAfter: startAfter, period: period, action: action) } } diff --git a/RxSwift/Schedulers/ConcurrentMainScheduler.swift b/RxSwift/Schedulers/ConcurrentMainScheduler.swift index c438d7d4d..8d1e5a11b 100644 --- a/RxSwift/Schedulers/ConcurrentMainScheduler.swift +++ b/RxSwift/Schedulers/ConcurrentMainScheduler.swift @@ -42,14 +42,14 @@ public final class ConcurrentMainScheduler : SchedulerType { - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ - public func schedule(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable { + public func schedule(_ state: StateType, action: @escaping @Sendable (StateType) -> Disposable) -> Disposable { if DispatchQueue.isMain { return action(state) } let cancel = SingleAssignmentDisposable() - self.mainQueue.async { + self.mainQueue.async { @Sendable () in if cancel.isDisposed { return } @@ -68,7 +68,7 @@ public final class ConcurrentMainScheduler : SchedulerType { - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ - public final func scheduleRelative(_ state: StateType, dueTime: RxTimeInterval, action: @escaping (StateType) -> Disposable) -> Disposable { + public final func scheduleRelative(_ state: StateType, dueTime: RxTimeInterval, action: @escaping @Sendable (StateType) -> Disposable) -> Disposable { self.mainScheduler.scheduleRelative(state, dueTime: dueTime, action: action) } @@ -81,7 +81,7 @@ public final class ConcurrentMainScheduler : SchedulerType { - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ - public func schedulePeriodic(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping (StateType) -> StateType) -> Disposable { + public func schedulePeriodic(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping @Sendable (StateType) -> StateType) -> Disposable { self.mainScheduler.schedulePeriodic(state, startAfter: startAfter, period: period, action: action) } } diff --git a/RxSwift/Schedulers/CurrentThreadScheduler.swift b/RxSwift/Schedulers/CurrentThreadScheduler.swift index 16a7dd328..d80dc9725 100644 --- a/RxSwift/Schedulers/CurrentThreadScheduler.swift +++ b/RxSwift/Schedulers/CurrentThreadScheduler.swift @@ -35,7 +35,7 @@ import Foundation /// This is the default scheduler for operators that generate elements. /// /// This scheduler is also sometimes called `trampoline scheduler`. -public class CurrentThreadScheduler : ImmediateSchedulerType { +public class CurrentThreadScheduler : ImmediateSchedulerType, @unchecked Sendable { typealias ScheduleQueue = RxMutableBox> /// The singleton instance of the current thread scheduler. @@ -87,7 +87,7 @@ public class CurrentThreadScheduler : ImmediateSchedulerType { - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ - public func schedule(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable { + public func schedule(_ state: StateType, action: @escaping @Sendable (StateType) -> Disposable) -> Disposable { if CurrentThreadScheduler.isScheduleRequired { CurrentThreadScheduler.isScheduleRequired = false diff --git a/RxSwift/Schedulers/HistoricalScheduler.swift b/RxSwift/Schedulers/HistoricalScheduler.swift index 254a3e83c..204b2f3a2 100644 --- a/RxSwift/Schedulers/HistoricalScheduler.swift +++ b/RxSwift/Schedulers/HistoricalScheduler.swift @@ -9,7 +9,7 @@ import Foundation /// Provides a virtual time scheduler that uses `Date` for absolute time and `TimeInterval` for relative time. -public class HistoricalScheduler : VirtualTimeScheduler { +public class HistoricalScheduler : VirtualTimeScheduler, @unchecked Sendable { /** Creates a new historical scheduler with initial clock value. diff --git a/RxSwift/Schedulers/Internal/DispatchQueueConfiguration.swift b/RxSwift/Schedulers/Internal/DispatchQueueConfiguration.swift index 53f148dc4..779257a73 100644 --- a/RxSwift/Schedulers/Internal/DispatchQueueConfiguration.swift +++ b/RxSwift/Schedulers/Internal/DispatchQueueConfiguration.swift @@ -15,10 +15,10 @@ struct DispatchQueueConfiguration { } extension DispatchQueueConfiguration { - func schedule(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable { + func schedule(_ state: StateType, action: @escaping @Sendable (StateType) -> Disposable) -> Disposable { let cancel = SingleAssignmentDisposable() - self.queue.async { + self.queue.async { @Sendable () in if cancel.isDisposed { return } @@ -30,7 +30,7 @@ extension DispatchQueueConfiguration { return cancel } - func scheduleRelative(_ state: StateType, dueTime: RxTimeInterval, action: @escaping (StateType) -> Disposable) -> Disposable { + func scheduleRelative(_ state: StateType, dueTime: RxTimeInterval, action: @escaping @Sendable (StateType) -> Disposable) -> Disposable { let deadline = DispatchTime.now() + dueTime let compositeDisposable = CompositeDisposable() @@ -44,13 +44,13 @@ extension DispatchQueueConfiguration { // Need more info on this. // It looks like just setting timer to fire and not holding a reference to it // until deadline causes timer cancellation. - var timerReference: DispatchSourceTimer? = timer + nonisolated(unsafe) var timerReference: DispatchSourceTimer? = timer let cancelTimer = Disposables.create { timerReference?.cancel() timerReference = nil } - timer.setEventHandler(handler: { + timer.setEventHandler(handler: { @Sendable () in if compositeDisposable.isDisposed { return } @@ -64,10 +64,10 @@ extension DispatchQueueConfiguration { return compositeDisposable } - func schedulePeriodic(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping (StateType) -> StateType) -> Disposable { + func schedulePeriodic(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping @Sendable (StateType) -> StateType) -> Disposable { let initial = DispatchTime.now() + startAfter - var timerState = state + nonisolated(unsafe) var timerState = state let timer = DispatchSource.makeTimerSource(queue: self.queue) timer.schedule(deadline: initial, repeating: period, leeway: self.leeway) @@ -78,13 +78,13 @@ extension DispatchQueueConfiguration { // Need more info on this. // It looks like just setting timer to fire and not holding a reference to it // until deadline causes timer cancellation. - var timerReference: DispatchSourceTimer? = timer + nonisolated(unsafe) var timerReference: DispatchSourceTimer? = timer let cancelTimer = Disposables.create { timerReference?.cancel() timerReference = nil } - timer.setEventHandler(handler: { + timer.setEventHandler(handler: { @Sendable () in if cancelTimer.isDisposed { return } diff --git a/RxSwift/Schedulers/Internal/ScheduledItem.swift b/RxSwift/Schedulers/Internal/ScheduledItem.swift index 3d790d7af..f4724bf27 100644 --- a/RxSwift/Schedulers/Internal/ScheduledItem.swift +++ b/RxSwift/Schedulers/Internal/ScheduledItem.swift @@ -8,8 +8,9 @@ struct ScheduledItem : ScheduledItemType - , InvocableType { - typealias Action = (T) -> Disposable + , InvocableType + , @unchecked Sendable { + typealias Action = @Sendable (T) -> Disposable private let action: Action private let state: T diff --git a/RxSwift/Schedulers/MainScheduler.swift b/RxSwift/Schedulers/MainScheduler.swift index f6a507f3f..4ac402409 100644 --- a/RxSwift/Schedulers/MainScheduler.swift +++ b/RxSwift/Schedulers/MainScheduler.swift @@ -21,7 +21,7 @@ Main scheduler is a specialization of `SerialDispatchQueueScheduler`. This scheduler is optimized for `observeOn` operator. To ensure observable sequence is subscribed on main thread using `subscribeOn` operator please use `ConcurrentMainScheduler` because it is more optimized for that purpose. */ -public final class MainScheduler : SerialDispatchQueueScheduler { +public final class MainScheduler : SerialDispatchQueueScheduler, @unchecked Sendable { private let mainQueue: DispatchQueue @@ -56,7 +56,7 @@ public final class MainScheduler : SerialDispatchQueueScheduler { #endif } - override func scheduleInternal(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable { + override func scheduleInternal(_ state: StateType, action: @escaping @Sendable (StateType) -> Disposable) -> Disposable { let previousNumberEnqueued = increment(self.numberEnqueued) if DispatchQueue.isMain && previousNumberEnqueued == 0 { @@ -78,3 +78,42 @@ public final class MainScheduler : SerialDispatchQueueScheduler { return cancel } } + +extension MainScheduler { + // implementation copied from from assumeIsolated. This is copied because assumeIsolated has @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *). This implementation also remove the check that the closure is executed on the main actor. + @preconcurrency nonisolated + static private func _assumeMainActor(execute operation: @MainActor(unsafe) () throws -> T) rethrows -> T { + typealias YesActor = @MainActor () throws -> T + typealias NoActor = () throws -> T + + // To do the unsafe cast, we have to pretend it's @escaping. + return try withoutActuallyEscaping(operation) { (_ fn: @escaping YesActor) throws -> T in + let rawFn = unsafeBitCast(fn, to: NoActor.self) + return try rawFn() + } + } + + @preconcurrency nonisolated + static public func assumeMainActor(execute operation: @MainActor(unsafe) () throws -> T) rethrows -> T { + ensureRunningOnMainThread() + return try _assumeMainActor(execute: operation) + } + + public static func assumeMainActor(_ work: @escaping @Sendable @MainActor(assumed) () -> T) -> (@Sendable () -> T) { + return { @Sendable () -> T in + return MainScheduler.assumeMainActor(execute: { return work() }) + } + } + + public static func assumeMainActor(_ work: @escaping @Sendable @MainActor(assumed) (Value) -> T) -> (@Sendable (Value) -> T) { + return { @Sendable (value: Value) -> T in + return MainScheduler.assumeMainActor(execute: { return work(value) }) + } + } + + public static func assumeMainActor(_ work: @escaping @Sendable @MainActor(assumed) (Value1, Value2) -> T) -> (@Sendable (Value1, Value2) -> T) { + return { @Sendable (value1: Value1, value2: Value2) -> T in + return MainScheduler.assumeMainActor(execute: { return work(value1, value2) }) + } + } +} diff --git a/RxSwift/Schedulers/OperationQueueScheduler.swift b/RxSwift/Schedulers/OperationQueueScheduler.swift index f0ad3d3ce..c2984c929 100644 --- a/RxSwift/Schedulers/OperationQueueScheduler.swift +++ b/RxSwift/Schedulers/OperationQueueScheduler.swift @@ -12,7 +12,7 @@ import Foundation /// Abstracts the work that needs to be performed on a specific `NSOperationQueue`. /// /// This scheduler is suitable for cases when there is some bigger chunk of work that needs to be performed in background and you want to fine tune concurrent processing using `maxConcurrentOperationCount`. -public class OperationQueueScheduler: ImmediateSchedulerType { +public class OperationQueueScheduler: ImmediateSchedulerType, @unchecked Sendable { public let operationQueue: OperationQueue public let queuePriority: Operation.QueuePriority @@ -32,7 +32,7 @@ public class OperationQueueScheduler: ImmediateSchedulerType { - parameter action: Action to execute recursively. The last parameter passed to the action is used to trigger recursive scheduling of the action, passing in recursive invocation state. - returns: The disposable object used to cancel the scheduled action (best effort). */ - public func schedule(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable { + public func schedule(_ state: StateType, action: @escaping @Sendable (StateType) -> Disposable) -> Disposable { let cancel = SingleAssignmentDisposable() let operation = BlockOperation { diff --git a/RxSwift/Schedulers/RecursiveScheduler.swift b/RxSwift/Schedulers/RecursiveScheduler.swift index 0ee78cd18..a017fc4ef 100644 --- a/RxSwift/Schedulers/RecursiveScheduler.swift +++ b/RxSwift/Schedulers/RecursiveScheduler.swift @@ -37,7 +37,7 @@ final class AnyRecursiveScheduler { - parameter dueTime: Relative time after which to execute the recursive action. */ func schedule(_ state: State, dueTime: RxTimeInterval) { - var scheduleState: ScheduleState = .initial + nonisolated(unsafe) var scheduleState: ScheduleState = .initial let d = self.scheduler.scheduleRelative(state, dueTime: dueTime) { state -> Disposable in // best effort @@ -88,7 +88,7 @@ final class AnyRecursiveScheduler { /// /// - parameter state: State passed to the action to be executed. func schedule(_ state: State) { - var scheduleState: ScheduleState = .initial + nonisolated(unsafe) var scheduleState: ScheduleState = .initial let d = self.scheduler.schedule(state) { state -> Disposable in // best effort @@ -135,6 +135,7 @@ final class AnyRecursiveScheduler { } } + @Sendable func dispose() { self.lock.performLocked { self.action = nil @@ -144,7 +145,7 @@ final class AnyRecursiveScheduler { } /// Type erased recursive scheduler. -final class RecursiveImmediateScheduler { +final class RecursiveImmediateScheduler: @unchecked Sendable { typealias Action = (_ state: State, _ recurse: (State) -> Void) -> Void private var lock = SpinLock() @@ -164,7 +165,7 @@ final class RecursiveImmediateScheduler { /// /// - parameter state: State passed to the action to be executed. func schedule(_ state: State) { - var scheduleState: ScheduleState = .initial + nonisolated(unsafe) var scheduleState: ScheduleState = .initial let d = self.scheduler.schedule(state) { state -> Disposable in // best effort @@ -211,6 +212,7 @@ final class RecursiveImmediateScheduler { } } + @Sendable func dispose() { self.lock.performLocked { self.action = nil diff --git a/RxSwift/Schedulers/SchedulerServices+Emulation.swift b/RxSwift/Schedulers/SchedulerServices+Emulation.swift index cab2bc8b3..81ae5ad93 100644 --- a/RxSwift/Schedulers/SchedulerServices+Emulation.swift +++ b/RxSwift/Schedulers/SchedulerServices+Emulation.swift @@ -12,7 +12,7 @@ enum SchedulePeriodicRecursiveCommand { } final class SchedulePeriodicRecursive { - typealias RecursiveAction = (State) -> State + typealias RecursiveAction = @Sendable (State) -> State typealias RecursiveScheduler = AnyRecursiveScheduler private let scheduler: SchedulerType @@ -34,7 +34,8 @@ final class SchedulePeriodicRecursive { func start() -> Disposable { self.scheduler.scheduleRecursive(SchedulePeriodicRecursiveCommand.tick, dueTime: self.startAfter, action: self.tick) } - + + @Sendable func tick(_ command: SchedulePeriodicRecursiveCommand, scheduler: RecursiveScheduler) { // Tries to emulate periodic scheduling as best as possible. // The problem that could arise is if handling periodic ticks take too long, or diff --git a/RxSwift/Schedulers/SerialDispatchQueueScheduler.swift b/RxSwift/Schedulers/SerialDispatchQueueScheduler.swift index bb0013c8f..65225d6e0 100644 --- a/RxSwift/Schedulers/SerialDispatchQueueScheduler.swift +++ b/RxSwift/Schedulers/SerialDispatchQueueScheduler.swift @@ -26,7 +26,7 @@ In case some customization need to be made on it before usage, internal serial queue can be customized using `serialQueueConfiguration` callback. */ -public class SerialDispatchQueueScheduler : SchedulerType { +public class SerialDispatchQueueScheduler : SchedulerType, @unchecked Sendable { public typealias TimeInterval = Foundation.TimeInterval public typealias Time = Date @@ -96,11 +96,11 @@ public class SerialDispatchQueueScheduler : SchedulerType { - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ - public final func schedule(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable { + public final func schedule(_ state: StateType, action: @escaping @Sendable (StateType) -> Disposable) -> Disposable { self.scheduleInternal(state, action: action) } - func scheduleInternal(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable { + func scheduleInternal(_ state: StateType, action: @escaping @Sendable (StateType) -> Disposable) -> Disposable { self.configuration.schedule(state, action: action) } @@ -112,7 +112,7 @@ public class SerialDispatchQueueScheduler : SchedulerType { - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ - public final func scheduleRelative(_ state: StateType, dueTime: RxTimeInterval, action: @escaping (StateType) -> Disposable) -> Disposable { + public final func scheduleRelative(_ state: StateType, dueTime: RxTimeInterval, action: @escaping @Sendable (StateType) -> Disposable) -> Disposable { self.configuration.scheduleRelative(state, dueTime: dueTime, action: action) } @@ -125,7 +125,7 @@ public class SerialDispatchQueueScheduler : SchedulerType { - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ - public func schedulePeriodic(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping (StateType) -> StateType) -> Disposable { + public func schedulePeriodic(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping @Sendable (StateType) -> StateType) -> Disposable { self.configuration.schedulePeriodic(state, startAfter: startAfter, period: period, action: action) } } diff --git a/RxSwift/Schedulers/VirtualTimeScheduler.swift b/RxSwift/Schedulers/VirtualTimeScheduler.swift index 4f55d29d7..392513bfe 100644 --- a/RxSwift/Schedulers/VirtualTimeScheduler.swift +++ b/RxSwift/Schedulers/VirtualTimeScheduler.swift @@ -10,7 +10,8 @@ import Foundation /// Base class for virtual time schedulers using a priority queue for scheduled items. open class VirtualTimeScheduler - : SchedulerType { + : SchedulerType + , @unchecked Sendable { public typealias VirtualTime = Converter.VirtualTimeUnit public typealias VirtualTimeInterval = Converter.VirtualTimeIntervalUnit @@ -24,6 +25,8 @@ open class VirtualTimeScheduler private var nextId = 0 + private let thread: Thread + /// - returns: Current time. public var now: RxTime { self.converter.convertFromVirtualTime(self.clock) @@ -41,6 +44,7 @@ open class VirtualTimeScheduler self.currentClock = initialClock self.running = false self.converter = converter + self.thread = Thread.current self.schedulerQueue = PriorityQueue(hasHigherPriority: { switch converter.compareVirtualTime($0.time, $1.time) { case .lessThan: @@ -63,7 +67,7 @@ open class VirtualTimeScheduler - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ - public func schedule(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable { + public func schedule(_ state: StateType, action: @escaping @Sendable (StateType) -> Disposable) -> Disposable { return self.scheduleRelative(state, dueTime: .microseconds(0)) { a in return action(a) } @@ -77,7 +81,7 @@ open class VirtualTimeScheduler - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ - public func scheduleRelative(_ state: StateType, dueTime: RxTimeInterval, action: @escaping (StateType) -> Disposable) -> Disposable { + public func scheduleRelative(_ state: StateType, dueTime: RxTimeInterval, action: @escaping @Sendable (StateType) -> Disposable) -> Disposable { let time = self.now.addingDispatchInterval(dueTime) let absoluteTime = self.converter.convertToVirtualTime(time) let adjustedTime = self.adjustScheduledTime(absoluteTime) @@ -92,7 +96,7 @@ open class VirtualTimeScheduler - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ - public func scheduleRelativeVirtual(_ state: StateType, dueTime: VirtualTimeInterval, action: @escaping (StateType) -> Disposable) -> Disposable { + public func scheduleRelativeVirtual(_ state: StateType, dueTime: VirtualTimeInterval, action: @escaping @Sendable (StateType) -> Disposable) -> Disposable { let time = self.converter.offsetVirtualTime(self.clock, offset: dueTime) return self.scheduleAbsoluteVirtual(state, time: time, action: action) } @@ -105,9 +109,8 @@ open class VirtualTimeScheduler - parameter action: Action to be executed. - returns: The disposable object used to cancel the scheduled action (best effort). */ - public func scheduleAbsoluteVirtual(_ state: StateType, time: VirtualTime, action: @escaping (StateType) -> Disposable) -> Disposable { - MainScheduler.ensureExecutingOnScheduler() - + public func scheduleAbsoluteVirtual(_ state: StateType, time: VirtualTime, action: @escaping @Sendable (StateType) -> Disposable) -> Disposable { + ensusreRunningOnCorrectThread() let compositeDisposable = CompositeDisposable() let item = VirtualSchedulerItem(action: { @@ -130,12 +133,11 @@ open class VirtualTimeScheduler /// Starts the virtual time scheduler. public func start() { - MainScheduler.ensureExecutingOnScheduler() - if self.running { return } + ensusreRunningOnCorrectThread() self.running = true repeat { guard let next = self.findNext() else { @@ -170,12 +172,11 @@ open class VirtualTimeScheduler /// /// - parameter virtualTime: Absolute time to advance the scheduler's clock to. public func advanceTo(_ virtualTime: VirtualTime) { - MainScheduler.ensureExecutingOnScheduler() - if self.running { fatalError("Scheduler is already running") } + ensusreRunningOnCorrectThread() self.running = true repeat { guard let next = self.findNext() else { @@ -199,8 +200,7 @@ open class VirtualTimeScheduler /// Advances the scheduler's clock by the specified relative time. public func sleep(_ virtualInterval: VirtualTimeInterval) { - MainScheduler.ensureExecutingOnScheduler() - + ensusreRunningOnCorrectThread() let sleepTo = self.converter.offsetVirtualTime(self.clock, offset: virtualInterval) if self.converter.compareVirtualTime(sleepTo, self.clock).lessThen { fatalError("Can't sleep to past.") @@ -211,8 +211,7 @@ open class VirtualTimeScheduler /// Stops the virtual time scheduler. public func stop() { - MainScheduler.ensureExecutingOnScheduler() - + ensusreRunningOnCorrectThread() self.running = false } @@ -221,6 +220,12 @@ open class VirtualTimeScheduler _ = Resources.decrementTotal() } #endif + + private func ensusreRunningOnCorrectThread() { + guard Thread.current == thread else { + rxFatalError("Executing on the wrong thread. Please ensure all work on the same thread.") + } + } } // MARK: description @@ -233,8 +238,9 @@ extension VirtualTimeScheduler: CustomDebugStringConvertible { } final class VirtualSchedulerItem