Custom schema mapping with copyEntity fails with renaming ids #255

Closed
opened 2025-12-29 15:27:40 +01:00 by adam · 2 comments
Owner

Originally created by @iby on GitHub (Jan 13, 2019).

I'm coming across a case with custom mapping provider that fails due to use of renaming identifiers in source attributes. A simplified scenario:

  • Migration from v1 to v2 to v3.
  • v1 has oldFoo attribute.
  • v2 has newFoo attribute with fooOld renaming id for lightweight migration from v1.
  • Migration from v2 to v3 fails because explicitly unwrapped source attribute doesn't exist…

I believe it narrows down to CustomSchemaMappingProvider.swift#L425 line:

let sourceAttributes = sourceEntity.cs_resolvedAttributeRenamingIdentities()
let destinationAttributes = destinationEntity.cs_resolvedAttributeRenamingIdentities()
var attributeMappings: [NSPropertyMapping] = []
for (renamingIdentifier, destination) in destinationAttributes {
    
    // 👇 The part goes down!
    let sourceAttribute = sourceAttributes[renamingIdentifier]!.attribute
    let destinationAttribute = destination.attribute
    let propertyMapping = NSPropertyMapping()
    propertyMapping.name = destinationAttribute.name
    propertyMapping.valueExpression = NSExpression(format: "FUNCTION($\(NSMigrationSourceObjectKey), \"\(#selector(NSManagedObject.value(forKey:)))\", \"\(sourceAttribute.name)\")")
    attributeMappings.append(propertyMapping)
}
return attributeMappings

The call above to cs_resolvedAttributeRenamingIdentities() returns source attributes dictionary organized by renaming id when they are present, or by name otherwise, but really should return them organized by attribute name only. In other words, instead of resolving renaming source attributes for v2 as ["newFoo": …] it returns them as ["oldFoo": …], but "oldFoo" isn't available in source entity v2.

Originally created by @iby on GitHub (Jan 13, 2019). I'm coming across a case with custom mapping provider that fails due to use of renaming identifiers in source attributes. A simplified scenario: - Migration from v1 to v2 to v3. - v1 has `oldFoo` attribute. - v2 has `newFoo` attribute with `fooOld` renaming id for lightweight migration from v1. - Migration from v2 to v3 fails because explicitly unwrapped source attribute doesn't exist… I believe it narrows down to [CustomSchemaMappingProvider.swift#L425](https://github.com/JohnEstropia/CoreStore/blob/45e110755dd5ac3af201b9d8ff16f2af909f6539/Sources/CustomSchemaMappingProvider.swift#L425) line: ```swift let sourceAttributes = sourceEntity.cs_resolvedAttributeRenamingIdentities() let destinationAttributes = destinationEntity.cs_resolvedAttributeRenamingIdentities() var attributeMappings: [NSPropertyMapping] = [] for (renamingIdentifier, destination) in destinationAttributes { // 👇 The part goes down! let sourceAttribute = sourceAttributes[renamingIdentifier]!.attribute let destinationAttribute = destination.attribute let propertyMapping = NSPropertyMapping() propertyMapping.name = destinationAttribute.name propertyMapping.valueExpression = NSExpression(format: "FUNCTION($\(NSMigrationSourceObjectKey), \"\(#selector(NSManagedObject.value(forKey:)))\", \"\(sourceAttribute.name)\")") attributeMappings.append(propertyMapping) } return attributeMappings ``` The call above to `cs_resolvedAttributeRenamingIdentities()` returns source attributes dictionary organized by renaming id when they are present, or by name otherwise, but really should return them organized by attribute name only. In other words, instead of resolving renaming source attributes for v2 as `["newFoo": …]` it returns them as `["oldFoo": …]`, but `"oldFoo"` isn't available in source entity v2.
adam closed this issue 2025-12-29 15:27:40 +01:00
Author
Owner

@iby commented on GitHub (Jan 13, 2019):

This also applies to .transformEntity. The #301 PR addresses both cases. The current workaround is to use manual .transformEntity with the following transformation block:

.transformEntity(sourceEntity: "…", destinationEntity: "…", transformer: { (sourceObject: UnsafeSourceObject, createDestinationObject: () -> UnsafeDestinationObject) in
    let destinationObject: UnsafeDestinationObject = createDestinationObject()
    destinationObject.enumerateAttributes({ (destinationAttribute, sourceAttribute) in
        destinationObject[destinationAttribute] = sourceObject[sourceAttribute ?? destinationAttribute]
    })
})
@iby commented on GitHub (Jan 13, 2019): This also applies to `.transformEntity`. The #301 PR addresses both cases. The current workaround is to use manual `.transformEntity` with the following transformation block: ```swift .transformEntity(sourceEntity: "…", destinationEntity: "…", transformer: { (sourceObject: UnsafeSourceObject, createDestinationObject: () -> UnsafeDestinationObject) in let destinationObject: UnsafeDestinationObject = createDestinationObject() destinationObject.enumerateAttributes({ (destinationAttribute, sourceAttribute) in destinationObject[destinationAttribute] = sourceObject[sourceAttribute ?? destinationAttribute] }) }) ```
Author
Owner

@JohnEstropia commented on GitHub (Jan 15, 2019):

I'll comment on #301 if there's anything that comes up. Thanks again!

@JohnEstropia commented on GitHub (Jan 15, 2019): I'll comment on #301 if there's anything that comes up. Thanks again!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/CoreStore#255