Skip to content

Commit 885e546

Browse files
committed
Merge branch 'develop'
2 parents 2ebc35a + 6c1ef00 commit 885e546

12 files changed

+444
-122
lines changed

Sources/ManagedModels/PersistentModel/PersistentModel+KVC.swift

+47-15
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public extension PersistentModel {
5252

5353
// MARK: - Transformable
5454
public extension PersistentModel {
55-
55+
5656
@inlinable
5757
func setTransformableValue(forKey key: String, to value: Any) {
5858
willChangeValue(forKey: key); defer { didChangeValue(forKey: key) }
@@ -64,6 +64,23 @@ public extension PersistentModel {
6464
willAccessValue(forKey: key); defer { didAccessValue(forKey: key) }
6565
return primitiveValue(forKey: key) as! T
6666
}
67+
68+
@inlinable
69+
func setTransformableValue(forKey key: String, to value: Any?) {
70+
willChangeValue(forKey: key); defer { didChangeValue(forKey: key) }
71+
setPrimitiveValue(value, forKey: key)
72+
}
73+
74+
@inlinable
75+
func getTransformableValue<T>(forKey key: String) -> T
76+
where T: AnyOptional
77+
{
78+
willAccessValue(forKey: key); defer { didAccessValue(forKey: key) }
79+
guard let value = primitiveValue(forKey: key) else {
80+
return .noneValue
81+
}
82+
return (value as? T) ?? .noneValue
83+
}
6784
}
6885

6986

@@ -223,6 +240,28 @@ public extension PersistentModel {
223240
}
224241
return wrapped
225242
}
243+
244+
// Overloads for RawRepresentables that are ALSO Codable
245+
246+
@inlinable
247+
func setValue<T>(forKey key: String, to value: T)
248+
where T: RawRepresentable & Codable,
249+
T.RawValue: Codable & CoreDataPrimitiveValue
250+
{
251+
setValue(forKey: key, to: value.rawValue)
252+
}
253+
254+
@inlinable
255+
func getValue<T>(forKey key: String) -> T
256+
where T: RawRepresentable & Codable,
257+
T.RawValue: Codable & CoreDataPrimitiveValue
258+
{
259+
let rawValue : T.RawValue = getValue(forKey: key)
260+
guard let wrapped = T.init(rawValue: rawValue) else {
261+
fatalError("Could not wrap raw value \(rawValue) for \(key)")
262+
}
263+
return wrapped
264+
}
226265
}
227266

228267
// MARK: - Codable
@@ -232,14 +271,14 @@ public extension PersistentModel {
232271

233272
func setValue<T>(forKey key: String, to value: T) where T: Codable {
234273
willChangeValue(forKey: key); defer { didChangeValue(forKey: key) }
235-
setPrimitiveValue(CodableBox<T>(value), forKey: key)
274+
setPrimitiveValue(value, forKey: key)
236275
}
237276

238277
func setValue<T>(forKey key: String, to value: T)
239278
where T: Codable & AnyOptional
240279
{
241280
willChangeValue(forKey: key); defer { didChangeValue(forKey: key) }
242-
if value.isSome { setPrimitiveValue(CodableBox<T>(value), forKey: key) }
281+
if value.isSome { setPrimitiveValue(value, forKey: key) }
243282
else { setPrimitiveValue(nil, forKey: key) }
244283
}
245284

@@ -249,10 +288,7 @@ public extension PersistentModel {
249288
fatalError("No box found for non-optional Codable value for key \(key)?")
250289
}
251290

252-
if let box = value as? CodableBox<T> {
253-
guard let value = box.value else {
254-
fatalError("Box has no value for non-optional Codable for key \(key)?")
255-
}
291+
if let value = value as? T {
256292
return value
257293
}
258294

@@ -266,17 +302,13 @@ public extension PersistentModel {
266302
}
267303
}
268304

269-
guard let value = value as? T else {
270-
fatalError("Unexpected value for key \(key)? \(value)")
271-
}
272-
assertionFailure("Codable value is directly stored? \(value)")
273-
return value
305+
fatalError("Codable value type doesn't match? \(value)")
274306
}
275307

