Skip to content

Commit b420aab

Browse files
committed
Update dependencies and enhance documentation for CoreDataEvolution
- Changed swift-syntax dependency version range in Package.swift - Improved README.md with additional platform badges and updated section headers - Added comprehensive documentation for CoreDataEvolution features and usage - Expanded NSModelActorTests with new test cases for background and main thread actors
1 parent 2303b85 commit b420aab

File tree

4 files changed

+184
-5
lines changed

4 files changed

+184
-5
lines changed

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ let package = Package(
2525
),
2626
],
2727
dependencies: [
28-
.package(url: "https://github.com/swiftlang/swift-syntax", from: "600.0.0"),
28+
.package(url: "https://github.com/swiftlang/swift-syntax", "509.0.0"..<"602.0.0")
2929
],
3030
targets: [
3131
// Targets are the basic building blocks of a package, defining a module or a test suite.

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# CoreDataEvolution
22

3-
![Swift 6](https://img.shields.io/badge/Swift-6-orange?logo=swift) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
3+
![Swift 6](https://img.shields.io/badge/Swift-6-orange?logo=swift) ![iOS](https://img.shields.io/badge/iOS-17.0+-green) ![macOS](https://img.shields.io/badge/macOS-14.0+-green) ![watchOS](https://img.shields.io/badge/watchOS-10.0+-green) ![visionOS](https://img.shields.io/badge/visionOS-1.0+-green) ![tvOS](https://img.shields.io/badge/tvOS-17.0+-green) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/fatbobman/CoreDataEvolution)
44

5-
**Revolutionizing Core Data with SwiftData-inspired Concurrent Operations**
5+
## Revolutionizing Core Data with SwiftData-inspired Concurrent Operations
66

77
Welcome to CoreDataEvolution, a library aimed at modernizing Core Data by incorporating the elegance and safety of SwiftData-style concurrency. This library is designed to simplify and enhance Core Data’s handling of multithreading, drawing inspiration from SwiftData's `@ModelActor` feature, enabling efficient, safe, and scalable operations.
88

@@ -133,4 +133,3 @@ CoreDataEvolution is available under the MIT license. See the LICENSE file for m
133133
Special thanks to the Swift community for their continuous support and contributions.
134134

135135
[![Buy Me A Coffee](https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png)](https://buymeacoffee.com/fatbobman)
136-
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# ``CoreDataEvolution``
2+
3+
Revolutionizing Core Data with SwiftData-inspired Concurrent Operations
4+
5+
## Overview
6+
7+
CoreDataEvolution is a library aimed at modernizing Core Data by incorporating the elegance and safety of SwiftData-style concurrency. This library is designed to simplify and enhance Core Data's handling of multithreading, drawing inspiration from SwiftData's `@ModelActor` feature, enabling efficient, safe, and scalable operations.
8+
9+
## Motivation
10+
11+
SwiftData introduced modern concurrency features like `@ModelActor`, making it easier to handle concurrent data access with safety guaranteed by the compiler. However, SwiftData's platform requirements and limited maturity in certain areas have deterred many developers from adopting it. CoreDataEvolution bridges the gap, bringing SwiftData's advanced design into the Core Data world for developers who are still reliant on Core Data.
12+
13+
## Key Features
14+
15+
### Custom Executors for Core Data Actors
16+
17+
Using Swift 5.9's new `SerialExecutor` and `ExecutorJob` protocols, CoreDataEvolution provides custom executors that ensure all operations on managed objects are performed on the appropriate thread associated with their managed object context.
18+
19+
### @NSModelActor Macro
20+
21+
The `@NSModelActor` macro simplifies Core Data concurrency, mirroring SwiftData's `@ModelActor` macro. It generates the necessary boilerplate code to manage a Core Data stack within an actor, ensuring safe and efficient access to managed objects.
22+
23+
### NSMainModelActor Macro
24+
25+
`NSMainModelActor` provides the same functionality as `NSModelActor`, but is used to declare a class that runs on the main thread.
26+
27+
### Elegant Actor-based Concurrency
28+
29+
CoreDataEvolution allows you to create actors with custom executors tied to Core Data contexts, ensuring that all operations within the actor are executed serially on the context's thread.
30+
31+
## Basic Usage
32+
33+
Here's how you can use CoreDataEvolution to manage concurrent Core Data operations with an actor:
34+
35+
```swift
36+
import CoreDataEvolution
37+
38+
@NSModelActor
39+
actor DataHandler {
40+
func updateItem(identifier: NSManagedObjectID, timestamp: Date) throws {
41+
guard let item = self[identifier, as: Item.self] else {
42+
throw MyError.objectNotExist
43+
}
44+
item.timestamp = timestamp
45+
try modelContext.save()
46+
}
47+
}
48+
```
49+
50+
In this example, the `@NSModelActor` macro simplifies the setup, automatically creating the required executor and Core Data stack inside the actor. Developers can then focus on their business logic without worrying about concurrency pitfalls.
51+
52+
## Advanced Usage
53+
54+
### Custom Initialization
55+
56+
You can disable the automatic generation of the constructor by using `disableGenerateInit`:
57+
58+
```swift
59+
@NSModelActor(disableGenerateInit: true)
60+
public actor DataHandler {
61+
let viewName: String
62+
63+
func createNewItem(_ timestamp: Date = .now, showThread: Bool = false) throws -> NSManagedObjectID {
64+
let item = Item(context: modelContext)
65+
item.timestamp = timestamp
66+
try modelContext.save()
67+
return item.objectID
68+
}
69+
70+
init(container: NSPersistentContainer, viewName: String) {
71+
modelContainer = container
72+
self.viewName = viewName
73+
let context = container.newBackgroundContext()
74+
context.name = viewName
75+
modelExecutor = .init(context: context)
76+
}
77+
}
78+
```
79+
80+
### Main Thread Operations
81+
82+
NSMainModelActor provides the same functionality as NSModelActor, but for operations that need to run on the main thread:
83+
84+
```swift
85+
@MainActor
86+
@NSMainModelActor
87+
final class DataHandler {
88+
func updateItem(identifier: NSManagedObjectID, timestamp: Date) throws {
89+
guard let item = self[identifier, as: Item.self] else {
90+
throw MyError.objectNotExist
91+
}
92+
item.timestamp = timestamp
93+
try modelContext.save()
94+
}
95+
}
96+
```
97+
98+
## Installation
99+
100+
Add CoreDataEvolution to your project using Swift Package Manager:
101+
102+
```swift
103+
dependencies: [
104+
.package(url: "https://github.com/fatbobman/CoreDataEvolution.git", .upToNextMajor(from: "0.3.0"))
105+
]
106+
```
107+
108+
Then import the module:
109+
110+
```swift
111+
import CoreDataEvolution
112+
```
113+
114+
## System Requirements
115+
116+
- iOS 17.0+ / macOS 14.0+ / watchOS 10.0+ / visionOS 1.0+ / tvOS 17.0+
117+
- Swift 6.0
118+
119+
> Important: Due to system limitations, custom executors and `SerialExecutor` are only available on iOS 17/macOS 14 and later.
120+
121+
## Topics
122+
123+
### Actors
124+
125+
- ``NSModelActor``
126+
- ``NSMainModelActor``
127+
128+
### Executors
129+
130+
- Custom executors for Core Data contexts
131+
- Thread-safe operations
132+
133+
### Migration
134+
135+
- Migrating from traditional Core Data patterns
136+
- SwiftData compatibility considerations
137+

Tests/CoreDataEvolutionTests/NSModelActorTests.swift

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,67 @@
11
import CoreDataEvolution
2-
import CoreDataEvolutionMacros
32
import Testing
43

4+
/// Test suite for NSModelActor functionality
5+
/// This struct contains tests that verify the behavior of actors decorated with @NSModelActor and @NSMainModelActor macros
56
struct NSModelActorTests {
7+
/// Test case for creating and managing Core Data items using a background actor
8+
/// This test verifies that:
9+
/// 1. Items can be created successfully in a background context
10+
/// 2. The item count is tracked correctly
11+
/// 3. Items can be deleted properly
12+
/// 4. Thread safety is maintained through the actor model
613
@Test func createNewItem() async throws {
14+
// Initialize the test Core Data stack
15+
// TestStack provides a pre-configured NSPersistentContainer for testing
716
let stack = TestStack()
17+
18+
// Create a DataHandler actor instance with custom executor
19+
// The DataHandler is decorated with @NSModelActor(disableGenerateInit: true)
20+
// This creates an actor that operates on a background thread with its own managed object context
821
let handler = DataHandler(container: stack.container, viewName: "hello")
22+
23+
// Create a new item asynchronously using the actor
24+
// The showThread parameter enables thread information logging for debugging
25+
// Returns the NSManagedObjectID of the created item
926
let id = try await handler.createNemItem(showThread: true)
27+
28+
// Verify that exactly one item was created
29+
// This tests the actor's ability to perform read operations safely
1030
let count = try await handler.getItemCount()
1131
#expect(count == 1)
32+
33+
// Delete the created item using its object ID
34+
// This tests the actor's ability to perform delete operations safely
1235
try await handler.delItem(id)
36+
37+
// Verify that the item was successfully deleted
38+
// The count should return to zero after deletion
1339
let newCount = try await handler.getItemCount()
1440
#expect(newCount == 0)
1541
}
1642

43+
/// Test case for creating Core Data items using a main thread actor
44+
/// This test verifies that:
45+
/// 1. Main thread actors work correctly with @NSMainModelActor
46+
/// 2. Items can be created synchronously on the main thread
47+
/// 3. Thread information can be logged for verification
1748
@MainActor
1849
@Test func createNewItemInMainActor() throws {
50+
// Initialize the test Core Data stack
1951
let stack = TestStack()
52+
53+
// Create a MainHandler instance decorated with @NSMainModelActor
54+
// Unlike DataHandler, this operates on the main thread and doesn't require async/await
55+
// The MainHandler provides the same Core Data operations but runs synchronously on the main thread
2056
let handler = MainHandler(modelContainer: stack.container)
57+
58+
// Create a new item synchronously on the main thread
59+
// The showThread parameter will log main thread information
60+
// Since this is a synchronous operation, we discard the returned object ID
2161
_ = try handler.createNemItem(showThread: true)
62+
63+
// Verify that the item was created successfully
64+
// This operation also runs synchronously on the main thread
2265
let count = try handler.getItemCount()
2366
#expect(count == 1)
2467
}

0 commit comments

Comments
 (0)