swiftcombineswift6

How to make a Set<AnyCancellable> sendable?


How can I get rid of @unchecked Sendable in this case:

final class TestSendable: @unchecked Sendable {
    private var subscriptions = Set<AnyCancellable>()
}

apart from:

  1. @MainActor subscriptions. I want to avoid unnecessary hopping to main thread
  2. Changing class to actor. I don't want to use actor just for the sake of it.

Solution

  • There are not a lot of synchronisation mechanisms that Swift can "check". Mutex is one of them.

    import Synchronization
    
    final class TestSendable: Sendable {
        private let subscriptions = Mutex(Set<AnyCancellable>())
        
        func exampleSubscription() {
            subscriptions.withLock { subs in
                [1,2,3].publisher.sink {
                    print($0)
                }
                .store(in: &subs)
            }
        }
    }
    

    I would also consider using an actor. subscriptions is a mutable state, and for TestSendable to be sendable, it must be protected. The whole point of an actor is to protect mutable state by ensuring that accesses to them are synchronised. Using an actor here is not "just for the seek of it" [sic]. You are using actors for the purpose that they are designed for.

    Also consider the idea that making TestSendable sendable might not be necessary in the first place. Does all the isolation domains need to share this one instance holding all the subscriptions. Could each isolation domain manage their own set of subscriptions.