276308
func getValue<T>(forKey key: String) -> T where T: Codable & AnyOptional {
277309
willAccessValue(forKey: key); defer { didAccessValue(forKey: key) }
278310
guard let value = primitiveValue(forKey: key) else { return .noneValue }
279-
if let box = value as? CodableBox<T> { return box.value ?? .noneValue }
311+
if let value = value as? T { return value }
280312

281313
if let data = value as? Data {
282314
assertionFailure("Unexpected Data as primitive!")
@@ -291,7 +323,7 @@ public extension PersistentModel {
291323
guard let value = value as? T else {
292324
fatalError("Unexpected value for key \(key)? \(value)")
293325
}
294-
assertionFailure("Codable value is directly stored? \(value)")
295-
return value
326+
assertionFailure("Codable value type doesn't match? \(value)")
327+
return .noneValue
296328
}
297329
}

Sources/ManagedModels/SchemaCompatibility/CodableBox.swift

-83
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//
2+
// Created by Helge Heß.
3+
// Copyright © 2023 ZeeZide GmbH.
4+
//
5+
6+
import Foundation
7+
import CoreData
8+
9+
final class CodableTransformer<T: Codable>: ValueTransformer {
10+
11+
#if false
12+
override class func transformedValueClass() -> AnyClass {
13+
T.self // doesn't work
14+
}
15+
#endif
16+
override class func allowsReverseTransformation() -> Bool { true }
17+
18+
override func transformedValue(_ value: Any?) -> Any? {
19+
// value is the box
20+
guard let value else { return nil }
21+
guard let typed = value as? T else {
22+
assertionFailure("Value to be transformed is not the right type? \(value)")
23+
return nil
24+
}
25+
do {
26+
return try JSONEncoder().encode(typed)
27+
}
28+
catch {
29+
assertionFailure("Could not encode JSON value of property? \(error)")
30+
return nil
31+
}
32+
}
33+
34+
override func reverseTransformedValue(_ value: Any?) -> Any? {
35+
guard let value else { return nil }
36+
guard let data = value as? Data else {
37+
assert(value is Data, "Reverse value is not `Data`?")
38+
return nil
39+
}
40+
do {
41+
return try JSONDecoder().decode(T.self, from: data)
42+
}
43+
catch {
44+
assertionFailure("Could not decode JSON value of property? \(error)")
45+
return nil
46+
}
47+
}
48+
}

Sources/ManagedModels/SchemaCompatibility/NSAttributeDescription+Data.swift

+33-11
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ extension CoreData.NSAttributeDescription: SchemaProperty {
4040

4141
if let primitiveType = newValue as? CoreDataPrimitiveValue.Type {
4242
let config = primitiveType.coreDataValue
43-
self.attributeType = config.attributeType
44-
self.isOptional = config.isOptional
43+
self.attributeType = config.attributeType
44+
self.isOptional = config.isOptional
4545
if let newClassName = config.attributeValueClassName {
4646
self.attributeValueClassName = newClassName
4747
}
@@ -55,8 +55,8 @@ extension CoreData.NSAttributeDescription: SchemaProperty {
5555
let rawType = type.RawValue.self
5656
if let primitiveType = rawType as? CoreDataPrimitiveValue.Type {
5757
let config = primitiveType.coreDataValue
58-
self.attributeType = config.attributeType
59-
self.isOptional = config.isOptional
58+
self.attributeType = config.attributeType
59+
self.isOptional = config.isOptional
6060
if let newClassName = config.attributeValueClassName {
6161
self.attributeValueClassName = newClassName
6262
}
@@ -75,12 +75,14 @@ extension CoreData.NSAttributeDescription: SchemaProperty {
7575
self.isOptional = newValue is any AnyOptional.Type
7676

7777
func setValueClassName<T: Codable>(for type: T.Type) {
78-
self.attributeValueClassName = NSStringFromClass(CodableBox<T>.self)
78+
#if false // doesn't work
79+
self.attributeValueClassName = NSStringFromClass(T.self)
80+
#endif
7981

80-
let name = NSStringFromClass(CodableBox<T>.Transformer.self)
82+
let name = NSStringFromClass(CodableTransformer<T>.self)
8183
if !ValueTransformer.valueTransformerNames().contains(.init(name)) {
8284
// no access to valueTransformerForName?
83-
let transformer = CodableBox<T>.Transformer()
85+
let transformer = CodableTransformer<T>()
8486
ValueTransformer
8587
.setValueTransformer(transformer, forName: .init(name))
8688
}
@@ -92,6 +94,15 @@ extension CoreData.NSAttributeDescription: SchemaProperty {
9294
return
9395
}
9496

97+
if let valueTransformerName = valueTransformerName {
98+
self.attributeType = .transformableAttributeType
99+
self.isOptional = newValue is any AnyOptional.Type
100+
101+
self.attributeValueClassName = NSStringFromClass(NSObject.self)
102+
assert(ValueTransformer.valueTransformerNames().contains(.init(valueTransformerName)))
103+
return
104+
}
105+
95106
// TBD:
96107
// undefinedAttributeType = 0
97108
// transformableAttributeType = 1800
@@ -168,9 +179,8 @@ public extension NSAttributeDescription {
168179

169180
assert(valueTransformerName == nil)
170181
valueTransformerName = nil
171-
if valueType != Any.self { self.valueType = valueType }
172-
173182
setOptions(options)
183+
if valueType != Any.self { self.valueType = valueType }
174184
}
175185
}
176186

@@ -197,10 +207,22 @@ private extension NSAttributeDescription {
197207

198208
case .transformableByName(let name):
199209
assert(valueTransformerName == nil)
210+
attributeType = .transformableAttributeType
200211
valueTransformerName = name
212+
if !ValueTransformer.valueTransformerNames().contains(.init(name)) {
213+
print("WARNING: Named transformer is not registered: \(name)",
214+
"in attribute:", self)
215+
}
201216
case .transformableByType(let type):
202-
assert(valueTransformerName == nil)
203-
valueTransformerName = NSStringFromClass(type)
217+
let name = NSStringFromClass(type)
218+
if !ValueTransformer.valueTransformerNames().contains(.init(name)) {
219+
// no access to valueTransformerForName?
220+
let transformer = type.init()
221+
ValueTransformer
222+
.setValueTransformer(transformer, forName: .init(name))
223+
}
224+
valueTransformerName = name
225+
attributeType = .transformableAttributeType
204226

205227
case .allowsCloudEncryption: // FIXME: restrict availability
206228
if #available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) {

Tests/ManagedModelMacrosTests/ManagedModelMacrosTests.swift

+2
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ final class ModelMacroTests: XCTestCase {
371371

372372
// MARK: - Helper
373373

374+
#if canImport(ManagedModelMacros)
374375
func parseAndExplode(_ source: String) -> Syntax {
375376
// Parse the original source file.
376377
let sourceFile : SourceFileSyntax = Parser.parse(source: source)
@@ -393,6 +394,7 @@ final class ModelMacroTests: XCTestCase {
393394

394395
return explodedFile
395396
}
397+
#endif // canImport(ManagedModelMacros)
396398

397399
// Note: This does not fail the test, but it does fail the compiler.
398400
// https://github.com/Data-swift/ManagedModels/issues/18

0 commit comments

Comments
 (0)