diff --git a/Sources/ManagedModelMacros/ModelMacro/GenerateInitializers.swift b/Sources/ManagedModelMacros/ModelMacro/GenerateInitializers.swift index db4ced2..5f8272f 100644 --- a/Sources/ManagedModelMacros/ModelMacro/GenerateInitializers.swift +++ b/Sources/ManagedModelMacros/ModelMacro/GenerateInitializers.swift @@ -42,11 +42,8 @@ extension ModelMacro { /// - Parameters: // - entity: An `NSEntityDescription` describing the object. // - context: An `NSManagedObjectContext` the object should be inserted into. - @available(*, deprecated, renamed: "init(context:)", - message: "Use `init(context:)` or `init()` instead.") \(raw: access)override init(entity: CoreData.NSEntityDescription, insertInto context: NSManagedObjectContext?) { - assert(entity === Self._$entity, "Attempt to initialize PersistentModel w/ different entity?") super.init(entity: entity, insertInto: context) } """ @@ -67,7 +64,7 @@ extension ModelMacro { /// - Parameters: // - context: An `NSManagedObjectContext` the object should be inserted into. \(raw: access)init(context: CoreData.NSManagedObjectContext?) { - super.init(entity: Self._$entity, insertInto: context) + super.init(entity: Self.entity(), insertInto: context) } """ ) @@ -78,7 +75,7 @@ extension ModelMacro { /// Initialize a `\(modelClassName)` object w/o inserting it into a /// context. \(raw: access)init() { - super.init(entity: Self._$entity, insertInto: nil) + super.init(entity: Self.entity(), insertInto: nil) } """ ) diff --git a/Sources/ManagedModelMacros/ModelMacro/ModelMembers.swift b/Sources/ManagedModelMacros/ModelMacro/ModelMembers.swift index cf8ea79..3eba04e 100644 --- a/Sources/ManagedModelMacros/ModelMacro/ModelMembers.swift +++ b/Sources/ManagedModelMacros/ModelMacro/ModelMembers.swift @@ -14,8 +14,6 @@ import SwiftDiagnostics * @attached(member, names: // Those are the names we add * named(init), // Initializers. * named(schemaMetadata), // The metadata. - * named(entity), // Override the `entity()` function. - * named(_$entity), // The cached the Entity * named(_$originalName), * named(_$hashModifier) * ) @@ -55,17 +53,6 @@ extension ModelMacro: MemberMacro { // @attached(member, names:...) ) newMembers.append(DeclSyntax(metadata)) - if classDecl.findFunctionWithName("entity", isStaticOrClass: true, - parameterCount: 0) == nil - { - newMembers.append( - """ - /// Returns the `NSEntityDescription` associated w/ the `PersistentModel`. - \(raw: access)override class func entity() -> NSEntityDescription { _$entity } - """ - ) - } - // TODO: Lookup `originalName` parameter in `macroNode` newMembers.append( """ @@ -77,15 +64,6 @@ extension ModelMacro: MemberMacro { // @attached(member, names:...) \(raw: access)static let _$hashModifier : String? = nil """ ) - - newMembers.append( - """ - /// The shared `NSEntityDescription` for the `PersistentModel`. - /// Never modify the referred object! - \(raw: access)static let _$entity = - ManagedModels.SchemaBuilder.shared._entity(for: \(modelClassName).self) - """ - ) return newMembers } diff --git a/Sources/ManagedModels/ModelMacroDefinition.swift b/Sources/ManagedModels/ModelMacroDefinition.swift index 09c4d4b..4a762f9 100644 --- a/Sources/ManagedModels/ModelMacroDefinition.swift +++ b/Sources/ManagedModels/ModelMacroDefinition.swift @@ -81,8 +81,6 @@ public macro _PersistedProperty() = @attached(member, names: // Those are the names we add named(init), // Initializers. named(schemaMetadata), // The metadata. - named(entity), // Override the `entity()` function. - named(_$entity), // The cached the Entity named(_$originalName), named(_$hashModifier) ) diff --git a/Sources/ManagedModels/PersistentModel/PersistentModel+KVC.swift b/Sources/ManagedModels/PersistentModel/PersistentModel+KVC.swift index 0ae9630..4bb2bed 100644 --- a/Sources/ManagedModels/PersistentModel/PersistentModel+KVC.swift +++ b/Sources/ManagedModels/PersistentModel/PersistentModel+KVC.swift @@ -120,10 +120,6 @@ public extension PersistentModel { func _setOptionalToOneValue(forKey key: String, to model: T?) where T: PersistentModel { - #if DEBUG - let relship = Self._$entity.relationshipsByName[key]! - assert(!relship.isToMany, "relship: \(relship)") - #endif if let model { if model.modelContext != self.modelContext { if let otherCtx = model.modelContext, self.modelContext == nil { diff --git a/Sources/ManagedModels/PersistentModel/PersistentModel.swift b/Sources/ManagedModels/PersistentModel/PersistentModel.swift index cafa6ec..9360666 100644 --- a/Sources/ManagedModels/PersistentModel/PersistentModel.swift +++ b/Sources/ManagedModels/PersistentModel/PersistentModel.swift @@ -19,16 +19,6 @@ public protocol PersistentModel: NSManagedObject, Hashable, Identifiable { */ static var schemaMetadata : [ NSManagedObjectModel.PropertyMetadata ] { get } - /** - * Reflection data for the model. - * - * This is considered private, use a Schema to access entities, and NEVER - * modify the schema objects after they got setup. - * - * API DIFF: SwiftData doesn't have that, always builds dynamically. - */ - static var _$entity : NSEntityDescription { get } - // Why have that? Cheap cache. /// The `renamingIdentifier` of the model. static var _$originalName : String? { get } @@ -55,16 +45,13 @@ extension PersistentModel { public static var schemaMetadata : [ NSManagedObjectModel.PropertyMetadata ] { fatalError("Subclass needs to implement `schemaMetadata`") } - - @inlinable - public static var _$entity : NSEntityDescription { self.entity() } } public extension PersistentModel { @inlinable static func fetchRequest() -> NSFetchRequest { - NSFetchRequest(entityName: _$entity.name ?? NSStringFromClass(self)) + NSFetchRequest(entityName: _typeName(Self.self, qualified: false)) } @inlinable diff --git a/Sources/ManagedModels/SchemaCompatibility/CoreDataPrimitiveValue.swift b/Sources/ManagedModels/SchemaCompatibility/CoreDataPrimitiveValue.swift index d79f16e..1228903 100644 --- a/Sources/ManagedModels/SchemaCompatibility/CoreDataPrimitiveValue.swift +++ b/Sources/ManagedModels/SchemaCompatibility/CoreDataPrimitiveValue.swift @@ -9,7 +9,7 @@ public extension CoreData.NSAttributeDescription { struct TypeConfiguration { let attributeType : NSAttributeType let isOptional : Bool - let attributeValueClassName : String + let attributeValueClassName : String? } } @@ -37,35 +37,35 @@ extension Int: CoreDataPrimitiveValue { public static let coreDataValue = NSAttributeDescription.TypeConfiguration( attributeType : .integer64AttributeType, isOptional : false, - attributeValueClassName : "NSNumber" + attributeValueClassName : nil ) } extension Int16: CoreDataPrimitiveValue { public static let coreDataValue = NSAttributeDescription.TypeConfiguration( attributeType : .integer16AttributeType, isOptional : false, - attributeValueClassName : "NSNumber" + attributeValueClassName : nil ) } extension Int32: CoreDataPrimitiveValue { public static let coreDataValue = NSAttributeDescription.TypeConfiguration( attributeType : .integer32AttributeType, isOptional : false, - attributeValueClassName : "NSNumber" + attributeValueClassName : nil ) } extension Int64: CoreDataPrimitiveValue { public static let coreDataValue = NSAttributeDescription.TypeConfiguration( attributeType : .integer64AttributeType, isOptional : false, - attributeValueClassName : "NSNumber" + attributeValueClassName : nil ) } extension Int8: CoreDataPrimitiveValue { public static let coreDataValue = NSAttributeDescription.TypeConfiguration( attributeType : .integer16AttributeType, isOptional : false, - attributeValueClassName : "NSNumber" + attributeValueClassName : nil ) } @@ -91,7 +91,7 @@ extension String: CoreDataPrimitiveValue { public static let coreDataValue = NSAttributeDescription.TypeConfiguration( attributeType : .stringAttributeType, isOptional : false, - attributeValueClassName : "NSString" + attributeValueClassName : nil ) } @@ -99,7 +99,7 @@ extension Bool: CoreDataPrimitiveValue { public static let coreDataValue = NSAttributeDescription.TypeConfiguration( attributeType : .booleanAttributeType, isOptional : false, - attributeValueClassName : "NSNumber" + attributeValueClassName : nil ) } @@ -107,14 +107,14 @@ extension Double: CoreDataPrimitiveValue { public static let coreDataValue = NSAttributeDescription.TypeConfiguration( attributeType : .doubleAttributeType, isOptional : false, - attributeValueClassName : "NSNumber" + attributeValueClassName : nil ) } extension Float: CoreDataPrimitiveValue { public static let coreDataValue = NSAttributeDescription.TypeConfiguration( attributeType : .floatAttributeType, isOptional : false, - attributeValueClassName : "NSNumber" + attributeValueClassName : nil ) } @@ -124,7 +124,7 @@ extension Date: CoreDataPrimitiveValue { public static let coreDataValue = NSAttributeDescription.TypeConfiguration( attributeType : .dateAttributeType, isOptional : false, - attributeValueClassName : "NSDate" + attributeValueClassName : nil ) } @@ -132,7 +132,7 @@ extension Data: CoreDataPrimitiveValue { public static let coreDataValue = NSAttributeDescription.TypeConfiguration( attributeType : .binaryDataAttributeType, isOptional : false, - attributeValueClassName : "NSDate" + attributeValueClassName : nil ) } @@ -140,7 +140,7 @@ extension Decimal: CoreDataPrimitiveValue { public static let coreDataValue = NSAttributeDescription.TypeConfiguration( attributeType : .decimalAttributeType, isOptional : false, - attributeValueClassName : "NSDecimalNumber" + attributeValueClassName : nil ) } @@ -148,7 +148,7 @@ extension UUID: CoreDataPrimitiveValue { public static let coreDataValue = NSAttributeDescription.TypeConfiguration( attributeType : .UUIDAttributeType, isOptional : false, - attributeValueClassName : "NSUUID" + attributeValueClassName : nil ) } @@ -156,6 +156,6 @@ extension URL: CoreDataPrimitiveValue { public static let coreDataValue = NSAttributeDescription.TypeConfiguration( attributeType : .URIAttributeType, isOptional : false, - attributeValueClassName : "NSURL" + attributeValueClassName : nil ) } diff --git a/Sources/ManagedModels/SchemaCompatibility/NSAttributeDescription+Data.swift b/Sources/ManagedModels/SchemaCompatibility/NSAttributeDescription+Data.swift index 4aec422..141ad3a 100644 --- a/Sources/ManagedModels/SchemaCompatibility/NSAttributeDescription+Data.swift +++ b/Sources/ManagedModels/SchemaCompatibility/NSAttributeDescription+Data.swift @@ -42,7 +42,9 @@ extension CoreData.NSAttributeDescription: SchemaProperty { let config = primitiveType.coreDataValue self.attributeType = config.attributeType self.isOptional = config.isOptional - self.attributeValueClassName = config.attributeValueClassName + if let newClassName = config.attributeValueClassName { + self.attributeValueClassName = newClassName + } return } @@ -55,7 +57,9 @@ extension CoreData.NSAttributeDescription: SchemaProperty { let config = primitiveType.coreDataValue self.attributeType = config.attributeType self.isOptional = config.isOptional - self.attributeValueClassName = config.attributeValueClassName + if let newClassName = config.attributeValueClassName { + self.attributeValueClassName = newClassName + } return true } else { diff --git a/Sources/ManagedModels/SchemaCompatibility/NSManagedObjectModel+Data.swift b/Sources/ManagedModels/SchemaCompatibility/NSManagedObjectModel+Data.swift index c395e67..d42b2c4 100644 --- a/Sources/ManagedModels/SchemaCompatibility/NSManagedObjectModel+Data.swift +++ b/Sources/ManagedModels/SchemaCompatibility/NSManagedObjectModel+Data.swift @@ -11,12 +11,6 @@ public extension NSManagedObjectModel { // - encodingVersion // - version - @available(*, deprecated, renamed: "model(for:)", message: - """ - Entities can only be used in one NSManagedObjectModel, use the `model(for:)` - static function to get access to s ahred, cached model. - """ - ) @inlinable convenience init(_ entities: NSEntityDescription..., version: Schema.Version = Version(1, 0, 0)) @@ -25,25 +19,13 @@ public extension NSManagedObjectModel { self.entities = entities } - @available(*, deprecated, renamed: "model(for:)", message: - """ - Entities can only be used in one NSManagedObjectModel, use the `model(for:)` - static function to get access to s ahred, cached model. - """ - ) convenience init(_ types: [ any PersistentModel.Type ], version: Schema.Version = Version(1, 0, 0)) { self.init() - self.entities = SchemaBuilder.shared.lookupAllEntities(for: types) + self.entities = SchemaBuilder().lookupAllEntities(for: types) } - @available(*, deprecated, renamed: "model(for:)", message: - """ - Entities can only be used in one NSManagedObjectModel, use the `model(for:)` - static function to get access to s ahred, cached model. - """ - ) @inlinable convenience init(versionedSchema: any VersionedSchema.Type) { self.init(versionedSchema.models, @@ -56,9 +38,11 @@ public extension NSManagedObjectModel { private let lock = NSLock() private var map = [ Set : NSManagedObjectModel ]() +private let sharedBuilder = SchemaBuilder() public extension NSManagedObjectModel { + /// A cached version of the initializer. static func model(for versionedSchema: VersionedSchema.Type) -> NSManagedObjectModel { @@ -86,7 +70,7 @@ public extension NSManagedObjectModel { if let cachedMOM { mom = cachedMOM } else { mom = NSManagedObjectModel() - mom.entities = SchemaBuilder.shared.lookupAllEntities(for: types) + mom.entities = sharedBuilder.lookupAllEntities(for: types) map[typeIDs] = mom } lock.unlock() diff --git a/Sources/ManagedModels/SchemaGeneration/SchemaBuilder.swift b/Sources/ManagedModels/SchemaGeneration/SchemaBuilder.swift index c5e0ff5..dace94a 100644 --- a/Sources/ManagedModels/SchemaGeneration/SchemaBuilder.swift +++ b/Sources/ManagedModels/SchemaGeneration/SchemaBuilder.swift @@ -27,17 +27,11 @@ import CoreData */ public final class SchemaBuilder { // Notes: - // - this MUST NOT call `.entity` on the model! might recurse w/ lock, this + // - this MUST NOT call `.entity()` on the model! might recurse w/ lock, this // object is the authority! // - there can be multiple entities that use the same name, this spans the // whole type system. E.g. when versioned schemas are used. - - /** - * A shared SchemaBuilder that caches `NSEntityDescription` values for - * ``PersistentModel`` `NSManagedObject`'s. - */ - public static let shared = SchemaBuilder() - + private let lock = NSLock() // TODO: use better lock :-) /// ObjectIdentifier of PersistentModel type to the associated schema. diff --git a/Tests/ManagedModelTests/BasicModelTests.swift b/Tests/ManagedModelTests/BasicModelTests.swift index 90f3fb0..ee8c382 100644 --- a/Tests/ManagedModelTests/BasicModelTests.swift +++ b/Tests/ManagedModelTests/BasicModelTests.swift @@ -16,7 +16,6 @@ final class BasicModelTests: XCTestCase { func testEntityName() throws { let addressType = Fixtures.PersonAddressSchema.Address.self - XCTAssertEqual(addressType._$entity.name, "Address") XCTAssertEqual(addressType.entity().name, "Address") } diff --git a/Tests/ManagedModelTests/CoreDataAssumptionsTests.swift b/Tests/ManagedModelTests/CoreDataAssumptionsTests.swift index f1783f6..3da87a0 100644 --- a/Tests/ManagedModelTests/CoreDataAssumptionsTests.swift +++ b/Tests/ManagedModelTests/CoreDataAssumptionsTests.swift @@ -20,9 +20,9 @@ final class CoreDataAssumptionsTests: XCTestCase { let relationship = NSRelationshipDescription() relationship.name = "addresses" //relationship.destinationEntity = - // Fixtures.PersonAddressSchema.Address._$entity + // Fixtures.PersonAddressSchema.Address.entity() //relationship.inverseRelationship = - // Fixtures.PersonAddressSchema.Address._$entity.relationshipsByName["person"] + // Fixtures.PersonAddressSchema.Address.entity().relationshipsByName["person"] // This just seems to be the default. XCTAssertTrue(relationship.isToMany) @@ -32,9 +32,10 @@ final class CoreDataAssumptionsTests: XCTestCase { let relationship = NSRelationshipDescription() relationship.name = "person" relationship.maxCount = 1 // toOne marker! + #if false // old relationship.destinationEntity = - Fixtures.PersonAddressSchema.Person._$entity - // Yes! This does not work at this point. + Fixtures.PersonAddressSchema.Person.entity() + #endif XCTAssertFalse(relationship.isToMany) } diff --git a/Tests/ManagedModelTests/SchemaGenerationTests.swift b/Tests/ManagedModelTests/SchemaGenerationTests.swift index 05e1532..4ff7919 100644 --- a/Tests/ManagedModelTests/SchemaGenerationTests.swift +++ b/Tests/ManagedModelTests/SchemaGenerationTests.swift @@ -224,7 +224,34 @@ final class SchemaGenerationTests: XCTestCase { XCTAssertEqual(address.attributes.count, 2) } } - + + func testDuplicateMOMGeneration() throws { + try autoreleasepool { + let model1 = NSManagedObjectModel([ + Fixtures.PersonAddressSchema.Person.self + ]) + XCTAssertEqual(model1.entities.count, 2) + + let address = try XCTUnwrap( + model1.entities.first(where: { $0.name == "Address" }) + ) + XCTAssertEqual(address.attributes.count, 2) + } + + // second run + try autoreleasepool { + let model2 = NSManagedObjectModel([ + Fixtures.PersonAddressSchema.Person.self + ]) + XCTAssertEqual(model2.entities.count, 2) + + let address = try XCTUnwrap( + model2.entities.first(where: { $0.name == "Address" }) + ) + XCTAssertEqual(address.attributes.count, 2) + } + } + func testOptionalBackRef() throws { let cache = SchemaBuilder() let schema = NSManagedObjectModel( diff --git a/Tests/ManagedModelTests/Schemas/ExpandedPersonAddressSchema.swift b/Tests/ManagedModelTests/Schemas/ExpandedPersonAddressSchema.swift index d3581f6..a6c442d 100644 --- a/Tests/ManagedModelTests/Schemas/ExpandedPersonAddressSchema.swift +++ b/Tests/ManagedModelTests/Schemas/ExpandedPersonAddressSchema.swift @@ -31,7 +31,7 @@ extension Fixtures { init(firstname: String, lastname: String, addresses: [ Address ]) { // Note: Could do Self.entity! - super.init(entity: Self._$entity, insertInto: nil) + super.init(entity: Self.entity(), insertInto: nil) self.firstname = firstname self.lastname = lastname self.addresses = addresses @@ -42,7 +42,6 @@ extension Fixtures { .init(name: "lastname" , keypath: \Person.lastname), .init(name: "addresses" , keypath: \Person.addresses) ] - public static let _$entity = SchemaBuilder.shared._entity(for: Person.self) public static let _$originalName : String? = nil public static let _$hashModifier : String? = nil } @@ -54,7 +53,7 @@ extension Fixtures { @NSManaged var person : Person init(street: String, appartment: String? = nil, person: Person) { - super.init(entity: Self._$entity, insertInto: nil) + super.init(entity: Self.entity(), insertInto: nil) self.street = street self.appartment = appartment self.person = person @@ -65,7 +64,6 @@ extension Fixtures { .init(name: "appartment" , keypath: \Address.appartment), .init(name: "person" , keypath: \Address.person) ] - public static let _$entity = SchemaBuilder.shared._entity(for: Address.self) public static let _$originalName : String? = nil public static let _$hashModifier : String? = nil } diff --git a/Tests/ManagedModelTests/Schemas/PersonAddressSchemaNoInverse.swift b/Tests/ManagedModelTests/Schemas/PersonAddressSchemaNoInverse.swift index efff698..07d9e7b 100644 --- a/Tests/ManagedModelTests/Schemas/PersonAddressSchemaNoInverse.swift +++ b/Tests/ManagedModelTests/Schemas/PersonAddressSchemaNoInverse.swift @@ -24,7 +24,7 @@ extension Fixtures { var addresses : [ Address ] init(firstname: String, lastname: String, addresses: [ Address ]) { - super.init(entity: Self._$entity, insertInto: nil) + super.init(entity: Self.entity(), insertInto: nil) self.firstname = firstname self.lastname = lastname self.addresses = addresses