11//
22// NSAttributeDescription.swift
3- //
3+ //
44//
55// Created by Alsey Coleman Miller on 8/17/23.
66//
@@ -11,16 +11,56 @@ import CoreData
1111import CoreModel
1212
1313public extension NSAttributeDescription {
14-
14+
15+ /// - Warning: A composite attribute needs an `NSCompositeAttributeDescription`,
16+ /// which this initializer cannot return. Use ``make(attribute:isOptional:)`` instead.
1517 convenience init (
1618 attribute: Attribute ,
1719 isOptional: Bool = true
1820 ) {
1921 self . init ( )
2022 self . name = attribute. id. rawValue
2123 self . isOptional = isOptional
24+ assert ( attribute. type. isComposite == false , " Use NSAttributeDescription.make(attribute:) for composite attributes " )
2225 self . attributeType = . init( attributeType: attribute. type)
2326 }
2427}
2528
29+ internal extension NSAttributeDescription {
30+
31+ /// Build the CoreData property description for a CoreModel attribute.
32+ ///
33+ /// A composite attribute produces an `NSCompositeAttributeDescription`, which is a
34+ /// different class and so cannot come from a `convenience init` on this one.
35+ ///
36+ /// - Note: CoreData's two other constraints on elements hold by construction here:
37+ /// an element can never be a relationship (``Attribute`` has no relationship type),
38+ /// and the element tree is a finite value, so it cannot be recursive.
39+ static func make( attribute: Attribute , isOptional: Bool = true ) throws -> NSAttributeDescription {
40+ guard case let . composite( elements) = attribute. type else {
41+ return NSAttributeDescription ( attribute: attribute, isOptional: isOptional)
42+ }
43+ guard elements. isEmpty == false else {
44+ throw CoreDataModelError . emptyCompositeAttribute ( attribute. id)
45+ }
46+ guard #available( macOS 14 , iOS 17 , tvOS 17 , watchOS 10 , * ) else {
47+ throw CoreDataModelError . compositeAttributesUnavailable ( " " , attribute. id)
48+ }
49+ let description = NSCompositeAttributeDescription ( )
50+ description. name = attribute. id. rawValue
51+ description. isOptional = isOptional
52+ description. attributeType = . composite
53+ // - Note: Elements are always optional. CoreData raises a "missing mandatory
54+ // data" validation fault on save when a non-optional element is absent, and
55+ // `momc` does not diagnose it at build time.
56+ var children = [ NSAttributeDescription] ( )
57+ children. reserveCapacity ( elements. count)
58+ for element in elements {
59+ children. append ( try make ( attribute: element, isOptional: true ) )
60+ }
61+ description. elements = children
62+ return description
63+ }
64+ }
65+
2666#endif
0 commit comments