Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
helje5 committed Oct 1, 2023
2 parents b4991dc + 921d3e7 commit 4b29c44
Show file tree
Hide file tree
Showing 9 changed files with 331 additions and 94 deletions.
46 changes: 35 additions & 11 deletions Sources/ManagedModels/PersistentModel/PersistentModel+KVC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,23 +64,23 @@ public extension PersistentModel {

@inlinable
func setValue<T>(forKey key: String, to model: T) where T: PersistentModel {
_setValue(forKey: key, to: model)
_setOptionalToOneValue(forKey: key, to: model)
}
@inlinable
func getValue<T>(forKey key: String) -> T where T: PersistentModel {
guard let value : T = _getValue(forKey: key) else {
guard let value : T = _getOptionalToOneValue(forKey: key) else {
fatalError("Non-optional toOne relationship contains nil value?!")
}
return value
}

@inlinable
func setValue<T>(forKey key: String, to model: T?) where T: PersistentModel {
_setValue(forKey: key, to: model)
_setOptionalToOneValue(forKey: key, to: model)
}
@inlinable
func getValue<T>(forKey key: String) -> T? where T: PersistentModel {
_getValue(forKey: key)
_getOptionalToOneValue(forKey: key)
}

// Codable disambiguation
Expand All @@ -89,13 +89,13 @@ public extension PersistentModel {
func setValue<T>(forKey key: String, to model: T)
where T: PersistentModel & Encodable
{
_setValue(forKey: key, to: model)
_setOptionalToOneValue(forKey: key, to: model)
}
@inlinable
func getValue<T>(forKey key: String) -> T
where T: PersistentModel & Encodable
{
guard let value : T = _getValue(forKey: key) else {
guard let value : T = _getOptionalToOneValue(forKey: key) else {
fatalError("Non-optional toOne relationship contains nil value?!")
}
return value
Expand All @@ -105,25 +105,49 @@ public extension PersistentModel {
func setValue<T>(forKey key: String, to model: T?)
where T: PersistentModel & Encodable
{
_setValue(forKey: key, to: model)
_setOptionalToOneValue(forKey: key, to: model)
}
@inlinable
func getValue<T>(forKey key: String) -> T?
where T: PersistentModel & Encodable
{
_getValue(forKey: key)
_getOptionalToOneValue(forKey: key)
}

// Primitives

@inlinable
func _setValue<T>(forKey key: String, to model: T?) where T: PersistentModel {
func _setOptionalToOneValue<T>(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 {
otherCtx.insert(self)
}
else if let ownCtx = self.modelContext, model.modelContext == nil {
ownCtx.insert(model)
}
}
}

willChangeValue(forKey: key); defer { didChangeValue(forKey: key) }
setPrimitiveValue(model, forKey: key)
if let model {
setPrimitiveValue(model, forKey: key)
}
else {
setPrimitiveValue(nil, forKey: key)
}
}

@inlinable
func _getValue<T>(forKey key: String) -> T? where T: PersistentModel {
func _getOptionalToOneValue<T>(forKey key: String) -> T?
where T: PersistentModel
{
willAccessValue(forKey: key); defer { didAccessValue(forKey: key) }
guard let model = primitiveValue(forKey: key) else { return nil }
guard let typed = model as? T else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ 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))
Expand All @@ -19,13 +25,25 @@ 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)
}

@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,
Expand All @@ -39,8 +57,14 @@ public extension NSManagedObjectModel {
private let lock = NSLock()
private var map = [ Set<ObjectIdentifier> : NSManagedObjectModel ]()

extension NSManagedObjectModel {
public extension NSManagedObjectModel {

static func model(for versionedSchema: VersionedSchema.Type)
-> NSManagedObjectModel
{
model(for: versionedSchema.models)
}

/// A cached version of the initializer.
static func model(for types: [ any PersistentModel.Type ])
-> NSManagedObjectModel
Expand All @@ -61,7 +85,8 @@ extension NSManagedObjectModel {
let mom : NSManagedObjectModel
if let cachedMOM { mom = cachedMOM }
else {
mom = NSManagedObjectModel(types)
mom = NSManagedObjectModel()
mom.entities = SchemaBuilder.shared.lookupAllEntities(for: types)
map[typeIDs] = mom
}
lock.unlock()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ extension NSEntityDescription {

case .toMany(collectionType: _, modelType: _):
let relationship = CoreData.NSRelationshipDescription()
relationship.valueType = valueType
relationship.valueType = valueType
relationship.isOptional = valueType is any AnyOptional.Type
fixup(relationship, targetType: valueType, isToOne: false,
meta: propMeta)
assert(relationship.maxCount != 1)
Expand Down Expand Up @@ -174,15 +175,25 @@ extension NSEntityDescription {
// TBD: Rather throw?
if relationship.name.isEmpty { relationship.name = meta.name }

if !isToOne {
if isToOne {
relationship.maxCount = 1 // toOne marker!
}
else {
// Note: In SwiftData arrays are not ordered.
relationship.isOrdered = targetType is NSOrderedSet.Type
assert(relationship.maxCount != 1, "toMany w/ maxCount 1?")
}

if relationship.keypath == nil { relationship.keypath = meta.keypath }
if relationship.valueType == Any.self {
relationship.valueType = targetType
}
if relationship.valueType != Any.self {
relationship.isOptional = relationship.valueType is any AnyOptional.Type
if !isToOne {
relationship.isOrdered = relationship.valueType is NSOrderedSet.Type
}
}
}

private func fixupOrderedSet(_ relationship: NSRelationshipDescription,
Expand Down
25 changes: 21 additions & 4 deletions Sources/ManagedModels/SchemaGeneration/SchemaBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -119,23 +119,31 @@ public final class SchemaBuilder {
entities: inout [ NSEntityDescription ])
{
// Note: This is called recursively
var allFrozen = false
var allFrozen = true

// Create the basic entity and property data
for modelType in modelTypes {
guard !isFrozen(modelType) else { continue }
if isFrozen(modelType) {
if let entity = lookupEntity(modelType) {
entities.append(entity)
continue
}
assertionFailure("Type frozen, but no entity found?")
}

allFrozen = false
if let newEntity = processModel(modelType) {
entities.append(newEntity)
}
}
if allFrozen { return } // all have been processed already

// TBD: The following does too much work, we might only need the
// most of those on the "new models"

// This recurses into `process`, if necessary.
discoverTargetTypes(in: entities, allEntities: &entities)

if allFrozen { return }

// Collect destination entity names in relships based on the modelType!
fillDestinationEntityNamesInRelationships(entities)
Expand All @@ -162,7 +170,16 @@ public final class SchemaBuilder {
continue
}
// This returns nil if the model is already processed.
guard let newEntity = processModel(targetType) else { continue }
guard let newEntity = processModel(targetType) else {
guard let existingEntity = lookupEntity(targetType) else {
assertionFailure("Type marked as processed, but no entity?")
continue
}
if !allEntities.contains(where: { $0 === existingEntity }) {
allEntities.append(existingEntity)
}
continue
}

allEntities.append(newEntity)
newEntities.append(newEntity)
Expand Down
Loading

0 comments on commit 4b29c44

Please sign in to comment.