Skip to content

Commit 921d3e7

Browse files
committed
Fix toOne's w/ explicit Relationships
This fixes issue #19.
1 parent cda33e5 commit 921d3e7

File tree

5 files changed

+82
-17
lines changed

5 files changed

+82
-17
lines changed

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,

Tests/ManagedModelTests/SchemaGenerationTests.swift

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ final class SchemaGenerationTests: XCTestCase {
9898
XCTAssertFalse (lastname.isRelationship)
9999
XCTAssertTrue (toAddresses.isRelationship)
100100
XCTAssertFalse (toAddresses.isToOneRelationship)
101+
XCTAssertTrue (toAddresses.isToMany)
101102
XCTAssertEqual (toAddresses.destination, "Address")
102103
XCTAssertNotNil(toAddresses.destinationEntity)
103104

@@ -106,6 +107,7 @@ final class SchemaGenerationTests: XCTestCase {
106107
let toPerson = try XCTUnwrap(address.relationshipsByName["person"])
107108
XCTAssertTrue (toPerson.isRelationship)
108109
XCTAssertTrue (toPerson.isToOneRelationship)
110+
XCTAssertFalse(toPerson.isToMany)
109111
XCTAssertEqual(toPerson.destination, "Person")
110112

111113
XCTAssertTrue(toAddresses.destinationEntity === address)
@@ -222,4 +224,43 @@ final class SchemaGenerationTests: XCTestCase {
222224
XCTAssertEqual(address.attributes.count, 2)
223225
}
224226
}
227+
228+
func testOptionalBackRef() throws {
229+
let cache = SchemaBuilder()
230+
let schema = NSManagedObjectModel(
231+
versionedSchema: Fixtures.PersonAddressOptionalToOneSchema.self,
232+
schemaCache: cache
233+
)
234+
235+
XCTAssertEqual(schema.entities.count, 2)
236+
XCTAssertEqual(schema.entitiesByName.count, 2)
237+
238+
let person = try XCTUnwrap(schema.entitiesByName["Person"])
239+
let address = try XCTUnwrap(schema.entitiesByName["Address"])
240+
241+
XCTAssertTrue(person.attributes.isEmpty)
242+
XCTAssertEqual(person.relationships.count, 1)
243+
let toAddresses = try XCTUnwrap(person.relationshipsByName["addresses"])
244+
XCTAssertTrue (toAddresses.isRelationship)
245+
XCTAssertFalse (toAddresses.isToOneRelationship)
246+
XCTAssertFalse (toAddresses.isOptional)
247+
XCTAssertEqual (toAddresses.destination, "Address")
248+
XCTAssertNotNil(toAddresses.destinationEntity)
249+
250+
XCTAssertTrue(address.attributes.isEmpty)
251+
XCTAssertEqual(address.relationships.count, 1)
252+
let toPerson = try XCTUnwrap(address.relationshipsByName["person"])
253+
XCTAssertTrue (toPerson.isRelationship)
254+
XCTAssertTrue (toPerson.isOptional)
255+
XCTAssertTrue (toPerson.isToOneRelationship)
256+
XCTAssertEqual(toPerson.destination, "Person")
257+
258+
XCTAssertTrue(toAddresses.destinationEntity === address)
259+
XCTAssertTrue(toPerson .destinationEntity === person)
260+
261+
XCTAssertEqual(toPerson .inverseName, "addresses")
262+
XCTAssertEqual(toAddresses.inverseName, "person")
263+
XCTAssertTrue (toAddresses.keypath == toPerson.inverseKeyPath)
264+
XCTAssertTrue (toAddresses.inverseKeyPath == toPerson.keypath)
265+
}
225266
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//
2+
// Created by Helge Heß.
3+
// Copyright © 2023 ZeeZide GmbH.
4+
//
5+
6+
import ManagedModels
7+
8+
extension Fixtures {
9+
10+
enum PersonAddressOptionalToOneSchema: VersionedSchema {
11+
static var models : [ any PersistentModel.Type ] = [
12+
Person.self,
13+
Address.self
14+
]
15+
16+
public static let versionIdentifier = Schema.Version(0, 1, 0)
17+
18+
19+
@Model class Person: NSManagedObject {
20+
var addresses : Set<Address> // [ Address ]
21+
}
22+
23+
@Model class Address: NSManagedObject {
24+
@Relationship(deleteRule: .nullify, originalName: "PERSON")
25+
var person : Person?
26+
}
27+
}
28+
}

Tests/ManagedModelTests/Schemas/PersonAddressSchema.swift

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,9 @@ extension Fixtures {
2121
@Model
2222
final class Person: NSManagedObject {
2323

24-
// TBD: Why are the inits required? *** NEED TO FIGURE THIS OUT
2524
var firstname : String
2625
var lastname : String
2726
var addresses : Set<Address> // [ Address ]
28-
29-
#if false
30-
init(firstname: String, lastname: String, addresses: [ Address ]) {
31-
self.init() // this does not work
32-
self.firstname = firstname
33-
self.lastname = lastname
34-
self.addresses = addresses
35-
}
36-
#endif
3727
}
3828

3929
@Model

Tests/ManagedModelTests/Schemas/PersonAddressSchemaNoInverse.swift

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,10 @@ extension Fixtures {
1919
@Model
2020
final class Person: NSManagedObject, PersistentModel {
2121

22-
// TBD: Why are the inits required? *** NEED TO FIGURE THIS OUT
2322
var firstname : String
2423
var lastname : String
2524
var addresses : [ Address ]
2625

27-
// init() is a convenience initializer, it looks up the the entity for the
28-
// object?
29-
// Can we generate inits?
30-
3126
init(firstname: String, lastname: String, addresses: [ Address ]) {
3227
super.init(entity: Self._$entity, insertInto: nil)
3328
self.firstname = firstname

0 commit comments

Comments
 (0)