Initial prototype for Swift 6 mode

This commit is contained in:
John Estropia
2026-07-07 11:18:17 +09:00
parent 38ec439b18
commit 49f65601cd
71 changed files with 998 additions and 684 deletions
+61 -29
View File
@@ -24,7 +24,8 @@
//
import Foundation
import CoreData
@preconcurrency import CoreData
import os
// MARK: - DataStack
@@ -49,7 +50,7 @@ extension DataStack {
*/
public func addStorage<T>(
_ storage: T,
completion: @escaping (SetupResult<T>) -> Void
completion: @escaping @MainActor (SetupResult<T>) -> Void
) {
self.coordinator.performAsynchronously {
@@ -110,7 +111,7 @@ extension DataStack {
*/
public func addStorage<T: LocalStorage>(
_ storage: T,
completion: @escaping (SetupResult<T>) -> Void
completion: @escaping @MainActor (SetupResult<T>) -> Void
) -> Progress? {
let fileURL = storage.fileURL
@@ -162,7 +163,7 @@ extension DataStack {
attributes: nil
)
let metadata = try NSPersistentStoreCoordinator.metadataForPersistentStore(
nonisolated(unsafe) let metadata = try NSPersistentStoreCoordinator.metadataForPersistentStore(
ofType: type(of: storage).storeType,
at: fileURL as URL,
options: storage.storeOptions
@@ -192,12 +193,18 @@ extension DataStack {
}
catch {
completion(.failure(CoreStoreError(error)))
DispatchQueue.main.async {
completion(.failure(CoreStoreError(error)))
}
}
return
}
completion(.failure(CoreStoreError(error)))
DispatchQueue.main.async {
completion(.failure(CoreStoreError(error)))
}
return
}
@@ -212,7 +219,10 @@ extension DataStack {
}
catch {
completion(.failure(CoreStoreError(error)))
DispatchQueue.main.async {
completion(.failure(CoreStoreError(error)))
}
}
}
)
@@ -264,7 +274,7 @@ extension DataStack {
*/
public func upgradeStorageIfNeeded<T: LocalStorage>(
_ storage: T,
completion: @escaping (MigrationResult) -> Void
completion: @escaping @MainActor (MigrationResult) -> Void
) throws(CoreStoreError) -> Progress? {
return try self.coordinator.performSynchronously {
@@ -376,7 +386,7 @@ extension DataStack {
private func upgradeStorageIfNeeded<T: LocalStorage>(
_ storage: T,
metadata: [String: Any],
completion: @escaping (MigrationResult) -> Void
completion: @escaping @MainActor (MigrationResult) -> Void
) -> Progress? {
guard let migrationSteps = self.computeMigrationFromStorage(storage, metadata: metadata) else {
@@ -423,9 +433,13 @@ extension DataStack {
}
let migrationTypes = migrationSteps.map { $0.migrationType }
var migrationResult: MigrationResult?
let migrationState: OSAllocatedUnfairLock<(migrationResult: MigrationResult?, cancelled: Bool)> = .init(
initialState: (
migrationResult: nil,
cancelled: false
)
)
var operations = [Operation]()
var cancelled = false
let progress = Progress(parent: nil, userInfo: nil)
progress.totalUnitCount = numberOfMigrations
@@ -440,12 +454,15 @@ extension DataStack {
operations.append(
BlockOperation { [weak self] in
guard let self = self, !cancelled else {
guard
let self = self,
!migrationState.withLock({ $0.cancelled })
else {
return
}
autoreleasepool {
Internals.autoreleasepool {
do {
@@ -465,8 +482,14 @@ extension DataStack {
migrationError,
"Failed to migrate version model \"\(migrationType.sourceVersion)\" to version \"\(migrationType.destinationVersion)\"."
)
migrationResult = .failure(migrationError)
cancelled = true
migrationState.withLock { state in
if !state.cancelled {
state.migrationResult = .failure(migrationError)
state.cancelled = true
}
}
}
}
@@ -488,7 +511,10 @@ extension DataStack {
DispatchQueue.main.async {
progress.setProgressHandler(nil)
completion(migrationResult ?? .success(migrationTypes))
completion(
migrationState.withLock { $0.migrationResult }
?? .success(migrationTypes)
)
return
}
}
@@ -594,22 +620,28 @@ extension DataStack {
let estimatedTime: TimeInterval = 60 * 3 // 3 mins
let interval: TimeInterval = 1
let fakeTotalUnitCount: Float = 0.9 * Float(progress.totalUnitCount)
var fakeProgress: Float = 0
let fakeProgress: OSAllocatedUnfairLock<Float> = .init(initialState: 0)
var recursiveCheck: () -> Void = {}
recursiveCheck = { [weak timerQueue] in
@Sendable
func recursiveCheck() {
guard let timerQueue = timerQueue, fakeProgress < 1 else {
let enqueueNext = fakeProgress.withLock {
return
guard $0 < 1.0 else {
return false
}
progress.completedUnitCount = Int64(fakeTotalUnitCount * $0)
$0 += Float(interval / estimatedTime)
return true
}
if enqueueNext {
timerQueue.asyncAfter(
deadline: .now() + interval,
execute: recursiveCheck
)
}
progress.completedUnitCount = Int64(fakeTotalUnitCount * fakeProgress)
fakeProgress += Float(interval / estimatedTime)
timerQueue.asyncAfter(
deadline: .now() + interval,
execute: recursiveCheck
)
}
timerQueue.async(execute: recursiveCheck)
@@ -626,7 +658,7 @@ extension DataStack {
}
timerQueue.sync {
fakeProgress = 1
fakeProgress.withLock({ $0 = 1.0 })
}
_ = try? storage.cs_finalizeStorageAndWait(soureModelHint: destinationModel)
progress.completedUnitCount = progress.totalUnitCount