In Swift 6, closures created in an isolated context automatically inherit the isolation of that context, unless they are marked as @Sendable. Because of this, creating an operation like map, flatMap, distinctUntilChanged, etc. from an isolated context, but calling it from a different thread, causes the call to crash.
Expected outcome:
No crash should happen even without manually marking the closures as @Sendable. All closures passed to operators that could be executed on another thread, should be marked as @Sendable inside the RxSwift framework.
What actually happens:
Unless manually specifying that the closure is Sendable, the code crashes
@preconcurrency import RxSwift
@MainActor
func test() -> any Disposable {
return Observable<Int>.just(1)
// any non main thread scheduler
.observe(on: RxSwift.SerialDispatchQueueScheduler(qos: .background))
.map({ $0 + 1 })
.subscribe()
}
Workaround:
@preconcurrency import RxSwift
@MainActor
func test() -> any Disposable {
return Observable<Int>.just(1)
// any non main thread scheduler
.observe(on: RxSwift.SerialDispatchQueueScheduler(qos: .background))
.map({ @Sendable in $0 + 1 })
.subscribe()
}
do a find and replace for @escaping and replace it with @escaping @Sendable, as most closures that are escaping should also be Sendable
make needed classes to conform to the @unchecked Sendable protocol. Most classes already are thread-safe but are not marked as Sendable
RxSwift/RxCocoa/RxBlocking/RxTest version/commit
6.8.0
Platform/Environment
[x] iOS
[ ] macOS
[ ] tvOS
[ ] watchOS
[ ] playgrounds
How easy is to reproduce? (chances of successful reproduce after running the self contained code)
[x] easy, 100% repro
[ ] sometimes, 10%-100%
[ ] hard, 2% - 10%
[ ] extremely hard, %0 - 2%
Xcode version:
16.0
:warning: Fields below are optional for general issues or in case those questions aren't related to your issue, but filling them out will increase the chances of getting your issue resolved. :warning:
Installation method:
[x] CocoaPods
[ ] Carthage
[ ] Git submodules
I have multiple versions of Xcode installed:
(so we can know if this is a potential cause of your issue)
[ ] yes (which ones)
[x] no
Level of RxSwift knowledge:
(this is so we can understand your level of knowledge
and formulate the response in an appropriate manner)
Short description of the issue:
In Swift 6, closures created in an isolated context automatically inherit the isolation of that context, unless they are marked as
@Sendable
. Because of this, creating an operation like map, flatMap, distinctUntilChanged, etc. from an isolated context, but calling it from a different thread, causes the call to crash.Expected outcome:
No crash should happen even without manually marking the closures as
@Sendable
. All closures passed to operators that could be executed on another thread, should be marked as@Sendable
inside the RxSwift framework.What actually happens:
Unless manually specifying that the closure is Sendable, the code crashes
Workaround:
Example fix (
Map.swift
):should be:
Proposed fix:
@escaping
and replace it with@escaping @Sendable
, as most closures that are escaping should also be Sendable@unchecked Sendable
protocol. Most classes already are thread-safe but are not marked as SendableRxSwift/RxCocoa/RxBlocking/RxTest version/commit
6.8.0
Platform/Environment
How easy is to reproduce? (chances of successful reproduce after running the self contained code)
Xcode version:
:warning: Fields below are optional for general issues or in case those questions aren't related to your issue, but filling them out will increase the chances of getting your issue resolved. :warning:
Installation method:
I have multiple versions of Xcode installed: (so we can know if this is a potential cause of your issue)
Level of RxSwift knowledge: (this is so we can understand your level of knowledge and formulate the response in an appropriate manner)