Skip to content

Commit

Permalink
Drop CodableBox, not required?
Browse files Browse the repository at this point in the history
Directly store the Codable Swift value in the
property as an Any.

Disadvantage: We cannot set the `transformedValueClass`
in the transformer nor the `attributeValueClassName` in
the `Attribute` (which will stick to `Any`.
But that doesn't seem to confused CoreData.
  • Loading branch information
helje5 committed Feb 15, 2024
1 parent 431ce35 commit 28d5984
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 113 deletions.
21 changes: 7 additions & 14 deletions Sources/ManagedModels/PersistentModel/PersistentModel+KVC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -232,14 +232,14 @@ public extension PersistentModel {

func setValue<T>(forKey key: String, to value: T) where T: Codable {
willChangeValue(forKey: key); defer { didChangeValue(forKey: key) }
setPrimitiveValue(CodableBox<T>(value), forKey: key)
setPrimitiveValue(value, forKey: key)
}

func setValue<T>(forKey key: String, to value: T)
where T: Codable & AnyOptional
{
willChangeValue(forKey: key); defer { didChangeValue(forKey: key) }
if value.isSome { setPrimitiveValue(CodableBox<T>(value), forKey: key) }
if value.isSome { setPrimitiveValue(value, forKey: key) }
else { setPrimitiveValue(nil, forKey: key) }
}

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

if let box = value as? CodableBox<T> {
guard let value = box.value else {
fatalError("Box has no value for non-optional Codable for key \(key)?")
}
if let value = value as? T {
return value
}

Expand All @@ -266,17 +263,13 @@ public extension PersistentModel {
}
}

guard let value = value as? T else {
fatalError("Unexpected value for key \(key)? \(value)")
}
assertionFailure("Codable value is directly stored? \(value)")
return value
fatalError("Codable value type doesn't match? \(value)")
}

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

if let data = value as? Data {
assertionFailure("Unexpected Data as primitive!")
Expand All @@ -291,7 +284,7 @@ public extension PersistentModel {
guard let value = value as? T else {
fatalError("Unexpected value for key \(key)? \(value)")
}
assertionFailure("Codable value is directly stored? \(value)")
return value
assertionFailure("Codable value type doesn't match? \(value)")
return .noneValue
}
}
86 changes: 0 additions & 86 deletions Sources/ManagedModels/SchemaCompatibility/CodableBox.swift

This file was deleted.

48 changes: 48 additions & 0 deletions Sources/ManagedModels/SchemaCompatibility/CodableTransformer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// Created by Helge Heß.
// Copyright © 2023 ZeeZide GmbH.
//

import Foundation
import CoreData

final class CodableTransformer<T: Codable>: ValueTransformer {

#if false
override class func transformedValueClass() -> AnyClass {
T.self // doesn't work
}
#endif
override class func allowsReverseTransformation() -> Bool { true }

override func transformedValue(_ value: Any?) -> Any? {
// value is the box
guard let value else { return nil }
guard let typed = value as? T else {
assertionFailure("Value to be transformed is not the right type? \(value)")
return nil
}
do {
return try JSONEncoder().encode(typed)
}
catch {
assertionFailure("Could not encode JSON value of property? \(error)")
return nil
}
}

override func reverseTransformedValue(_ value: Any?) -> Any? {
guard let value else { return nil }
guard let data = value as? Data else {
assert(value is Data, "Reverse value is not `Data`?")
return nil
}
do {
return try JSONDecoder().decode(T.self, from: data)
}
catch {
assertionFailure("Could not decode JSON value of property? \(error)")
return nil
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ extension CoreData.NSAttributeDescription: SchemaProperty {

if let primitiveType = newValue as? CoreDataPrimitiveValue.Type {
let config = primitiveType.coreDataValue
self.attributeType = config.attributeType
self.isOptional = config.isOptional
self.attributeType = config.attributeType
self.isOptional = config.isOptional
if let newClassName = config.attributeValueClassName {
self.attributeValueClassName = newClassName
}
Expand All @@ -55,8 +55,8 @@ extension CoreData.NSAttributeDescription: SchemaProperty {
let rawType = type.RawValue.self
if let primitiveType = rawType as? CoreDataPrimitiveValue.Type {
let config = primitiveType.coreDataValue
self.attributeType = config.attributeType
self.isOptional = config.isOptional
self.attributeType = config.attributeType
self.isOptional = config.isOptional
if let newClassName = config.attributeValueClassName {
self.attributeValueClassName = newClassName
}
Expand All @@ -75,12 +75,14 @@ extension CoreData.NSAttributeDescription: SchemaProperty {
self.isOptional = newValue is any AnyOptional.Type

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

let name = NSStringFromClass(CodableBox<T>.Transformer.self)
let name = NSStringFromClass(CodableTransformer<T>.self)
if !ValueTransformer.valueTransformerNames().contains(.init(name)) {
// no access to valueTransformerForName?
let transformer = CodableBox<T>.Transformer()
let transformer = CodableTransformer<T>()
ValueTransformer
.setValueTransformer(transformer, forName: .init(name))
}
Expand Down
12 changes: 6 additions & 6 deletions Tests/ManagedModelTests/CodablePropertiesTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ final class CodablePropertiesTests: XCTestCase {

let transformerName = try XCTUnwrap(
ValueTransformer.valueTransformerNames().first(where: {
$0.rawValue.range(of: "CodableBox11TransformerVOO17ManagedModelTests8")
$0.rawValue.range(of: "CodableTransformerVOO17ManagedModelTests8")
!= nil
})
)
let transformer = try XCTUnwrap(ValueTransformer(forName: transformerName))
_ = transformer // to clear unused-wraning

XCTAssertTrue(attribute.valueType ==
CodableBox<Fixtures.CodablePropertiesSchema.AccessSIP>.self)
XCTAssertTrue(attribute.valueType == Any.self)
// Fixtures.CodablePropertiesSchema.AccessSIP.self
XCTAssertNotNil(attribute.valueTransformerName)
XCTAssertEqual(attribute.valueTransformerName, transformerName.rawValue)
}
Expand All @@ -54,7 +54,7 @@ final class CodablePropertiesTests: XCTestCase {
// CodableBox.
let transformerName = try XCTUnwrap(
ValueTransformer.valueTransformerNames().first(where: {
$0.rawValue.range(of: "CodableBox11TransformerVOO17ManagedModelTests8")
$0.rawValue.range(of: "CodableTransformerVOO17ManagedModelTests8")
!= nil
})
)
Expand All @@ -63,8 +63,8 @@ final class CodablePropertiesTests: XCTestCase {

let attribute = try XCTUnwrap(entity.attributesByName["sip"])
XCTAssertEqual(attribute.name, "sip")
XCTAssertTrue(attribute.valueType ==
CodableBox<Fixtures.CodablePropertiesSchema.AccessSIP>.self)
XCTAssertTrue(attribute.valueType == Any.self)
// Fixtures.CodablePropertiesSchema.AccessSIP.self)
XCTAssertNotNil(attribute.valueTransformerName)
XCTAssertEqual(attribute.valueTransformerName, transformerName.rawValue)
}
Expand Down

0 comments on commit 28d5984

Please sign in to comment.