Skip to content

Commit 4b29c44

Browse files
committed
Merge branch 'develop'
2 parents b4991dc + 921d3e7 commit 4b29c44

File tree

9 files changed

+331
-94
lines changed

9 files changed

+331
-94
lines changed

Sources/ManagedModels/PersistentModel/PersistentModel+KVC.swift

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,23 +64,23 @@ public extension PersistentModel {
6464

6565
@inlinable
6666
func setValue<T>(forKey key: String, to model: T) where T: PersistentModel {
67-
_setValue(forKey: key, to: model)
67+
_setOptionalToOneValue(forKey: key, to: model)
6868
}
6969
@inlinable
7070
func getValue<T>(forKey key: String) -> T where T: PersistentModel {
71-
guard let value : T = _getValue(forKey: key) else {
71+
guard let value : T = _getOptionalToOneValue(forKey: key) else {
7272
fatalError("Non-optional toOne relationship contains nil value?!")
7373
}
7474
return value
7575
}
7676

7777
@inlinable
7878
func setValue<T>(forKey key: String, to model: T?) where T: PersistentModel {
79-
_setValue(forKey: key, to: model)
79+
_setOptionalToOneValue(forKey: key, to: model)
8080
}
8181
@inlinable
8282
func getValue<T>(forKey key: String) -> T? where T: PersistentModel {
83-
_getValue(forKey: key)
83+
_getOptionalToOneValue(forKey: key)
8484
}
8585

8686
// Codable disambiguation
@@ -89,13 +89,13 @@ public extension PersistentModel {
8989
func setValue<T>(forKey key: String, to model: T)
9090
where T: PersistentModel & Encodable
9191
{
92-
_setValue(forKey: key, to: model)
92+
_setOptionalToOneValue(forKey: key, to: model)
9393
}
9494
@inlinable
9595
func getValue<T>(forKey key: String) -> T
9696
where T: PersistentModel & Encodable
9797
{
98-
guard let value : T = _getValue(forKey: key) else {
98+
guard let value : T = _getOptionalToOneValue(forKey: key) else {
9999
fatalError("Non-optional toOne relationship contains nil value?!")
100100
}
101101
return value
@@ -105,25 +105,49 @@ public extension PersistentModel {
105105
func setValue<T>(forKey key: String, to model: T?)
106106
where T: PersistentModel & Encodable
107107
{
108-
_setValue(forKey: key, to: model)
108+
_setOptionalToOneValue(forKey: key, to: model)
109109
}
110110
@inlinable
111111
func getValue<T>(forKey key: String) -> T?
112112
where T: PersistentModel & Encodable
113113
{
114-
_getValue(forKey: key)
114+
_getOptionalToOneValue(forKey: key)
115115
}
116116

117117
// Primitives
118118

119119
@inlinable
120-
func _setValue<T>(forKey key: String, to model: T?) where T: PersistentModel {
120+
func _setOptionalToOneValue<T>(forKey key: String, to model: T?)
121+
where T: PersistentModel
122+
{
123+
#if DEBUG
124+
let relship = Self._$entity.relationshipsByName[key]!
125+
assert(!relship.isToMany, "relship: \(relship)")
126+
#endif
127+
if let model {
128+
if model.modelContext != self.modelContext {
129+
if let otherCtx = model.modelContext, self.modelContext == nil {
130+
otherCtx.insert(self)
131+
}
132+
else if let ownCtx = self.modelContext, model.modelContext == nil {
133+
ownCtx.insert(model)
134+
}
135+
}
136+
}
137+
121138
willChangeValue(forKey: key); defer { didChangeValue(forKey: key) }
122-
setPrimitiveValue(model, forKey: key)
139+
if let model {
140+
setPrimitiveValue(model, forKey: key)
141+
}
142+
else {
143+
setPrimitiveValue(nil, forKey: key)
144+
}
123145
}
124146

125147
@inlinable
126-
func _getValue<T>(forKey key: String) -> T? where T: PersistentModel {
148+
func _getOptionalToOneValue<T>(forKey key: String) -> T?
149+
where T: PersistentModel
150+
{
127151
willAccessValue(forKey: key); defer { didAccessValue(forKey: key) }
128152
guard let model = primitiveValue(forKey: key) else { return nil }
129153
guard let typed = model as? T else {

Sources/ManagedModels/SchemaCompatibility/NSManagedObjectModel+Data.swift

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ public extension NSManagedObjectModel {
1111
// - encodingVersion
1212
// - version
1313

14+
@available(*, deprecated, renamed: "model(for:)", message:
15+
"""
16+
Entities can only be used in one NSManagedObjectModel, use the `model(for:)`
17+
static function to get access to s ahred, cached model.
18+
"""
19+
)
1420
@inlinable
1521
convenience init(_ entities: NSEntityDescription...,
1622
version: Schema.Version = Version(1, 0, 0))
@@ -19,13 +25,25 @@ public extension NSManagedObjectModel {
1925
self.entities = entities
2026
}
2127

28+
@available(*, deprecated, renamed: "model(for:)", message:
29+
"""
30+
Entities can only be used in one NSManagedObjectModel, use the `model(for:)`
31+
static function to get access to s ahred, cached model.
32+
"""
33+
)
2234
convenience init(_ types: [ any PersistentModel.Type ],
2335
version: Schema.Version = Version(1, 0, 0))
2436
{
2537
self.init()
2638
self.entities = SchemaBuilder.shared.lookupAllEntities(for: types)
2739
}
2840

41+
@available(*, deprecated, renamed: "model(for:)", message:
42+
"""
43+
Entities can only be used in one NSManagedObjectModel, use the `model(for:)`
44+
static function to get access to s ahred, cached model.
45+
"""
46+
)
2947
@inlinable
3048
convenience init(versionedSchema: any VersionedSchema.Type) {
3149
self.init(versionedSchema.models,
@@ -39,8 +57,14 @@ public extension NSManagedObjectModel {
3957
private let lock = NSLock()
4058
private var map = [ Set<ObjectIdentifier> : NSManagedObjectModel ]()
4159

42-
extension NSManagedObjectModel {
60+
public extension NSManagedObjectModel {
4361

62+
static func model(for versionedSchema: VersionedSchema.Type)
63+
-> NSManagedObjectModel
64+
{
65+
model(for: versionedSchema.models)
66+
}
67+
4468
/// A cached version of the initializer.
4569
static func model(for types: [ any PersistentModel.Type ])
4670
-> NSManagedObjectModel
@@ -61,7 +85,8 @@ extension NSManagedObjectModel {
6185
let mom : NSManagedObjectModel
6286
if let cachedMOM { mom = cachedMOM }
6387
else {
64-
mom = NSManagedObjectModel(types)
88+
mom = NSManagedObjectModel()
89+
mom.entities = SchemaBuilder.shared.lookupAllEntities(for: types)
6590
map[typeIDs] = mom
6691
}
6792
lock.unlock()

Sources/ManagedModels/SchemaGeneration/NSEntityDescription+Generation.swift

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ extension NSEntityDescription {
130130

131131
case .toMany(collectionType: _, modelType: _):
132132
let relationship = CoreData.NSRelationshipDescription()
133-
relationship.valueType = valueType
133+
relationship.valueType = valueType
134+
relationship.isOptional = valueType is any AnyOptional.Type
134135
fixup(relationship, targetType: valueType, isToOne: false,
135136
meta: propMeta)
136137
assert(relationship.maxCount != 1)
@@ -174,15 +175,25 @@ extension NSEntityDescription {
174175
// TBD: Rather throw?
175176
if relationship.name.isEmpty { relationship.name = meta.name }
176177

177-
if !isToOne {
178+
if isToOne {
179+
relationship.maxCount = 1 // toOne marker!
180+
}
181+
else {
178182
// Note: In SwiftData arrays are not ordered.
179183
relationship.isOrdered = targetType is NSOrderedSet.Type
184+
assert(relationship.maxCount != 1, "toMany w/ maxCount 1?")
180185
}
181186

182187
if relationship.keypath == nil { relationship.keypath = meta.keypath }
183188
if relationship.valueType == Any.self {
184189
relationship.valueType = targetType
185190
}
191+
if relationship.valueType != Any.self {
192+
relationship.isOptional = relationship.valueType is any AnyOptional.Type
193+
if !isToOne {
194+
relationship.isOrdered = relationship.valueType is NSOrderedSet.Type
195+
}
196+
}
186197
}
187198

188199
private func fixupOrderedSet(_ relationship: NSRelationshipDescription,

Sources/ManagedModels/SchemaGeneration/SchemaBuilder.swift

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,23 +119,31 @@ public final class SchemaBuilder {
119119
entities: inout [ NSEntityDescription ])
120120
{
121121
// Note: This is called recursively
122-
var allFrozen = false
122+
var allFrozen = true
123123

124124
// Create the basic entity and property data
125125
for modelType in modelTypes {
126-
guard !isFrozen(modelType) else { continue }
126+
if isFrozen(modelType) {
127+
if let entity = lookupEntity(modelType) {
128+
entities.append(entity)
129+
continue
130+
}
131+
assertionFailure("Type frozen, but no entity found?")
132+
}
133+
127134
allFrozen = false
128135
if let newEntity = processModel(modelType) {
129136
entities.append(newEntity)
130137
}
131138
}
132-
if allFrozen { return } // all have been processed already
133139

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

137143
// This recurses into `process`, if necessary.
138144
discoverTargetTypes(in: entities, allEntities: &entities)
145+
146+
if allFrozen { return }
139147

140148
// Collect destination entity names in relships based on the modelType!
141149
fillDestinationEntityNamesInRelationships(entities)
@@ -162,7 +170,16 @@ public final class SchemaBuilder {
162170
continue
163171
}
164172
// This returns nil if the model is already processed.
165-
guard let newEntity = processModel(targetType) else { continue }
173+
guard let newEntity = processModel(targetType) else {
174+
guard let existingEntity = lookupEntity(targetType) else {
175+
assertionFailure("Type marked as processed, but no entity?")
176+
continue
177+
}
178+
if !allEntities.contains(where: { $0 === existingEntity }) {
179+
allEntities.append(existingEntity)
180+
}
181+
continue
182+
}
166183

167184
allEntities.append(newEntity)
168185
newEntities.append(newEntity)

0 commit comments

Comments
 (0)