diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index 76e70ec..ca9e981 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -3047,7 +3047,7 @@ SWIFT_COMPILATION_MODE = singlefile; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_SWIFT3_OBJC_INFERENCE = Off; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; TVOS_DEPLOYMENT_TARGET = 17.0; VERSIONING_SYSTEM = "apple-generic"; @@ -3112,7 +3112,7 @@ SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_SWIFT3_OBJC_INFERENCE = Off; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; TVOS_DEPLOYMENT_TARGET = 17.0; VALIDATE_PRODUCT = YES; diff --git a/Demo/Sources/Demos/Advanced/EvolutionDemo/Advanced.EvolutionDemo.CreaturesDataSource.swift b/Demo/Sources/Demos/Advanced/EvolutionDemo/Advanced.EvolutionDemo.CreaturesDataSource.swift index 2efc9e2..87e6c42 100644 --- a/Demo/Sources/Demos/Advanced/EvolutionDemo/Advanced.EvolutionDemo.CreaturesDataSource.swift +++ b/Demo/Sources/Demos/Advanced/EvolutionDemo/Advanced.EvolutionDemo.CreaturesDataSource.swift @@ -50,11 +50,11 @@ extension Advanced.EvolutionDemo { } self.mutateItemAtIndex = { index in - let object = listPublisher.snapshot[index] + let persistentID = listPublisher.snapshot[index].persistentID() dataStack.perform( asynchronous: { transaction in - object + persistentID .asEditable(in: transaction)? .mutate(in: transaction) }, diff --git a/Demo/Sources/Demos/Classic/ColorsDemo/Classic.ColorsDemo.ListViewController.swift b/Demo/Sources/Demos/Classic/ColorsDemo/Classic.ColorsDemo.ListViewController.swift index 940c160..3f24adb 100644 --- a/Demo/Sources/Demos/Classic/ColorsDemo/Classic.ColorsDemo.ListViewController.swift +++ b/Demo/Sources/Demos/Classic/ColorsDemo/Classic.ColorsDemo.ListViewController.swift @@ -26,7 +26,7 @@ extension Classic.ColorsDemo { /** ⭐️ Sample 2: We can end monitoring updates anytime. `removeObserver()` was called here for illustration purposes only. `ListMonitor`s safely remove deallocated observers automatically. */ - deinit { + isolated deinit { self.listMonitor.removeObserver(self) } @@ -40,59 +40,86 @@ extension Classic.ColorsDemo { typealias ListEntityType = Classic.ColorsDemo.Palette - func listMonitorWillChange(_ monitor: ListMonitor) { + nonisolated func listMonitorWillChange(_ monitor: ListMonitor) { - self.tableView.beginUpdates() + MainActor.assumeIsolated { + + self.tableView.beginUpdates() + } } - func listMonitorDidChange(_ monitor: ListMonitor) { + nonisolated func listMonitorDidChange(_ monitor: ListMonitor) { - self.tableView.endUpdates() + MainActor.assumeIsolated { + + self.tableView.endUpdates() + } } - func listMonitorDidRefetch(_ monitor: ListMonitor) { + nonisolated func listMonitorDidRefetch(_ monitor: ListMonitor) { - self.tableView.reloadData() + MainActor.assumeIsolated { + + self.tableView.reloadData() + } } // MARK: ListObjectObserver - func listMonitor(_ monitor: ListMonitor, didInsertObject object: ListEntityType, toIndexPath indexPath: IndexPath) { + nonisolated func listMonitor(_ monitor: ListMonitor, didInsertObject object: ListEntityType, toIndexPath indexPath: IndexPath) { - self.tableView.insertRows(at: [indexPath], with: .automatic) - } - - func listMonitor(_ monitor: ListMonitor, didDeleteObject object: ListEntityType, fromIndexPath indexPath: IndexPath) { - - self.tableView.deleteRows(at: [indexPath], with: .automatic) - } - - func listMonitor(_ monitor: ListMonitor, didUpdateObject object: ListEntityType, atIndexPath indexPath: IndexPath) { - - if case let cell as Classic.ColorsDemo.ItemCell = self.tableView.cellForRow(at: indexPath) { - - cell.setPalette(object) + MainActor.assumeIsolated { + + self.tableView.insertRows(at: [indexPath], with: .automatic) } } - func listMonitor(_ monitor: ListMonitor, didMoveObject object: ListEntityType, fromIndexPath: IndexPath, toIndexPath: IndexPath) { + nonisolated func listMonitor(_ monitor: ListMonitor, didDeleteObject object: ListEntityType, fromIndexPath indexPath: IndexPath) { - self.tableView.deleteRows(at: [fromIndexPath], with: .automatic) - self.tableView.insertRows(at: [toIndexPath], with: .automatic) + MainActor.assumeIsolated { + + self.tableView.deleteRows(at: [indexPath], with: .automatic) + } + } + + nonisolated func listMonitor(_ monitor: ListMonitor, didUpdateObject object: sending ListEntityType, atIndexPath indexPath: IndexPath) { + + MainActor.assumeIsolated { + + if case let cell as Classic.ColorsDemo.ItemCell = self.tableView.cellForRow(at: indexPath) { + + cell.setPalette(object) + } + } + } + + nonisolated func listMonitor(_ monitor: ListMonitor, didMoveObject object: ListEntityType, fromIndexPath: IndexPath, toIndexPath: IndexPath) { + + MainActor.assumeIsolated { + + self.tableView.deleteRows(at: [fromIndexPath], with: .automatic) + self.tableView.insertRows(at: [toIndexPath], with: .automatic) + } } // MARK: ListSectionObserver - func listMonitor(_ monitor: ListMonitor, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int) { + nonisolated func listMonitor(_ monitor: ListMonitor, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int) { - self.tableView.insertSections(IndexSet(integer: sectionIndex), with: .automatic) + MainActor.assumeIsolated { + + self.tableView.insertSections(IndexSet(integer: sectionIndex), with: .automatic) + } } - func listMonitor(_ monitor: ListMonitor, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int) { + nonisolated func listMonitor(_ monitor: ListMonitor, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int) { - self.tableView.deleteSections(IndexSet(integer: sectionIndex), with: .automatic) + MainActor.assumeIsolated { + + self.tableView.deleteSections(IndexSet(integer: sectionIndex), with: .automatic) + } } @@ -131,11 +158,11 @@ extension Classic.ColorsDemo { switch editingStyle { case .delete: - let object = self.listMonitor[indexPath] + let persistentID = self.listMonitor[indexPath].persistentID() Classic.ColorsDemo.dataStack.perform( asynchronous: { (transaction) in - transaction.delete(object) + transaction.delete(persistentID) }, completion: { _ in } ) diff --git a/Sources/CoreStoreObject.swift b/Sources/CoreStoreObject.swift index 2307beb..e848c85 100644 --- a/Sources/CoreStoreObject.swift +++ b/Sources/CoreStoreObject.swift @@ -65,7 +65,6 @@ open /*abstract*/ class CoreStoreObject: DynamicObject, Hashable { Do not call this directly. This is exposed as public only as a required initializer. - Important: subclasses that need a custom initializer should override both `init(rawObject:)` and `init(asMeta:)`, and to call their corresponding super implementations. */ - @_spi(Internals) public required init(rawObject: NSManagedObject) { self.isMeta = false @@ -85,7 +84,6 @@ open /*abstract*/ class CoreStoreObject: DynamicObject, Hashable { Do not call this directly. This is exposed as public only as a required initializer. - Important: subclasses that need a custom initializer should override both `init(rawObject:)` and `init(asMeta:)`, and to call their corresponding super implementations. */ - @_spi(Internals) public required init(asMeta: Void) { self.isMeta = true diff --git a/Sources/DataStack+Observing.swift b/Sources/DataStack+Observing.swift index 8dc9572..b16333e 100644 --- a/Sources/DataStack+Observing.swift +++ b/Sources/DataStack+Observing.swift @@ -46,7 +46,7 @@ extension DataStack { Thread.isMainThread, "Attempted to observe objects from \(Internals.typeName(self)) outside the main thread." ) - return .init(objectID: object.cs_id(), context: self.unsafeContext()) + return .init(managedObjectID: object.cs_id(), context: self.unsafeContext()) } /** diff --git a/Sources/DynamicObject.swift b/Sources/DynamicObject.swift index fb95c46..8641747 100644 --- a/Sources/DynamicObject.swift +++ b/Sources/DynamicObject.swift @@ -91,14 +91,6 @@ public nonisolated protocol DynamicObject: AnyObject, SendableMetatype { extension DynamicObject { - // MARK: Public - - public func persistentID() -> DynamicObjectID { - - return .init(managedObjectID: self.cs_id()) - } - - // MARK: Internal internal func runtimeType() -> Self.Type { diff --git a/Sources/DynamicObjectID.swift b/Sources/DynamicObjectID.swift index a2fb43f..0959c65 100644 --- a/Sources/DynamicObjectID.swift +++ b/Sources/DynamicObjectID.swift @@ -95,6 +95,11 @@ public struct DynamicObjectID: Hashable, ObjectRepresentation, public typealias ObjectType = O + public func persistentID() -> ObjectType.ObjectID { + + return self + } + public func asPublisher(in dataStack: DataStack) -> ObjectPublisher { let context = dataStack.unsafeContext() diff --git a/Sources/ObjectMonitor.swift b/Sources/ObjectMonitor.swift index cb7c319..cc994e1 100644 --- a/Sources/ObjectMonitor.swift +++ b/Sources/ObjectMonitor.swift @@ -170,7 +170,7 @@ public final class ObjectMonitor: Hashable, ObjectRepresentati @_spi(Internals) public func cs_id() -> NSManagedObjectID { - return self.id + return self.managedObjectID } @_spi(Internals) @@ -184,43 +184,48 @@ public final class ObjectMonitor: Hashable, ObjectRepresentati public typealias ObjectType = O + public func persistentID() -> O.ObjectID { + + return .init(managedObjectID: self.managedObjectID) + } + public func asPublisher(in dataStack: DataStack) -> ObjectPublisher { - return dataStack.unsafeContext().objectPublisher(managedObjectID: self.id) + return dataStack.unsafeContext().objectPublisher(managedObjectID: self.managedObjectID) } public func asReadOnly(in dataStack: DataStack) -> O? { - return dataStack.unsafeContext().fetchExisting(self.id) + return dataStack.unsafeContext().fetchExisting(self.managedObjectID) } public func asEditable(in transaction: BaseDataTransaction) -> O? { - return transaction.unsafeContext().fetchExisting(self.id) + return transaction.unsafeContext().fetchExisting(self.managedObjectID) } public func asSnapshot(in dataStack: DataStack) -> ObjectSnapshot? { let context = dataStack.unsafeContext() - return ObjectSnapshot(managedObjectID: self.id, context: context) + return ObjectSnapshot(managedObjectID: self.managedObjectID, context: context) } public func asSnapshot(in transaction: BaseDataTransaction) -> ObjectSnapshot? { let context = transaction.unsafeContext() - return ObjectSnapshot(managedObjectID: self.id, context: context) + return ObjectSnapshot(managedObjectID: self.managedObjectID, context: context) } // MARK: Internal internal init( - objectID: NSManagedObjectID, + managedObjectID: NSManagedObjectID, context: NSManagedObjectContext ) { let fetchRequest = Internals.CoreStoreFetchRequest() - fetchRequest.entity = objectID.entity + fetchRequest.entity = managedObjectID.entity fetchRequest.fetchLimit = 0 fetchRequest.resultType = .managedObjectResultType fetchRequest.sortDescriptors = [] @@ -230,13 +235,13 @@ public final class ObjectMonitor: Hashable, ObjectRepresentati let fetchedResultsController = Internals.CoreStoreFetchedResultsController( context: context, fetchRequest: fetchRequest, - from: From([objectID.persistentStore?.configurationName]), - applyFetchClauses: Where("SELF", isEqualTo: objectID).applyToFetchRequest + from: From([managedObjectID.persistentStore?.configurationName]), + applyFetchClauses: Where("SELF", isEqualTo: managedObjectID).applyToFetchRequest ) let fetchedResultsControllerDelegate = Internals.FetchedResultsControllerDelegate() - self.id = objectID + self.managedObjectID = managedObjectID self.fetchedResultsController = fetchedResultsController self.fetchedResultsControllerDelegate = fetchedResultsControllerDelegate @@ -355,7 +360,7 @@ public final class ObjectMonitor: Hashable, ObjectRepresentati // MARK: Private - private let id: NSManagedObjectID + private let managedObjectID: NSManagedObjectID private let fetchedResultsController: Internals.CoreStoreFetchedResultsController private let fetchedResultsControllerDelegate: Internals.FetchedResultsControllerDelegate private let lastCommittedAttributes: Internals.Mutex<[String: NSObject]> = .init([:]) diff --git a/Sources/ObjectPublisher.swift b/Sources/ObjectPublisher.swift index 12d2746..c239ccc 100644 --- a/Sources/ObjectPublisher.swift +++ b/Sources/ObjectPublisher.swift @@ -177,6 +177,11 @@ public final class ObjectPublisher: ObjectRepresentation, Hash public typealias ObjectType = O + public func persistentID() -> O.ObjectID { + + return .init(managedObjectID: self.managedObjectID) + } + public func asPublisher(in dataStack: DataStack) -> ObjectPublisher { let context = dataStack.unsafeContext() diff --git a/Sources/ObjectRepresentation.swift b/Sources/ObjectRepresentation.swift index c30114f..38ff5bc 100644 --- a/Sources/ObjectRepresentation.swift +++ b/Sources/ObjectRepresentation.swift @@ -59,6 +59,11 @@ public protocol ObjectRepresentation: AnyObjectRepresentation { */ associatedtype ObjectType: DynamicObject + /** + The `Sendable` ID to use for referencing the object between isolation contexts. + */ + func persistentID() -> ObjectType.ObjectID + /** An instance that may be observed for object changes. */ @@ -85,6 +90,7 @@ public protocol ObjectRepresentation: AnyObjectRepresentation { func asSnapshot(in transaction: BaseDataTransaction) -> ObjectSnapshot? } + extension NSManagedObject: ObjectRepresentation {} extension CoreStoreObject: ObjectRepresentation {} @@ -92,6 +98,14 @@ extension CoreStoreObject: ObjectRepresentation {} extension DynamicObject where Self: ObjectRepresentation { // MARK: Public + + /** + The `Sendable` ID to use for referencing the object between isolation contexts. + */ + public func persistentID() -> Self.ObjectID { + + return .init(managedObjectID: self.cs_id()) + } /** An `ObjectPublisher` wrapper for the exact same object diff --git a/Sources/ObjectSnapshot.swift b/Sources/ObjectSnapshot.swift index f3c7a96..042329e 100644 --- a/Sources/ObjectSnapshot.swift +++ b/Sources/ObjectSnapshot.swift @@ -69,6 +69,11 @@ public struct ObjectSnapshot: ObjectRepresentation, Hashable, public typealias ObjectType = O + public func persistentID() -> ObjectType.ObjectID { + + return .init(managedObjectID: self.managedObjectID) + } + public func asPublisher(in dataStack: DataStack) -> ObjectPublisher { let context = dataStack.unsafeContext() diff --git a/Sources/UnsafeDataTransaction+Observing.swift b/Sources/UnsafeDataTransaction+Observing.swift index b56ae47..a62c7a4 100644 --- a/Sources/UnsafeDataTransaction+Observing.swift +++ b/Sources/UnsafeDataTransaction+Observing.swift @@ -41,7 +41,7 @@ extension UnsafeDataTransaction { _ object: O ) -> ObjectMonitor { - return .init(objectID: object.cs_id(), context: self.unsafeContext()) + return .init(managedObjectID: object.cs_id(), context: self.unsafeContext()) } /**