Skip to content

Commit ef2358f

Browse files
committed
Merge branch 'develop'
2 parents a4165f3 + c4e4002 commit ef2358f

File tree

9 files changed

+145
-16
lines changed

9 files changed

+145
-16
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ ManagedModels has no other dependencies.
139139

140140
- [x] Archiving/Unarchiving, required for migration.
141141
- [ ] Figure out whether we can do ordered attributes: [Issue #1](https://github.com/Data-swift/ManagedModels/issues/1).
142-
- [ ] Figure out whether we can add support for array toMany properties: [Issue #2](https://github.com/Data-swift/ManagedModels/issues/2)
142+
- [x] Figure out whether we can add support for array toMany properties: [Issue #2](https://github.com/Data-swift/ManagedModels/issues/2)
143143
- [ ] Support for "autosave": [Issue #3](https://github.com/Data-swift/ManagedModels/issues/3)
144144
- [ ] Support transformable types, not sure they work right yet: [Issue #4](https://github.com/Data-swift/ManagedModels/issues/4)
145145
- [ ] Generate property initializers if the user didn't specify any inits: [Issue #5](https://github.com/Data-swift/ManagedModels/issues/5)
@@ -154,7 +154,7 @@ ManagedModels has no other dependencies.
154154
- [ ] SwiftUI `@Query` property wrapper/macro?: [Issue 12](https://github.com/Data-swift/ManagedModels/issues/12)
155155
- [ ] Figure out all the cloud sync options SwiftData has and whether CoreData
156156
can do them: [Issue 13](https://github.com/Data-swift/ManagedModels/issues/13)
157-
- [ ] Figure out whether we can allow initialized properties
157+
- [x] Figure out whether we can allow initialized properties
158158
(`var title = "No Title"`): [Issue 14](https://github.com/Data-swift/ManagedModels/issues/14)
159159

160160
Pull requests are very welcome!

Sources/ManagedModelMacros/ModelMacro/MetadataSlot.swift

+6-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ extension ModelMacro {
1717
func generateInfo(for property: ModelProperty) -> ExprSyntax? {
1818
guard let valueType = property.valueType else { return nil }
1919
guard valueType.isKnownAttributePropertyType else { return nil }
20-
return "CoreData.NSAttributeDescription(name: \(literal: property.name), valueType: \(valueType).self)"
20+
let cleanValueType = valueType.replacingImplicitlyUnwrappedOptionalTypes().trimmed
21+
return "CoreData.NSAttributeDescription(name: \(literal: property.name), valueType: \(cleanValueType).self)"
2122
}
2223

2324
func attributeInfo(for property: ModelProperty,
@@ -26,7 +27,8 @@ extension ModelMacro {
2627
{
2728
// Note: We still want empty prop objects, because they still tell the
2829
// type of a property!
29-
let valueType = property.valueType ?? "Any"
30+
let valueType : TypeSyntax = property.valueType?
31+
.replacingImplicitlyUnwrappedOptionalTypes().trimmed ?? "Any"
3032
var fallback: ExprSyntax {
3133
"CoreData.NSAttributeDescription(name: \(literal: property.name), valueType: \(valueType).self)"
3234
}
@@ -53,7 +55,8 @@ extension ModelMacro {
5355
{
5456
// Note: We still want empty prop objects, because they still tell the
5557
// type of a property!
56-
let valueType = property.valueType ?? "Any"
58+
let valueType : TypeSyntax = property.valueType?
59+
.replacingImplicitlyUnwrappedOptionalTypes().trimmed ?? "Any"
5760
var fallback: ExprSyntax {
5861
"CoreData.NSRelationshipDescription(name: \(literal: property.name), valueType: \(valueType).self)"
5962
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//
2+
// Created by Helge Heß.
3+
// Copyright © 2023 ZeeZide GmbH.
4+
//
5+
6+
import SwiftSyntax
7+
8+
extension TypeSyntax {
9+
10+
/**
11+
* Rewrite `Product!` to `Product?`, since the former is not allowed in
12+
* type references, like `Product!.self` (is forbidden).
13+
*/
14+
func replacingImplicitlyUnwrappedOptionalTypes() -> TypeSyntax {
15+
guard let force = self.as(ImplicitlyUnwrappedOptionalTypeSyntax.self) else {
16+
return self
17+
}
18+
let regularOptional = OptionalTypeSyntax(wrappedType: force.wrappedType)
19+
return TypeSyntax(regularOptional)
20+
}
21+
}

Sources/ManagedModels/Container/ModelConfiguration.swift

+11-10
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ public struct ModelConfiguration: Hashable {
2323
}
2424

2525
public init(path: String? = nil, name: String? = nil,
26+
schema : NSManagedObjectModel? = nil,
27+
isStoredInMemoryOnly : Bool = false,
28+
allowsSave : Bool = true,
2629
groupAppContainerIdentifier : String? = nil,
2730
cloudKitContainerIdentifier : String? = nil,
2831
groupContainer : GroupContainer = .none,
29-
cloudKitDatabase : CloudKitDatabase = .none,
30-
schema : NSManagedObjectModel? = nil,
31-
allowsSave : Bool = true,
32-
isStoredInMemoryOnly : Bool = false)
32+
cloudKitDatabase : CloudKitDatabase = .none)
3333
{
3434
let actualPath : String
3535

@@ -114,20 +114,21 @@ public extension ModelConfiguration {
114114
get { URL(fileURLWithPath: path) }
115115
}
116116

117-
init(url: URL, name: String? = nil,
117+
init(_ name: String? = nil, schema: Schema? = nil, url: URL,
118+
isStoredInMemoryOnly: Bool = false, allowsSave: Bool = true,
118119
groupAppContainerIdentifier: String? = nil,
119120
cloudKitContainerIdentifier: String? = nil,
120121
groupContainer: GroupContainer = .none,
121-
cloudKitDatabase: CloudKitDatabase = .none, schema: Schema? = nil,
122-
allowsSave: Bool = true, isStoredInMemoryOnly: Bool = false)
122+
cloudKitDatabase: CloudKitDatabase = .none)
123123
{
124124
self.init(path: url.path, name: name ?? url.lastPathComponent,
125+
schema: schema,
126+
isStoredInMemoryOnly: isStoredInMemoryOnly,
127+
allowsSave: allowsSave,
125128
groupAppContainerIdentifier: groupAppContainerIdentifier,
126129
cloudKitContainerIdentifier: cloudKitContainerIdentifier,
127130
groupContainer: groupContainer,
128-
cloudKitDatabase: cloudKitDatabase,
129-
schema: schema, allowsSave: allowsSave,
130-
isStoredInMemoryOnly: isStoredInMemoryOnly)
131+
cloudKitDatabase: cloudKitDatabase)
131132
}
132133
}
133134

Sources/ManagedModels/Context/NSManagedObjectContext+Data.swift

+10
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,16 @@ public extension NSManagedObjectContext {
4545
}
4646
}
4747

48+
public extension NSManagedObjectContext {
49+
50+
@inlinable
51+
func fetchCount<T>(_ request: NSFetchRequest<T>) throws -> Int
52+
where T: NSFetchRequestResult
53+
{
54+
try count(for: request)
55+
}
56+
}
57+
4858
public extension NSManagedObjectContext {
4959

5060
static let willSave = willSaveObjectsNotification

Sources/ManagedModels/ModelMacroDefinition.swift

+3
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ public macro Attribute(
3737
@attached(peer)
3838
public macro Relationship(
3939
_ options: NSRelationshipDescription.Option...,
40+
deleteRule: Schema.Relationship.DeleteRule = .nullify,
41+
minimumModelCount: Int? = 0, maximumModelCount: Int? = 0,
4042
originalName: String? = nil,
43+
inverse: AnyKeyPath? = nil,
4144
hashModifier: String? = nil
4245
) = #externalMacro(module: "ManagedModelMacros", type: "RelationshipMacro")
4346

Sources/ManagedModels/PersistentModel/PersistentIdentifier.swift

+10
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,16 @@ extension NSManagedObjectID: Identifiable {
1515
public var id: Self { self }
1616
}
1717

18+
extension NSManagedObjectID: Encodable {
19+
20+
@inlinable
21+
public func encode(to encoder: Encoder) throws {
22+
var container = encoder.singleValueContainer()
23+
try container.encode(self.uriRepresentation())
24+
}
25+
}
26+
27+
1828
public extension NSManagedObjectID {
1929

2030
@inlinable

Sources/ManagedModels/PersistentModel/PersistentModel.swift

+3
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ public extension PersistentModel {
4444
/// The `NSManagedObjectID` of the model.
4545
@inlinable
4646
var persistentModelID : NSManagedObjectID { objectID }
47+
48+
@inlinable
49+
var id : NSManagedObjectID { persistentModelID }
4750
}
4851

4952
extension PersistentModel {

Tests/ManagedModelMacrosTests/ManagedModelMacrosTests.swift

+79-1
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,85 @@ final class ModelMacroTests: XCTestCase {
289289
#endif
290290
#endif // canImport(ManagedModelMacros)
291291
}
292-
292+
293+
func testForceUnwrapType() throws {
294+
#if !canImport(ManagedModelMacros)
295+
throw XCTSkip("macros are only supported when running tests for the host platform")
296+
#else
297+
let explodedFile = parseAndExplode(
298+
"""
299+
@Model
300+
final class Address: NSManagedObject {
301+
@Relationship(originalName: "ProductID")
302+
var person: Person!
303+
}
304+
"""
305+
)
306+
307+
// Hm, this doesn't seem to work?
308+
let diags = ParseDiagnosticsGenerator.diagnostics(for: explodedFile)
309+
XCTAssertTrue(diags.isEmpty)
310+
if !diags.isEmpty {
311+
print("DIAGS:", diags)
312+
}
313+
314+
let explodedSource = explodedFile.description
315+
XCTAssertTrue (explodedSource.contains(
316+
"name: \"person\", valueType: Person?.self"
317+
))
318+
XCTAssertFalse(explodedSource.contains(
319+
"name: \"person\", valueType: Person!.self"
320+
))
321+
322+
#if false
323+
print("Exploded:---\n")
324+
print(explodedSource)
325+
print("\n-----")
326+
#endif
327+
#endif // canImport(ManagedModelMacros)
328+
}
329+
330+
func testCommentInType() throws {
331+
#if !canImport(ManagedModelMacros)
332+
throw XCTSkip("macros are only supported when running tests for the host platform")
333+
#else
334+
let explodedFile = parseAndExplode(
335+
"""
336+
@Model
337+
final class Address: NSManagedObject {
338+
@Attribute(.unique, originalName: "CustomerTypeID")
339+
public var typeID: String // pkey in original
340+
}
341+
"""
342+
)
343+
344+
// Hm, this doesn't seem to work?
345+
let diags = ParseDiagnosticsGenerator.diagnostics(for: explodedFile)
346+
XCTAssertTrue(diags.isEmpty)
347+
if !diags.isEmpty {
348+
print("DIAGS:", diags)
349+
}
350+
351+
let explodedSource = explodedFile.description
352+
XCTAssertTrue (explodedSource.contains(
353+
"""
354+
originalName: "CustomerTypeID", name: "typeID", valueType: String
355+
"""
356+
))
357+
XCTAssertFalse(explodedSource.contains(
358+
"""
359+
originalName: "CustomerTypeID", name: "typeID", valueType: String // pkey in original.self
360+
"""
361+
))
362+
363+
#if true
364+
print("Exploded:---\n")
365+
print(explodedSource)
366+
print("\n-----")
367+
#endif
368+
#endif // canImport(ManagedModelMacros)
369+
}
370+
293371

294372
// MARK: - Helper
295373

0 commit comments

Comments
 (0)