Search code examples
swiftswift-concurrency

Can a class be Sendable if it holds a reference to a non-Sendable singleton?


Suppose I have a class whose only state is a reference to a singleton class:

class MyClass {
    let networkHelper: NetworkHelper

    func makeNetworkRequest(_ url: URL) {
        networkHelper.actuallyMakeNetworkRequest(url)
    }
}

The NetworkHelper class is not Sendable; it maintains some mutable state (although that state is mostly invisible to outside callers). However, the class is a singleton and so any two instances of MyClass will always hold references to the same NetworkHelper.

Given that MyClass contains no mutable state of its own, and that it does hold a reference to a non-Sendable class instance, does MyClass conform to Sendable?


Solution

  • In short, because NetworkHelper is not Sendable, MyClass cannot be Sendable either. The fact that NetworkHelper is a singleton is immaterial.

    FWIW, the compiler will help you out here: If you attempt to make MyClass conform to Sendable (by making it final and adding Sendable conformance), the compiler will tell you about the issue with NetworkHelper not being Sendable:

    final class MyClass: Sendable {
        let networkHelper: NetworkHelper = .shared    // Stored property 'networkHelper' of 'Sendable'-conforming class 'MyClass' has non-sendable type 'NetworkHelper'
    
        func makeNetworkRequest(_ url: URL) {
            networkHelper.actuallyMakeNetworkRequest(url)
        }
    }
    

    enter image description here

    For MyClass to be Sendable, you will need to make NetworkHelper conform to Sendable, too.

    FWIW, if NetworkHelper has a mutable state, you probably want to make it threadsafe, anyway, and once you do that, making it Sendable, too, is easy.