Skip to content

Commit 9e2b103

Browse files
committed
Release version 1.3.0
Swift 6 concurrency improvements: - Add @unchecked Sendable conformance - Thread-safe task management with DispatchQueue - Use NotificationCenter.notifications() (iOS 15+) - Fix Xcode 26+ compatibility (performAndWait → performAndWaitWithResult) - Add [weak self] to prevent retain cycles - Improve Sendable compliance in CombineAsyncPublisher Documentation: - Update README with version 1.3.0 information - Add migration guide for API changes - Document Xcode 26+ compatibility - Add information about upcoming Version 2.0
2 parents 73de364 + 8b71dd5 commit 9e2b103

5 files changed

Lines changed: 148 additions & 73 deletions

File tree

.vscode/settings.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"swiftlint.enable": false,
3+
"swiftlint.autoLintWorkspace": false,
4+
"swift.path": "/Applications/Xcode-26.2.0.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin",
5+
"swift.diagnosticsCollection": "onlySourceKit"
6+
}

README.md

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,64 @@ Helps you easily handle Core Data's Persistent History Tracking
66

77
[中文版说明](https://github.com/fatbobman/PersistentHistoryTrackingKit/blob/main/READMECN.md)
88

9-
## 🚀 Swift 6 Branch Available
9+
## 🚀 Version 1.3.0 - Swift 6 Compatible
1010

11-
> **🎯 New Swift 6 Compatible Version Available**
11+
> **🎉 Current Version (1.3.0) is Swift 6 Compatible**
1212
>
13-
> We've created a comprehensive **Swift 6 adaptation** with full concurrency safety, true Sendable compliance, and memory leak fixes. The new version is available in the `swift6-adaptation` branch.
13+
> The latest release includes comprehensive Swift 6 concurrency improvements:
1414
>
15-
> **Key Improvements:**
15+
> **What's New in 1.3.0:**
1616
>
17-
> - 🔒 **True Sendable Compliance** - Not just `@unchecked Sendable`
18-
> - 🧵 **Data Race Free** - Comprehensive concurrency testing
19-
> - 🛡️ **Memory Safe** - Zero retain cycles or memory leaks
20-
> - 🧪 **Swift Testing Framework** - Modern testing infrastructure
21-
> - 📚 **Enhanced Documentation** - Comprehensive guides and examples
17+
> -**Swift 6 Compatible** - Works with Swift 6 strict concurrency mode
18+
> - 🔒 **Thread-Safe Task Management** - Protected with DispatchQueue barriers
19+
> - 🧵 **No Retain Cycles** - Uses `[weak self]` to prevent memory leaks
20+
> - 📱 **Xcode 26+ Compatible** - Resolves system API naming conflicts
21+
> - 🚀 **Native Notifications** - Uses iOS 15+ `NotificationCenter.notifications()`
22+
> - 🎯 **Sendable Compliance** - Added `@unchecked Sendable` conformance
2223
>
23-
> **🔄 Try it out:**
24+
> **📦 Install the latest version:**
2425
>
2526
> ```swift
2627
> dependencies: [
27-
> .package(url: "https://github.com/fatbobman/PersistentHistoryTrackingKit.git", branch: "swift6-adaptation")
28+
> .package(url: "https://github.com/fatbobman/PersistentHistoryTrackingKit.git", from: "1.3.0")
2829
> ]
2930
> ```
3031
>
31-
> **📝 Feedback Welcome:**
32-
> Please test the Swift 6 version and [**create an issue**](https://github.com/fatbobman/PersistentHistoryTrackingKit/issues) with your feedback. We'll merge it to main once we have sufficient real-world validation.
32+
> **🔜 Looking ahead:**
3333
>
34-
> **📖 Full Documentation:** [Swift 6 Branch README](https://github.com/fatbobman/PersistentHistoryTrackingKit/blob/swift6-adaptation/README.md)
34+
> We're actively developing **Version 2.0** with:
35+
>
36+
> - Full Swift 6 strict concurrency mode
37+
> - iOS 17+ with Actor-based architecture
38+
> - Built-in Hook callback system
39+
> - Tombstone mechanism for deleted objects
40+
> - Complete Sendable safety without `@unchecked`
41+
>
42+
> Follow progress in the `feature/swift6-optimization-and-hook-system` branch.
43+
44+
## ⚠️ Important API Note for Xcode 26+ Users
45+
46+
Version 1.3.0+ is fully compatible with Xcode 26 (Swift 6.0)
47+
48+
Since Xcode 26 Beta 5, Apple has added a native `performAndWait<T>(_ block: @Sendable () throws -> T) rethrows -> T` method to `NSManagedObjectContext`. To avoid naming conflicts with this system API, we renamed our extension method:
49+
50+
### Old (before 1.3.0)
51+
52+
```swift
53+
// ❌ Conflicts with Xcode 26+ system API
54+
let result = try context.performAndWait { ... }
55+
```
56+
57+
### New (1.3.0+)
58+
59+
```swift
60+
// ✅ Works with all Xcode versions
61+
let result = try context.performAndWaitWithResult { ... }
62+
```
63+
64+
**Migration:** If you were using the old `performAndWait` extension, simply rename it to `performAndWaitWithResult`. The functionality is identical.
65+
66+
For more details, see [Issue #6](https://github.com/fatbobman/PersistentHistoryTrackingKit/issues/6).
3567

3668
## What's This?
3769

READMECN.md

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,64 @@
66

77
[English Version](https://github.com/fatbobman/PersistentHistoryTrackingKit/blob/main/README.md)
88

9-
## 🚀 Swift 6 分支现已可用
9+
## 🚀 版本 1.3.0 - Swift 6 兼容
1010

11-
> **🎯 新的 Swift 6 兼容版本现已可用**
11+
> **🎉 当前版本 (1.3.0) 兼容 Swift 6**
1212
>
13-
> 我们创建了一个全面的 **Swift 6 适配版本**,具备完整的并发安全性、真正的 Sendable 合规性和内存泄漏修复。新版本可在 `swift6-adaptation` 分支中使用。
13+
> 最新版本包含了全面的 Swift 6 并发改进:
1414
>
15-
> **主要改进**
15+
> **1.3.0 新特性**
1616
>
17-
> - 🔒 **真正的 Sendable 合规** - 不仅仅是 `@unchecked Sendable`
18-
> - 🧵 **无数据竞争** - 全面的并发测试
19-
> - 🛡️ **内存安全** - 零保留循环或内存泄漏
20-
> - 🧪 **Swift Testing 框架** - 现代测试基础设施
21-
> - 📚 **增强文档** - 全面的指南和示例
17+
> -**Swift 6 兼容** - 支持 Swift 6 严格并发模式
18+
> - 🔒 **线程安全的任务管理** - 使用 DispatchQueue 屏障保护
19+
> - 🧵 **无内存泄漏** - 使用 `[weak self]` 防止循环引用
20+
> - 📱 **Xcode 26+ 兼容** - 解决系统 API 命名冲突
21+
> - 🚀 **原生通知** - 使用 iOS 15+ `NotificationCenter.notifications()`
22+
> - 🎯 **Sendable 合规** - 添加 `@unchecked Sendable` 一致性
2223
>
23-
> **🔄 试用方法**
24+
> **📦 安装最新版本**
2425
>
2526
> ```swift
2627
> dependencies: [
27-
> .package(url: "https://github.com/fatbobman/PersistentHistoryTrackingKit.git", branch: "swift6-adaptation")
28+
> .package(url: "https://github.com/fatbobman/PersistentHistoryTrackingKit.git", from: "1.3.0")
2829
> ]
2930
> ```
3031
>
31-
> **📝 欢迎反馈:**
32-
> 请测试 Swift 6 版本并[**创建 issue**](https://github.com/fatbobman/PersistentHistoryTrackingKit/issues) 提供您的反馈。一旦我们获得足够的实际使用验证,就会将其合并到 main 分支。
32+
> **🔜 展望未来:**
3333
>
34-
> **📖 完整文档:** [Swift 6 分支 README](https://github.com/fatbobman/PersistentHistoryTrackingKit/blob/swift6-adaptation/READMECN.md)
34+
> 我们正在积极开发 **版本 2.0**,包含:
35+
>
36+
> - 完整的 Swift 6 严格并发模式
37+
> - iOS 17+ 基于 Actor 的架构
38+
> - 内置 Hook 回调系统
39+
> - 删除对象的墓碑机制
40+
> - 完全的 Sendable 安全性(无需 `@unchecked`)
41+
>
42+
> 在 `feature/swift6-optimization-and-hook-system` 分支关注开发进度
43+
44+
## ⚠️ Xcode 26+ 用户重要 API 说明
45+
46+
**版本 1.3.0+ 完全兼容 Xcode 26 (Swift 6.0)**
47+
48+
从 Xcode 26 Beta 5 开始,Apple 为 `NSManagedObjectContext` 添加了原生的 `performAndWait<T>(_ block: @Sendable () throws -> T) rethrows -> T` 方法为了避免与系统 API 命名冲突,我们重命名了扩展方法:
49+
50+
### 旧版本 (1.3.0 之前)
51+
52+
```swift
53+
// ❌ 与 Xcode 26+ 系统API冲突
54+
let result = try context.performAndWait { ... }
55+
```
56+
57+
### 新版本 (1.3.0+)
58+
59+
```swift
60+
// ✅ 兼容所有 Xcode 版本
61+
let result = try context.performAndWaitWithResult { ... }
62+
```
63+
64+
**迁移指南:** 如果您之前使用的是旧的 `performAndWait` 扩展,只需将其重命名为 `performAndWaitWithResult`。功能完全相同。
65+
66+
更多详情请参阅 [Issue #6](https://github.com/fatbobman/PersistentHistoryTrackingKit/issues/6)
3567

3668
## What's This?
3769

Sources/PersistentHistoryTrackingKit/Extensions.swift

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,29 +33,32 @@ extension NSManagedObjectContext {
3333
}
3434
}
3535

36-
public extension Task where Success == Never, Failure == Never {
37-
static func sleep(seconds duration: Double) async throws {
38-
try await sleep(nanoseconds: UInt64(duration * 1000000000))
36+
extension Task where Success == Never, Failure == Never {
37+
public static func sleep(seconds duration: Double) async throws {
38+
try await sleep(nanoseconds: UInt64(duration * 1_000_000_000))
3939
}
4040
}
4141

4242
import Combine
4343
/// 将Publisher转换成异步序列。
4444
///
4545
/// 同系统内置的 publisher.values 不同,本实现将首先对数据进行缓存。尤其适用于NotificationCenter之类的应用。
46-
struct CombineAsyncPublisher<P>: AsyncSequence, AsyncIteratorProtocol where P: Publisher, P.Failure == Never {
46+
struct CombineAsyncPublisher<P>: AsyncSequence, AsyncIteratorProtocol where P: Publisher, P.Failure == Never, P.Output: Sendable {
4747
typealias Element = P.Output
4848
typealias AsyncIterator = CombineAsyncPublisher<P>
4949

5050
func makeAsyncIterator() -> Self {
51-
return self
51+
self
5252
}
5353

5454
private let stream: AsyncStream<P.Output>
5555
private var iterator: AsyncStream<P.Output>.Iterator
5656
private var cancellable: AnyCancellable?
5757

58-
init(_ upstream: P, bufferingPolicy limit: AsyncStream<Element>.Continuation.BufferingPolicy = .unbounded) {
58+
init(
59+
_ upstream: P,
60+
bufferingPolicy limit: AsyncStream<Element>.Continuation.BufferingPolicy = .unbounded)
61+
{
5962
var subscription: AnyCancellable?
6063
stream = AsyncStream<P.Output>(P.Output.self, bufferingPolicy: limit) { continuation in
6164
subscription = upstream
@@ -72,7 +75,7 @@ struct CombineAsyncPublisher<P>: AsyncSequence, AsyncIteratorProtocol where P: P
7275
}
7376
}
7477

75-
extension Publisher where Self.Failure == Never {
78+
extension Publisher where Self.Failure == Never, Self.Output: Sendable {
7679
var sequence: CombineAsyncPublisher<Self> {
7780
CombineAsyncPublisher(self)
7881
}

Sources/PersistentHistoryTrackingKit/PersistentHistoryTrackingKit.swift

Lines changed: 40 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,12 @@
1212

1313
import CoreData
1414
import Foundation
15-
import AsyncAlgorithms
1615

1716
// swiftlint:disable line_length
1817

19-
public final class PersistentHistoryTrackingKit {
18+
public final class PersistentHistoryTrackingKit: @unchecked Sendable {
2019
/// 日志显示等级,从0-2级。0 关闭 2 最详尽
21-
public var logLevel: Int
20+
public private(set) var logLevel: Int
2221

2322
/// 清除策略
2423
var strategy: TransactionPurgePolicy
@@ -70,7 +69,8 @@ public final class PersistentHistoryTrackingKit {
7069
let timestampManager: TransactionTimestampManager
7170

7271
/// 处理持久化历史跟踪事件的任务。可以通过start开启,stop停止。
73-
var transactionProcessingTasks = [Task<Void, Never>]()
72+
private var transactionProcessingTasks = [Task<Void, Never>]()
73+
private let taskQueue = DispatchQueue(label: "com.persistenthistorytrackingkit.tasks", attributes: .concurrent)
7474

7575
/// 持久化存储协调器,用于缩小通知返回
7676
private let coordinator: NSPersistentStoreCoordinator
@@ -81,45 +81,44 @@ public final class PersistentHistoryTrackingKit {
8181
///
8282
/// 通过将持久化历史跟踪记录的通知转换成异步序列,实现了逐个处理的机制。
8383
func createTransactionProcessingTask() -> Task<Void, Never> {
84-
Task {
85-
sendMessage(type: .info, level: 1, message: "Persistent History Track Kit Start")
84+
Task { [weak self] in
85+
guard let self = self else { return }
86+
87+
self.sendMessage(type: .info, level: 1, message: "Persistent History Track Kit Start")
8688
// 响应 notification
87-
let publisher = NotificationCenter.default.publisher(
88-
for: .NSPersistentStoreRemoteChange,
89-
object: coordinator
90-
)
91-
for await _ in publisher.values.buffer(policy: .unbounded) where !Task.isCancelled {
92-
sendMessage(type: .info,
89+
for await _ in NotificationCenter.default.notifications(named: .NSPersistentStoreRemoteChange, object: self.coordinator) where !Task.isCancelled {
90+
91+
self.sendMessage(type: .info,
9392
level: 2,
9493
message: "Get a `NSPersistentStoreRemoteChange` notification")
9594

9695
// fetch
97-
let transactions = fetchTransactions(
98-
for: currentAuthor,
99-
since: timestampManager,
100-
by: fetcher,
101-
logger: sendMessage
96+
let transactions = self.fetchTransactions(
97+
for: self.currentAuthor,
98+
since: self.timestampManager,
99+
by: self.fetcher,
100+
logger: self.sendMessage
102101
)
103-
102+
104103
if transactions.isEmpty { continue }
105-
104+
106105
// merge
107-
mergeTransactionsInContexts(
106+
self.mergeTransactionsInContexts(
108107
transactions: transactions,
109-
by: merger,
110-
timestampManager: timestampManager,
111-
logger: sendMessage
108+
by: self.merger,
109+
timestampManager: self.timestampManager,
110+
logger: self.sendMessage
112111
)
113-
114-
deduplicator?(deduplicate: transactions, in: contexts)
112+
113+
self.deduplicator?(deduplicate: transactions, in: self.contexts)
115114

116115
// clean
117-
cleanTransactions(
118-
beforeDate: timestampManager,
119-
allAuthors: allAuthors,
120-
batchAuthors: batchAuthors,
121-
by: cleaner,
122-
logger: sendMessage
116+
self.cleanTransactions(
117+
beforeDate: self.timestampManager,
118+
allAuthors: self.allAuthors,
119+
batchAuthors: self.batchAuthors,
120+
by: self.cleaner,
121+
logger: self.sendMessage
123122
)
124123
}
125124
sendMessage(type: .info, level: 1, message: "Persistent History Track Kit Stop")
@@ -264,18 +263,22 @@ public final class PersistentHistoryTrackingKit {
264263
public extension PersistentHistoryTrackingKit {
265264
/// 启动处理任务
266265
func start() {
267-
guard transactionProcessingTasks.isEmpty else {
268-
return
266+
taskQueue.async(flags: .barrier) {
267+
guard self.transactionProcessingTasks.isEmpty else {
268+
return
269+
}
270+
self.transactionProcessingTasks.append(self.createTransactionProcessingTask())
269271
}
270-
transactionProcessingTasks.append(createTransactionProcessingTask())
271272
}
272273

273274
/// 停止处理任务
274275
func stop() {
275-
transactionProcessingTasks.forEach {
276-
$0.cancel()
276+
taskQueue.async(flags: .barrier) {
277+
self.transactionProcessingTasks.forEach {
278+
$0.cancel()
279+
}
280+
self.transactionProcessingTasks.removeAll()
277281
}
278-
transactionProcessingTasks.removeAll()
279282
}
280283
}
281284

@@ -384,4 +387,3 @@ public extension PersistentHistoryTrackingKit {
384387
}
385388
}
386389

387-
extension Notification:@unchecked Sendable {}

0 commit comments

Comments
 (0)