Skip to content

Commit 4432f00

Browse files
committed
Merge branch 'bugs/codable-raw-property-1' into develop
2 parents 5d7c1ac + b4db7ac commit 4432f00

File tree

4 files changed

+158
-0
lines changed

4 files changed

+158
-0
lines changed

Sources/ManagedModels/PersistentModel/PersistentModel+KVC.swift

+22
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,28 @@ public extension PersistentModel {
223223
}
224224
return wrapped
225225
}
226+
227+
// Overloads for RawRepresentables that are ALSO Codable
228+
229+
@inlinable
230+
func setValue<T>(forKey key: String, to value: T)
231+
where T: RawRepresentable & Codable,
232+
T.RawValue: Codable & CoreDataPrimitiveValue
233+
{
234+
setValue(forKey: key, to: value.rawValue)
235+
}
236+
237+
@inlinable
238+
func getValue<T>(forKey key: String) -> T
239+
where T: RawRepresentable & Codable,
240+
T.RawValue: Codable & CoreDataPrimitiveValue
241+
{
242+
let rawValue : T.RawValue = getValue(forKey: key)
243+
guard let wrapped = T.init(rawValue: rawValue) else {
244+
fatalError("Could not wrap raw value \(rawValue) for \(key)")
245+
}
246+
return wrapped
247+
}
226248
}
227249

228250
// MARK: - Codable

Tests/ManagedModelTests/CodablePropertiesTest.swift Tests/ManagedModelTests/CodablePropertiesTests.swift

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ final class CodablePropertiesTests: XCTestCase {
1616
)
1717

1818
func testEntityName() throws {
19+
_ = container
1920
let entityType = Fixtures.CodablePropertiesSchema.StoredAccess.self
2021
XCTAssertEqual(entityType.entity().name, "StoredAccess")
2122
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//
2+
// Created by Helge Heß.
3+
// Copyright © 2024 ZeeZide GmbH.
4+
//
5+
6+
import XCTest
7+
import Foundation
8+
import CoreData
9+
@testable import ManagedModels
10+
11+
final class CodableRawRepresentableTests: XCTestCase {
12+
// https://github.com/Data-swift/ManagedModels/issues/29
13+
14+
private lazy var container = try? ModelContainer(
15+
for: Fixtures.ToDoListSchema.managedObjectModel,
16+
configurations: ModelConfiguration(isStoredInMemoryOnly: true)
17+
)
18+
19+
func testEntityName() throws {
20+
_ = container // required to register the entity type mapping
21+
let entityType = Fixtures.ToDoListSchema.ToDo.self
22+
XCTAssertEqual(entityType.entity().name, "ToDo")
23+
}
24+
25+
func testPropertySetup() throws {
26+
let valueType = Fixtures.ToDoListSchema.ToDo.Priority.self
27+
let attribute = CoreData.NSAttributeDescription(
28+
name: "priority",
29+
valueType: valueType,
30+
defaultValue: nil
31+
)
32+
XCTAssertEqual(attribute.name, "priority")
33+
XCTAssertEqual(attribute.attributeType, .integer64AttributeType)
34+
35+
XCTAssertTrue(attribute.valueType == Int.self)
36+
XCTAssertNil(attribute.valueTransformerName)
37+
}
38+
39+
func testModel() throws {
40+
_ = container // required to register the entity type mapping
41+
let todo = Fixtures.ToDoListSchema.ToDo()
42+
todo.priority = .high
43+
}
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
//
2+
// Created by Helge Heß.
3+
// Copyright © 2024 ZeeZide GmbH.
4+
//
5+
6+
import ManagedModels
7+
8+
extension Fixtures {
9+
10+
enum ToDoListSchema: VersionedSchema {
11+
static var models : [ any PersistentModel.Type ] = [
12+
ToDo.self, ToDoList.self
13+
]
14+
15+
public static let versionIdentifier = Schema.Version(0, 1, 0)
16+
17+
@Model
18+
final class ToDo: NSManagedObject {
19+
20+
var title : String
21+
var isDone : Bool
22+
var priority : Priority
23+
var created : Date
24+
var due : Date?
25+
var list : ToDoList
26+
27+
enum Priority: Int, Comparable, CaseIterable, Codable {
28+
case veryLow = 1
29+
case low = 2
30+
case medium = 3
31+
case high = 4
32+
case veryHigh = 5
33+
34+
static func < (lhs: Self, rhs: Self) -> Bool {
35+
lhs.rawValue < rhs.rawValue
36+
}
37+
}
38+
39+
convenience init(list : ToDoList,
40+
title : String,
41+
isDone : Bool = false,
42+
priority : Priority = .medium,
43+
created : Date = Date(),
44+
due : Date? = nil)
45+
{
46+
// This is important so that the objects don't end up in different
47+
// contexts.
48+
self.init(context: list.modelContext)
49+
50+
self.list = list
51+
self.title = title
52+
self.isDone = isDone
53+
self.priority = priority
54+
self.created = created
55+
self.due = due
56+
}
57+
58+
var isOverDue : Bool {
59+
guard let due else { return false }
60+
return due < Date()
61+
}
62+
}
63+
64+
@Model
65+
final class ToDoList: NSManagedObject {
66+
67+
var title = ""
68+
var toDos = [ ToDo ]()
69+
70+
convenience init(title: String) {
71+
self.init()
72+
self.title = title
73+
}
74+
75+
var hasOverdueItems : Bool { toDos.contains { $0.isOverDue && !$0.isDone } }
76+
77+
enum Doneness { case all, none, some }
78+
79+
var doneness : Doneness {
80+
let hasDone = toDos.contains { $0.isDone }
81+
let hasUndone = toDos.contains { !$0.isDone }
82+
switch ( hasDone, hasUndone ) {
83+
case ( true , true ) : return .some
84+
case ( true , false ) : return .all
85+
case ( false , true ) : return .none
86+
case ( false , false ) : return .all // empty
87+
}
88+
}
89+
}
90+
}
91+
}

0 commit comments

Comments
 (0)