Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Strict concurrency #30

Merged
merged 22 commits into from
Mar 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
import PackageDescription
import CompilerPluginSupport

#if swift(>=5.10)
let settings = [ .enableExperimentalFeature("StrictConcurrency") ]
#else
let settings = [ SwiftSetting ]()
#endif

let package = Package(
name: "ManagedModels",

Expand All @@ -13,8 +19,12 @@ let package = Package(
.package(url: "https://github.com/apple/swift-syntax.git", from: "509.0.0")
],
targets: [
.target(name: "ManagedModels", dependencies: [ "ManagedModelMacros" ]),

.target(
name: "ManagedModels",
dependencies: [ "ManagedModelMacros" ],
swiftSettings: settings
),

.macro(
name: "ManagedModelMacros",
dependencies: [
Expand Down
8 changes: 4 additions & 4 deletions Sources/ManagedModels/Container/ModelConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
// Copyright © 2023 ZeeZide GmbH.
//

import CoreData
@preconcurrency import CoreData

public struct ModelConfiguration: Hashable {
public struct ModelConfiguration: Hashable, Sendable {
// TBD: Some of those are `let` in SwiftData

public var path : String
Expand Down Expand Up @@ -83,7 +83,7 @@ extension ModelConfiguration: Identifiable {

public extension ModelConfiguration {

struct GroupContainer: Hashable {
struct GroupContainer: Hashable, Sendable {
enum Value: Hashable {
case automatic, none
case identifier(String)
Expand All @@ -100,7 +100,7 @@ public extension ModelConfiguration {

public extension ModelConfiguration {

struct CloudKitDatabase: Hashable {
struct CloudKitDatabase: Hashable, Sendable {
enum Value: Hashable {
case automatic, none
case `private`(String)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public extension NSManagedObjectModel {
* All (code defined) properties of the ``NSManagedObjectModel`` are stored
* in the `schemaMetadata` static property.
*/
struct PropertyMetadata {
struct PropertyMetadata: @unchecked Sendable {

/// The name of the property instance variable, e.g. `street`.
public let name : String
Expand Down
4 changes: 2 additions & 2 deletions Sources/ManagedModels/Schema/AttributeOption.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import class Foundation.ValueTransformer

extension NSAttributeDescription {

public struct Option: Equatable {
public struct Option: Equatable, Sendable {

let value : Value

Expand Down Expand Up @@ -39,7 +39,7 @@ extension NSAttributeDescription {

extension NSAttributeDescription.Option {

enum Value {
enum Value: Sendable {
case unique, externalStorage, preserveValueOnDeletion, ephemeral, spotlight
case transformableByType(ValueTransformer.Type)
case transformableByName(String)
Expand Down
4 changes: 2 additions & 2 deletions Sources/ManagedModels/Schema/RelationshipOption.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import CoreData

public extension NSRelationshipDescription {

struct Option: Equatable {
struct Option: Equatable, Sendable {

enum Value {
enum Value: Sendable {
case unique
}
let value : Value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import CoreData

public extension CoreData.NSAttributeDescription {
struct TypeConfiguration {
struct TypeConfiguration: Sendable {
let attributeType : NSAttributeType
let isOptional : Bool
let attributeValueClassName : String?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,14 @@ public extension NSManagedObjectModel {
// MARK: - Cached ManagedObjectModels

private let lock = NSLock()
#if swift(>=5.10)
nonisolated(unsafe)
private var map = [ Set<ObjectIdentifier> : NSManagedObjectModel ]()
private let sharedBuilder = SchemaBuilder()
nonisolated(unsafe) private let sharedBuilder = SchemaBuilder()
#else // 5.9: nonisolated(unsafe) not available, nonisolated nor working on var
private var map = [ Set<ObjectIdentifier> : NSManagedObjectModel ]()
nonisolated private let sharedBuilder = SchemaBuilder()
#endif

public extension NSManagedObjectModel {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,44 @@
// Copyright © 2023 ZeeZide GmbH.
//

private var _propertyIsUniqueAssociatedKey: UInt8 = 42

extension NSPropertyDescription {

private struct AssociatedKeys {
#if swift(>=5.10)
nonisolated(unsafe) static var propertyIsUniqueAssociatedKey: Void? = nil
#else // 5.9: nonisolated(unsafe) not available
static var propertyIsUniqueAssociatedKey: Void? = nil
#endif
}

public internal(set) var isUnique: Bool {
// Note: isUnique is only used during schema construction!
set {
if newValue {
objc_setAssociatedObject(self, &_propertyIsUniqueAssociatedKey,
type(of: self), .OBJC_ASSOCIATION_ASSIGN)
objc_setAssociatedObject(
self, &AssociatedKeys.propertyIsUniqueAssociatedKey,
type(of: self), // Just used as a flag, type won't go away
.OBJC_ASSOCIATION_ASSIGN
)
}
else {
objc_setAssociatedObject(self, &_propertyIsUniqueAssociatedKey, nil, .OBJC_ASSOCIATION_RETAIN)
objc_setAssociatedObject(
self, &AssociatedKeys.propertyIsUniqueAssociatedKey,
nil, // clear // clear flag
.OBJC_ASSOCIATION_ASSIGN
)
}
#if false // do we need this? The entity might not yet be setup?
#if false // do we need this? The entity might not yet be setup?
guard !entity.isPropertyUnique(self) else { return }
entity.uniquenessConstraints.append( [ self ])
#endif
#endif
}
get {
objc_getAssociatedObject(self, &_propertyIsUniqueAssociatedKey) != nil
? true
: entity.isPropertyUnique(self)
objc_getAssociatedObject(
self,
&AssociatedKeys.propertyIsUniqueAssociatedKey
) != nil
? true
: entity.isPropertyUnique(self)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ extension CoreData.NSRelationshipDescription {
extension CoreData.NSRelationshipDescription: SchemaProperty {}

public extension CoreData.NSRelationshipDescription {

@inlinable var isToOneRelationship : Bool { !isToMany }

@inlinable var isAttribute : Bool { return false }
@inlinable var isRelationship : Bool { return true }

Expand Down Expand Up @@ -109,7 +109,7 @@ extension CoreData.NSRelationshipDescription {
// MARK: - Initializer

public extension CoreData.NSRelationshipDescription {

// Note: This matches what the `Relationship` macro takes.
convenience init(_ options: Option..., deleteRule: NSDeleteRule = .nullify,
minimumModelCount: Int? = 0, maximumModelCount: Int? = 0,
Expand All @@ -122,16 +122,16 @@ public extension CoreData.NSRelationshipDescription {
precondition(minimumModelCount ?? 0 >= 0)
precondition(maximumModelCount ?? 0 >= 0)
self.init()

self.name = name ?? ""
self.valueType = valueType
self.renamingIdentifier = originalName ?? ""
self.versionHashModifier = hashModifier
self.deleteRule = deleteRule
self.inverseKeyPath = inverse

if options.contains(.unique) { isUnique = true }

if let minimumModelCount { self.minCount = minimumModelCount }
if let maximumModelCount {
self.maxCount = maximumModelCount
Expand All @@ -140,7 +140,7 @@ public extension CoreData.NSRelationshipDescription {
if valueType is any RelationshipCollection.Type {
self.maxCount = 0
}
else if valueType is NSOrderedSet.Type ||
else if valueType is NSOrderedSet.Type ||
valueType is Optional<NSOrderedSet>.Type
{
self.maxCount = 0
Expand All @@ -155,10 +155,8 @@ public extension CoreData.NSRelationshipDescription {

// MARK: - Storage

private var _relationshipInfoAssociatedKey: UInt8 = 72

extension CoreData.NSRelationshipDescription {

func internalCopy() -> Self {
guard let copy = self.copy() as? Self else {
fatalError("Could not copy relationship \(self)")
Expand Down Expand Up @@ -187,7 +185,7 @@ extension CoreData.NSRelationshipDescription {
var inverseName : String?
var destination : String?
var isToOneRelationship : Bool?

override func copy() -> Any { internalCopy() }

func internalCopy() -> MacroInfo {
Expand All @@ -201,11 +199,18 @@ extension CoreData.NSRelationshipDescription {
return copy
}
}


private struct AssociatedKeys {
#if swift(>=5.10)
nonisolated(unsafe) static var relationshipInfoAssociatedKey: Void? = nil
#else // 5.9: nonisolated(unsafe) not available
static var relationshipInfoAssociatedKey: Void? = nil
#endif
}

var writableRelationshipInfo : MacroInfo {
if let info =
objc_getAssociatedObject(self, &_relationshipInfoAssociatedKey)
as? MacroInfo
if let info = objc_getAssociatedObject(
self, &AssociatedKeys.relationshipInfoAssociatedKey) as? MacroInfo
{
return info
}
Expand All @@ -214,16 +219,19 @@ extension CoreData.NSRelationshipDescription {
self.relationshipInfo = info
return info
}

var relationshipInfo: MacroInfo? {
// Note: isUnique is only used during schema construction!
set {
objc_setAssociatedObject(self, &_relationshipInfoAssociatedKey,
newValue, .OBJC_ASSOCIATION_RETAIN)
objc_setAssociatedObject(
self, &AssociatedKeys.relationshipInfoAssociatedKey,
newValue, .OBJC_ASSOCIATION_RETAIN
)
}
get {
objc_getAssociatedObject(self, &_relationshipInfoAssociatedKey)
as? MacroInfo
objc_getAssociatedObject(
self, &AssociatedKeys.relationshipInfoAssociatedKey
) as? MacroInfo
}
}
}
6 changes: 6 additions & 0 deletions Sources/ManagedModels/SwiftUI/ModelContainer+SwiftUI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public extension View {
self.modelContext(container.viewContext)
}

@MainActor
@ViewBuilder
func modelContainer(
for modelTypes : [ any PersistentModel.Type ],
Expand All @@ -48,6 +49,7 @@ public extension View {
}
}

@MainActor
@inlinable
func modelContainer(
for modelType : any PersistentModel.Type,
Expand All @@ -73,6 +75,7 @@ public extension Scene {
self.modelContext(container.viewContext)
}

@MainActor
@SceneBuilder
func modelContainer(
for modelTypes : [ any PersistentModel.Type ],
Expand All @@ -92,6 +95,7 @@ public extension Scene {
self.modelContainer(try! result.get())
}

@MainActor
@inlinable
func modelContainer(
for modelType : any PersistentModel.Type,
Expand All @@ -113,8 +117,10 @@ public extension Scene {
// MARK: - Primitive

// Note: The docs say that a container is only ever created once! So cache it.
@MainActor
private var modelToContainer = [ ObjectIdentifier: NSPersistentContainer ]()

@MainActor
private func makeModelContainer(
for modelTypes : [ any PersistentModel.Type ],
inMemory : Bool = false,
Expand Down
Loading