Make my SwiftData code concurrency proof (sendable model, singleton)

Here is my current code:

@Model
final public class ServerModel {
    @Attribute(.unique) var serverUrl: URL

    init(serverUrl: URL) {
        self.serverUrl = serverUrl
    }
}

@ModelActor
public actor MyDatabaseService {
    public static var shared = MyDatabaseService()

    public init() {
        self.modelContainer = try! ModelContainer(for: ServerModel.self)
        let context = ModelContext(modelContainer)
        self.modelExecutor = DefaultSerialModelExecutor(modelContext: context)
    }

    public func listServers() throws -> [ServerModel] {
        let descriptor = FetchDescriptor<ServerModel>()

        return try modelContext.fetch(descriptor)
    }
}

When try to call try await MyDatabaseService.shared.listServers()

There are two problems:

  • ServerModel is not Sendable
  • "Reference to static property 'shared' is not concurrency-safe because it involves shared mutable state; this is an error in Swift 6"

For the first one, I can solve it by doing:

public struct Server {
    let serverUrl: URL;
}

@Model
final public class ServerModel {
    @Attribute(.unique) var serverUrl: URL

    init(serverUrl: URL) {
        self.serverUrl = serverUrl
    }

    func getSendable() -> Server {
        return Server(serverUrl: self.serverUrl)
    }
}

@ModelActor
public actor MyDatabaseService {
    (...)

    public func listServers() throws -> [Server] {
        let descriptor = FetchDescriptor<ServerModel>()

        return try modelContext.fetch(descriptor).map { $0.getSendable() }
    }
}

I am wondering if there is a smarter solution to this first issue. SwiftData already require to define the model with basic/codable types, so if there was a magic way to get a sendable from the model. I try to make my model 'Codable' but 'Codable' is not compatible with 'Sendable'.

For my second issue, the singleton issue. I do not really know how to fix it.

Actually, the singleton was easier to fix. I have just realised that replacing:

public static var shared = MyDatabaseService()

is more correct and fix the issue:

public static let shared = MyDatabaseService()
Make my SwiftData code concurrency proof (sendable model, singleton)
 
 
Q