Description
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
@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()
}
Example fix (Map.swift
):
public func map<Result>(_ transform: @escaping (Element) throws -> Result)
-> Observable<Result> {
should be:
public func map<Result>(_ transform: @escaping @Sendable (Element) throws -> Result)
-> Observable<Result> {
Proposed fix:
- 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
- iOS
- macOS
- tvOS
- watchOS
- playgrounds
How easy is to reproduce? (chances of successful reproduce after running the self contained code)
- easy, 100% repro
- sometimes, 10%-100%
- hard, 2% - 10%
- extremely hard, %0 - 2%
Xcode version:
16.0
Installation method:
- 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)
- no
Level of RxSwift knowledge:
(this is so we can understand your level of knowledge
and formulate the response in an appropriate manner)
- just starting
- I have a small code base
- I have a significant code base