Skip to content

Commit 621e0b8

Browse files
Support for relationships
1 parent 6cfcb20 commit 621e0b8

15 files changed

Lines changed: 1655 additions & 547 deletions
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Created by Axel Ancona Esselmann on 3/22/24.
2+
//
3+
4+
import Foundation
5+
6+
public struct AttributeProperties {
7+
public var defaultValue: Any?
8+
public init(defaultValue: Any? = nil) {
9+
self.defaultValue = defaultValue
10+
}
11+
}

Sources/ProgrammaticCoreData/EntityDescriptionAttribute.swift

Lines changed: 0 additions & 91 deletions
This file was deleted.
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// Created by Axel Ancona Esselmann on 3/22/24.
2+
//
3+
4+
import CoreData
5+
6+
public extension EntityDescriptionAttribute {
7+
static func relationship<InverseCoreDataType, InverseT>(
8+
_ keyPath: KeyPath<CoreDataEntity, T>,
9+
inverse: KeyPath<InverseCoreDataType, InverseT>,
10+
is type: RelationshipProperties.RelationshipType = .toOne,
11+
deleteRule: NSDeleteRule = .noActionDeleteRule
12+
) -> Self
13+
where
14+
CoreDataEntity: SelfDescribingCoreDataEntity,
15+
InverseCoreDataType: SelfDescribingCoreDataEntity
16+
{
17+
.relationship(keyPath, RelationshipProperties(
18+
inverse: inverse,
19+
deleteRule: deleteRule,
20+
relationshipType: type
21+
))
22+
}
23+
24+
// TODO: allow to pass in min/max.
25+
static func relationship<InverseCoreDataType, InverseT>(
26+
_ keyPath: KeyPath<CoreDataEntity, NSSet?>,
27+
inverse: KeyPath<InverseCoreDataType, InverseT>,
28+
deleteRule: NSDeleteRule = .noActionDeleteRule
29+
) -> Self
30+
where
31+
CoreDataEntity: SelfDescribingCoreDataEntity,
32+
InverseCoreDataType: SelfDescribingCoreDataEntity,
33+
T == NSSet?
34+
{
35+
.relationship(keyPath, RelationshipProperties(
36+
inverse: inverse,
37+
deleteRule: deleteRule,
38+
relationshipType: .toMany
39+
))
40+
}
41+
42+
// TODO: allow to pass in min/max.
43+
static func relationship<InverseCoreDataType, InverseT>(
44+
_ keyPath: KeyPath<CoreDataEntity, NSSet>,
45+
inverse: KeyPath<InverseCoreDataType, InverseT>,
46+
is type: RelationshipProperties.RelationshipType = .toOne,
47+
deleteRule: NSDeleteRule = .noActionDeleteRule
48+
) -> Self
49+
where
50+
CoreDataEntity: SelfDescribingCoreDataEntity,
51+
InverseCoreDataType: SelfDescribingCoreDataEntity,
52+
T == NSSet
53+
{
54+
.relationship(keyPath, RelationshipProperties(
55+
inverse: inverse,
56+
deleteRule: deleteRule,
57+
relationshipType: .toMany
58+
))
59+
}
60+
61+
static func undefined(_ keyPath: KeyPath<CoreDataEntity, T>, defaultValue: Any? = nil) -> Self {
62+
.undefined(keyPath, AttributeProperties(defaultValue: defaultValue))
63+
}
64+
static func integer16(_ keyPath: KeyPath<CoreDataEntity, T>, defaultValue: Int16? = nil) -> Self {
65+
.integer16(keyPath, AttributeProperties(defaultValue: defaultValue))
66+
}
67+
static func integer32(_ keyPath: KeyPath<CoreDataEntity, T>, defaultValue: Int32? = nil) -> Self {
68+
.integer32(keyPath, AttributeProperties(defaultValue: defaultValue))
69+
}
70+
static func integer64(_ keyPath: KeyPath<CoreDataEntity, T>, defaultValue: Int64? = nil) -> Self {
71+
.integer64(keyPath, AttributeProperties(defaultValue: defaultValue))
72+
}
73+
static func decimal(_ keyPath: KeyPath<CoreDataEntity, T>, defaultValue: Decimal? = nil) -> Self {
74+
.decimal(keyPath, AttributeProperties(defaultValue: defaultValue))
75+
}
76+
static func double(_ keyPath: KeyPath<CoreDataEntity, T>, defaultValue: Double? = nil) -> Self {
77+
.double(keyPath, AttributeProperties(defaultValue: defaultValue))
78+
}
79+
static func float(_ keyPath: KeyPath<CoreDataEntity, T>, defaultValue: Float? = nil) -> Self {
80+
.float(keyPath, AttributeProperties(defaultValue: defaultValue))
81+
}
82+
static func string(_ keyPath: KeyPath<CoreDataEntity, T>, defaultValue: String? = nil) -> Self {
83+
.string(keyPath, AttributeProperties(defaultValue: defaultValue))
84+
}
85+
static func boolean(_ keyPath: KeyPath<CoreDataEntity, T>, defaultValue: Bool? = nil) -> Self {
86+
.boolean(keyPath, AttributeProperties(defaultValue: defaultValue))
87+
}
88+
static func date(_ keyPath: KeyPath<CoreDataEntity, T>, defaultValue: Date? = nil) -> Self {
89+
.date(keyPath, AttributeProperties(defaultValue: defaultValue))
90+
}
91+
static func binaryData(_ keyPath: KeyPath<CoreDataEntity, T>, defaultValue: Data? = nil) -> Self {
92+
.binaryData(keyPath, AttributeProperties(defaultValue: defaultValue))
93+
}
94+
static func uuid(_ keyPath: KeyPath<CoreDataEntity, T>, defaultValue: UUID? = nil) -> Self {
95+
.uuid(keyPath, AttributeProperties(defaultValue: defaultValue))
96+
}
97+
static func uri(_ keyPath: KeyPath<CoreDataEntity, T>, defaultValue: Any? = nil) -> Self {
98+
.uri(keyPath, AttributeProperties(defaultValue: defaultValue))
99+
}
100+
static func transformable(_ keyPath: KeyPath<CoreDataEntity, T>, defaultValue: Any? = nil) -> Self {
101+
.transformable(keyPath, AttributeProperties(defaultValue: defaultValue))
102+
}
103+
static func objectID(_ keyPath: KeyPath<CoreDataEntity, T>, defaultValue: Any? = nil) -> Self {
104+
.objectID(keyPath, AttributeProperties(defaultValue: defaultValue))
105+
}
106+
static func composite(_ keyPath: KeyPath<CoreDataEntity, T>, defaultValue: Any? = nil) -> Self {
107+
.composite(keyPath, AttributeProperties(defaultValue: defaultValue))
108+
}
109+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// Created by Axel Ancona Esselmann on 2/24/24.
2+
//
3+
4+
import CoreData
5+
6+
public enum EntityDescriptionAttribute<CoreDataEntity, T> {
7+
8+
enum Properties {
9+
case attribute(AttributeProperties)
10+
case relationship(RelationshipProperties)
11+
}
12+
13+
case undefined(KeyPath<CoreDataEntity, T>, AttributeProperties)
14+
case integer16(KeyPath<CoreDataEntity, T>, AttributeProperties)
15+
case integer32(KeyPath<CoreDataEntity, T>, AttributeProperties)
16+
case integer64(KeyPath<CoreDataEntity, T>, AttributeProperties)
17+
case decimal(KeyPath<CoreDataEntity, T>, AttributeProperties)
18+
case double(KeyPath<CoreDataEntity, T>, AttributeProperties)
19+
case float(KeyPath<CoreDataEntity, T>, AttributeProperties)
20+
case string(KeyPath<CoreDataEntity, T>, AttributeProperties)
21+
case boolean(KeyPath<CoreDataEntity, T>, AttributeProperties)
22+
case date(KeyPath<CoreDataEntity, T>, AttributeProperties)
23+
case binaryData(KeyPath<CoreDataEntity, T>, AttributeProperties)
24+
case uuid(KeyPath<CoreDataEntity, T>, AttributeProperties)
25+
case uri(KeyPath<CoreDataEntity, T>, AttributeProperties)
26+
case transformable(KeyPath<CoreDataEntity, T>, AttributeProperties)
27+
case objectID(KeyPath<CoreDataEntity, T>, AttributeProperties)
28+
case composite(KeyPath<CoreDataEntity, T>, AttributeProperties)
29+
case relationship(KeyPath<CoreDataEntity, T>, RelationshipProperties)
30+
31+
internal var attributeType: NSAttributeType? {
32+
NSAttributeType(self)
33+
}
34+
35+
internal var keyPath: KeyPath<CoreDataEntity, T> {
36+
switch self {
37+
case
38+
.undefined(let keyPath, _),
39+
.integer16(let keyPath, _),
40+
.integer32(let keyPath, _),
41+
.integer64(let keyPath, _),
42+
.decimal(let keyPath, _),
43+
.double(let keyPath, _),
44+
.float(let keyPath, _),
45+
.string(let keyPath, _),
46+
.boolean(let keyPath, _),
47+
.date(let keyPath, _),
48+
.binaryData(let keyPath, _),
49+
.uuid(let keyPath, _),
50+
.uri(let keyPath, _),
51+
.transformable(let keyPath, _),
52+
.objectID(let keyPath, _),
53+
.composite(let keyPath, _),
54+
.relationship(let keyPath, _):
55+
return keyPath
56+
}
57+
}
58+
59+
internal var properties: Properties {
60+
switch self {
61+
case .relationship(_, let properties):
62+
return .relationship(properties)
63+
case
64+
.undefined(_, let properties),
65+
.integer16(_, let properties),
66+
.integer32(_, let properties),
67+
.integer64(_, let properties),
68+
.decimal(_, let properties),
69+
.double(_, let properties),
70+
.float(_, let properties),
71+
.string(_, let properties),
72+
.boolean(_, let properties),
73+
.date(_, let properties),
74+
.binaryData(_, let properties),
75+
.uuid(_, let properties),
76+
.uri(_, let properties),
77+
.transformable(_, let properties),
78+
.objectID(_, let properties),
79+
.composite(_, let properties):
80+
return .attribute(properties)
81+
}
82+
}
83+
84+
public var nsPropertyDescription: NSPropertyDescription {
85+
let name = NSExpression(forKeyPath: keyPath).keyPath
86+
switch properties {
87+
case .attribute(let attributeProperties) where attributeType != nil:
88+
return NSAttributeDescription(
89+
name: name,
90+
type: attributeType!,
91+
defaultValue: attributeProperties.defaultValue
92+
)
93+
case .relationship(let relationshipProperties):
94+
let description = NSRelationshipDescription(
95+
name: name,
96+
deleteRule: relationshipProperties.deleteRule,
97+
relationshipType: relationshipProperties.relationshipType
98+
)
99+
RelationshipRegistry.shared
100+
.register(
101+
description: description,
102+
keyPath: keyPath.typeErased,
103+
inverseKeyPath: relationshipProperties.inverse
104+
)
105+
return description
106+
default:
107+
fatalError()
108+
}
109+
}
110+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Created by Axel Ancona Esselmann on 3/22/24.
2+
//
3+
4+
import CoreData
5+
6+
extension NSAttributeType {
7+
init?<CoreDataEntity, T>(_ entityDescriptionAttribute: EntityDescriptionAttribute<CoreDataEntity, T>) {
8+
switch entityDescriptionAttribute {
9+
case .undefined:
10+
self = .undefinedAttributeType
11+
case .integer16:
12+
self = .integer16AttributeType
13+
case .integer32:
14+
self = .integer32AttributeType
15+
case .integer64:
16+
self = .integer64AttributeType
17+
case .decimal:
18+
self = .decimalAttributeType
19+
case .double:
20+
self = .doubleAttributeType
21+
case .float:
22+
self = .floatAttributeType
23+
case .string:
24+
self = .stringAttributeType
25+
case .boolean:
26+
self = .booleanAttributeType
27+
case .date:
28+
self = .dateAttributeType
29+
case .binaryData:
30+
self = .binaryDataAttributeType
31+
case .uuid:
32+
self = .UUIDAttributeType
33+
case .uri:
34+
self = .URIAttributeType
35+
case .transformable:
36+
self = .transformableAttributeType
37+
case .objectID:
38+
self = .objectIDAttributeType
39+
case .composite:
40+
self = .compositeAttributeType
41+
case .relationship:
42+
return nil
43+
}
44+
}
45+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Created by Axel Ancona Esselmann on 3/22/24.
2+
//
3+
4+
import Foundation
5+
6+
extension KeyPath {
7+
var propertyAsString: String {
8+
NSExpression(forKeyPath: self).keyPath
9+
}
10+
11+
var rootType: Any.Type {
12+
type(of: self).rootType
13+
}
14+
15+
var typeErased: TypeErasedKeyPath {
16+
.init(self)
17+
}
18+
}

0 commit comments

Comments
 (0)