diff --git a/CodeGen/Sources/LucidCodeGen/Meta/MetaEntity.swift b/CodeGen/Sources/LucidCodeGen/Meta/MetaEntity.swift index 46a025e..1a0d904 100644 --- a/CodeGen/Sources/LucidCodeGen/Meta/MetaEntity.swift +++ b/CodeGen/Sources/LucidCodeGen/Meta/MetaEntity.swift @@ -68,6 +68,7 @@ struct MetaEntity { return Type(identifier: entity.typeID()) .adding(inheritedType: .codable) + .adding(inheritedType: entity.senable ? .sendable : nil) .with(kind: .class(final: true)) .with(accessLevel: .public) .with(body: [ diff --git a/CodeGen/Sources/LucidCodeGen/Meta/MetaEntityIdentifier.swift b/CodeGen/Sources/LucidCodeGen/Meta/MetaEntityIdentifier.swift index 5f7db6a..466f74b 100644 --- a/CodeGen/Sources/LucidCodeGen/Meta/MetaEntityIdentifier.swift +++ b/CodeGen/Sources/LucidCodeGen/Meta/MetaEntityIdentifier.swift @@ -44,6 +44,7 @@ struct MetaEntityIdentifier { return Type(identifier: entity.identifierTypeID()) .adding(inheritedType: .codable) + .adding(inheritedType: entity.senable ? .sendable : nil) .with(kind: .class(final: true)) .with(accessLevel: .public) .adding(inheritedType: .coreDataIdentifier) diff --git a/CodeGen/Sources/LucidCodeGen/Meta/MetaSubtype.swift b/CodeGen/Sources/LucidCodeGen/Meta/MetaSubtype.swift index a7be3a2..2d0a476 100644 --- a/CodeGen/Sources/LucidCodeGen/Meta/MetaSubtype.swift +++ b/CodeGen/Sources/LucidCodeGen/Meta/MetaSubtype.swift @@ -50,6 +50,7 @@ struct MetaSubtype { let type = Type(identifier: subtype.typeID()) .with(accessLevel: .public) .adding(inheritedType: .codable) + .adding(inheritedType: subtype.sendable ? .sendable : nil) .adding(inheritedType: .hashable) switch subtype.items { diff --git a/CodeGen/Sources/LucidCodeGenCore/Codable.swift b/CodeGen/Sources/LucidCodeGenCore/Codable.swift index 0f4cc7e..edd82af 100644 --- a/CodeGen/Sources/LucidCodeGenCore/Codable.swift +++ b/CodeGen/Sources/LucidCodeGenCore/Codable.swift @@ -40,6 +40,7 @@ public enum DescriptionDefaults { public static let ignorePropertyMigrationChecksOn = [String]() public static let httpMethod: EndpointPayloadTest.HTTPMethod = .get public static let cacheSize: EntityCacheSize = .group(.medium) + public static let sendable = false } public extension Entity { @@ -322,6 +323,7 @@ extension Entity: Codable { case queryContext case clientQueueName case cacheSize + case sendable } public init(from decoder: Decoder) throws { @@ -348,6 +350,7 @@ extension Entity: Codable { queryContext = try container.decodeIfPresent(Bool.self, forKey: .queryContext) ?? DescriptionDefaults.queryContext clientQueueName = try container.decodeIfPresent(String.self, forKey: .clientQueueName) ?? DescriptionDefaults.clientQueueName cacheSize = try container.decodeIfPresent(EntityCacheSize.self, forKey: .cacheSize) ?? DescriptionDefaults.cacheSize + senable = try container.decodeIfPresent(Bool.self, forKey: .sendable) ?? DescriptionDefaults.sendable let systemPropertiesSet = Set(SystemPropertyName.allCases.map { $0.rawValue }) for property in properties where systemPropertiesSet.contains(property.name) { @@ -754,6 +757,7 @@ extension Subtype: Codable { case objc case objcNoneCase case platforms + case sendable } public init(from decoder: Decoder) throws { @@ -762,6 +766,7 @@ extension Subtype: Codable { name = try container.decode(String.self, forKey: .name) manualImplementations = Set(try container.decodeIfPresent([`Protocol`].self, forKey: .manualImplementations) ?? []) platforms = try container.decodeIfPresent(Set.self, forKey: .platforms) ?? DescriptionDefaults.platforms + sendable = try container.decodeIfPresent(Bool.self, forKey: .sendable) ?? DescriptionDefaults.sendable if let usedCases = try container.decodeIfPresent([String].self, forKey: .cases) { let unusedCases = try container.decodeIfPresent([String].self, forKey: .unusedCases) ?? [] diff --git a/CodeGen/Sources/LucidCodeGenCore/Descriptions.swift b/CodeGen/Sources/LucidCodeGenCore/Descriptions.swift index 2dd20ce..654336b 100644 --- a/CodeGen/Sources/LucidCodeGenCore/Descriptions.swift +++ b/CodeGen/Sources/LucidCodeGenCore/Descriptions.swift @@ -368,6 +368,8 @@ public struct Entity: Equatable { public let clientQueueName: String public let cacheSize: EntityCacheSize + + public let senable: Bool public init(name: String, persistedName: String? = nil, @@ -382,7 +384,8 @@ public struct Entity: Equatable { versionHistory: [VersionHistoryItem] = [], queryContext: Bool = DescriptionDefaults.queryContext, clientQueueName: String = DescriptionDefaults.clientQueueName, - cacheSize: EntityCacheSize = DescriptionDefaults.cacheSize) { + cacheSize: EntityCacheSize = DescriptionDefaults.cacheSize, + sendable: Bool = DescriptionDefaults.sendable) { self.name = name self.persistedName = persistedName @@ -400,6 +403,7 @@ public struct Entity: Equatable { self.queryContext = queryContext self.clientQueueName = clientQueueName self.cacheSize = cacheSize + self.senable = sendable } } @@ -641,6 +645,8 @@ public struct Subtype: Equatable { public let objc: Bool public let platforms: Set + + public let sendable: Bool } // MARK: - Conversions diff --git a/CodeGen/Sources/LucidCodeGenCore/MetaUtils.swift b/CodeGen/Sources/LucidCodeGenCore/MetaUtils.swift index dfa69e2..a41aa13 100644 --- a/CodeGen/Sources/LucidCodeGenCore/MetaUtils.swift +++ b/CodeGen/Sources/LucidCodeGenCore/MetaUtils.swift @@ -206,6 +206,10 @@ public extension TypeIdentifier { return TypeIdentifier(name: "CoreDataConversionError") } + static var sendable: TypeIdentifier { + return TypeIdentifier(name: "Sendable") + } + static var equatable: TypeIdentifier { return TypeIdentifier(name: "Equatable") } diff --git a/Lucid/Core/Payload.swift b/Lucid/Core/Payload.swift index ee49d12..0331cbc 100644 --- a/Lucid/Core/Payload.swift +++ b/Lucid/Core/Payload.swift @@ -85,6 +85,8 @@ public enum Lazy { case unrequested } +extension Lazy: Sendable where T: Sendable {} + public extension Lazy where T: PayloadIdentifiable { func identifier() -> Lazy { diff --git a/Lucid/Utils/AnySequence.swift b/Lucid/Utils/AnySequence.swift index ae71267..20df6a0 100644 --- a/Lucid/Utils/AnySequence.swift +++ b/Lucid/Utils/AnySequence.swift @@ -61,6 +61,8 @@ extension AnySequence: Equatable where Element: Equatable { } } +extension AnySequence: @unchecked Sendable where Element: Sendable {} + public extension Result where Success: Sequence { @inlinable var any: Result, Failure> { diff --git a/Lucid/Utils/PropertyBox.swift b/Lucid/Utils/PropertyBox.swift index 8694c8c..cc82be6 100644 --- a/Lucid/Utils/PropertyBox.swift +++ b/Lucid/Utils/PropertyBox.swift @@ -9,7 +9,7 @@ import Foundation /// A mutable property which can either ensure atomicity or not. -public final class PropertyBox { +public final class PropertyBox: @unchecked Sendable { private let valueQueue: DispatchQueue?