From 7fdf52aa42865c5d02ae8911b0b17a1c778712d2 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Fri, 11 Oct 2024 12:58:04 -0400 Subject: [PATCH] improve-api --- Sources/DataThespian/BackgroundDatabase.swift | 1 - Sources/DataThespian/DataMonitor.swift | 2 - Sources/DataThespian/Database+Extras.swift | 186 +++++++++--------- Sources/DataThespian/DatabaseKey.swift | 3 - Sources/DataThespian/FetchDescriptor.swift | 1 - .../DataThespian/ManagedObjectMetadata.swift | 1 - Sources/DataThespian/Model.swift | 2 - .../DataThespian/ModelActor+Database.swift | 22 ++- Sources/DataThespian/ModelActorDatabase.swift | 84 ++++---- .../DataThespian/ModelContext+Extension.swift | 90 ++++----- Sources/DataThespian/NSManagedObjectID.swift | 2 - Sources/DataThespian/Notification.swift | 1 - .../DataThespian/NotificationDataUpdate.swift | 1 - Sources/DataThespian/PublishingAgent.swift | 2 - Sources/DataThespian/PublishingRegister.swift | 1 - .../DataThespian/RegistrationCollection.swift | 1 - 16 files changed, 195 insertions(+), 205 deletions(-) diff --git a/Sources/DataThespian/BackgroundDatabase.swift b/Sources/DataThespian/BackgroundDatabase.swift index ee9db06..10aa1d6 100644 --- a/Sources/DataThespian/BackgroundDatabase.swift +++ b/Sources/DataThespian/BackgroundDatabase.swift @@ -30,7 +30,6 @@ #if canImport(SwiftData) import Foundation public import SwiftData - import SwiftUI public final class BackgroundDatabase: Database { private actor DatabaseContainer { diff --git a/Sources/DataThespian/DataMonitor.swift b/Sources/DataThespian/DataMonitor.swift index e479705..413d691 100644 --- a/Sources/DataThespian/DataMonitor.swift +++ b/Sources/DataThespian/DataMonitor.swift @@ -30,9 +30,7 @@ #if canImport(Combine) && canImport(SwiftData) && canImport(CoreData) import Combine - import CoreData - import Foundation import SwiftData diff --git a/Sources/DataThespian/Database+Extras.swift b/Sources/DataThespian/Database+Extras.swift index 3a78d4f..15644f5 100644 --- a/Sources/DataThespian/Database+Extras.swift +++ b/Sources/DataThespian/Database+Extras.swift @@ -27,117 +27,119 @@ // OTHER DEALINGS IN THE SOFTWARE. // -public import Foundation -public import SwiftData - -extension Database { - public func insert( - _ closuer: @Sendable @escaping () -> PersistentModelType - ) async -> Model { - let id: PersistentIdentifier = await self.insert(closuer) - return .init(persistentIdentifier: id) - } +#if canImport(SwiftData) + public import Foundation + public import SwiftData + + extension Database { + public func insert( + _ closuer: @Sendable @escaping () -> PersistentModelType + ) async -> Model { + let id: PersistentIdentifier = await self.insert(closuer) + return .init(persistentIdentifier: id) + } - public func with( - _ id: Model, - _ closure: @escaping @Sendable (PersistentModelType) throws -> U - ) async rethrows -> U { - try await self.get(for: id.persistentIdentifier) { (model: PersistentModelType?) -> U in - guard let model else { - throw Model.NotFoundError( - persistentIdentifier: id.persistentIdentifier - ) + public func with( + _ id: Model, + _ closure: @escaping @Sendable (PersistentModelType) throws -> U + ) async rethrows -> U { + try await self.get(for: id.persistentIdentifier) { (model: PersistentModelType?) -> U in + guard let model else { + throw Model.NotFoundError( + persistentIdentifier: id.persistentIdentifier + ) + } + return try closure(model) } - return try closure(model) } - } - public func first(_ selectPredicate: Predicate) async throws -> Model? { - try await self.first(selectPredicate, with: Model.ifMap) - } - - public func first( - _ selectPredicate: Predicate, with closure: @escaping @Sendable (T?) throws -> U - ) async throws -> U { - try await self.fetch { - .init(predicate: selectPredicate, fetchLimit: 1) - } with: { models in - try closure(models.first) + public func first(_ selectPredicate: Predicate) async throws -> Model? { + try await self.first(selectPredicate, with: Model.ifMap) } - } - public func first( - fetchWith selectPredicate: Predicate, - otherwiseInsertBy insert: @Sendable @escaping () -> T, - with closure: @escaping @Sendable (T) throws -> U - ) async throws -> U { - let value = try await self.fetch { - .init(predicate: selectPredicate, fetchLimit: 1) - } with: { models in - try models.first.map(closure) - } - - if let value { - return value + public func first( + _ selectPredicate: Predicate, with closure: @escaping @Sendable (T?) throws -> U + ) async throws -> U { + try await self.fetch { + .init(predicate: selectPredicate, fetchLimit: 1) + } with: { models in + try closure(models.first) + } } - let inserted: Model = await self.insert(insert) + public func first( + fetchWith selectPredicate: Predicate, + otherwiseInsertBy insert: @Sendable @escaping () -> T, + with closure: @escaping @Sendable (T) throws -> U + ) async throws -> U { + let value = try await self.fetch { + .init(predicate: selectPredicate, fetchLimit: 1) + } with: { models in + try models.first.map(closure) + } - return try await self.with(inserted, closure) - } + if let value { + return value + } - public func delete(model _: T.Type, where predicate: Predicate? = nil) - async throws - { try await self.delete(where: predicate) } + let inserted: Model = await self.insert(insert) - public func delete(_ model: Model) async { - await self.delete(T.self, withID: model.persistentIdentifier) - } + return try await self.with(inserted, closure) + } - public func deleteAll(of types: [any PersistentModel.Type]) async throws { - try await self.transaction { context in for type in types { try context.delete(model: type) } } - } + public func delete(model _: T.Type, where predicate: Predicate? = nil) + async throws + { try await self.delete(where: predicate) } - public func fetch( - _: T.Type, with closure: @escaping @Sendable ([T]) throws -> U - ) async throws -> U { - try await self.fetch { - FetchDescriptor() - } with: { models in - try closure(models) + public func delete(_ model: Model) async { + await self.delete(T.self, withID: model.persistentIdentifier) } - } - public func fetch(_: T.Type) async throws -> [Model] { - try await self.fetch(T.self) { models in models.map(Model.init) } - } - public func fetch( - _: T.Type, _ selectDescriptor: @escaping @Sendable () -> FetchDescriptor - ) async throws -> [Model] { - await self.fetch(selectDescriptor) { models in models.map(Model.init) } - } + public func deleteAll(of types: [any PersistentModel.Type]) async throws { + try await self.transaction { context in for type in types { try context.delete(model: type) } } + } - public func fetch( - of _: T.Type, - for objectIDs: [PersistentIdentifier], - with closure: @escaping @Sendable (T) throws -> U - ) async throws -> [U] where T: PersistentModel { - try await withThrowingTaskGroup(of: U?.self, returning: [U].self) { group in - for id in objectIDs { - group.addTask { try await self.get(for: id) { model in try model.map(closure) } } + public func fetch( + _: T.Type, with closure: @escaping @Sendable ([T]) throws -> U + ) async throws -> U { + try await self.fetch { + FetchDescriptor() + } with: { models in + try closure(models) } + } + + public func fetch(_: T.Type) async throws -> [Model] { + try await self.fetch(T.self) { models in models.map(Model.init) } + } + public func fetch( + _: T.Type, _ selectDescriptor: @escaping @Sendable () -> FetchDescriptor + ) async throws -> [Model] { + await self.fetch(selectDescriptor) { models in models.map(Model.init) } + } - return try await group.reduce(into: []) { partialResult, item in - if let item { partialResult.append(item) } + public func fetch( + of _: T.Type, + for objectIDs: [PersistentIdentifier], + with closure: @escaping @Sendable (T) throws -> U + ) async throws -> [U] where T: PersistentModel { + try await withThrowingTaskGroup(of: U?.self, returning: [U].self) { group in + for id in objectIDs { + group.addTask { try await self.get(for: id) { model in try model.map(closure) } } + } + + return try await group.reduce(into: []) { partialResult, item in + if let item { partialResult.append(item) } + } } } - } - public func get( - of _: T.Type, - for objectID: PersistentIdentifier, - with closure: @escaping @Sendable (T?) throws -> U - ) async throws -> U where T: PersistentModel { - try await self.get(for: objectID) { model in try closure(model) } + public func get( + of _: T.Type, + for objectID: PersistentIdentifier, + with closure: @escaping @Sendable (T?) throws -> U + ) async throws -> U where T: PersistentModel { + try await self.get(for: objectID) { model in try closure(model) } + } } -} +#endif diff --git a/Sources/DataThespian/DatabaseKey.swift b/Sources/DataThespian/DatabaseKey.swift index 379cd25..3c2d0fd 100644 --- a/Sources/DataThespian/DatabaseKey.swift +++ b/Sources/DataThespian/DatabaseKey.swift @@ -28,11 +28,8 @@ // #if canImport(SwiftUI) - import Foundation - import SwiftData - public import SwiftUI private struct DefaultDatabase: Database { diff --git a/Sources/DataThespian/FetchDescriptor.swift b/Sources/DataThespian/FetchDescriptor.swift index 64b60c9..1ced50d 100644 --- a/Sources/DataThespian/FetchDescriptor.swift +++ b/Sources/DataThespian/FetchDescriptor.swift @@ -29,7 +29,6 @@ #if canImport(SwiftData) public import Foundation - public import SwiftData extension FetchDescriptor { diff --git a/Sources/DataThespian/ManagedObjectMetadata.swift b/Sources/DataThespian/ManagedObjectMetadata.swift index 8b667b2..1cc8202 100644 --- a/Sources/DataThespian/ManagedObjectMetadata.swift +++ b/Sources/DataThespian/ManagedObjectMetadata.swift @@ -28,7 +28,6 @@ // #if canImport(SwiftData) - public import SwiftData public struct ManagedObjectMetadata: Sendable, Hashable { diff --git a/Sources/DataThespian/Model.swift b/Sources/DataThespian/Model.swift index 342c84a..ed01dac 100644 --- a/Sources/DataThespian/Model.swift +++ b/Sources/DataThespian/Model.swift @@ -28,9 +28,7 @@ // #if canImport(SwiftData) - import Foundation - public import SwiftData @available(*, deprecated, renamed: "Model") diff --git a/Sources/DataThespian/ModelActor+Database.swift b/Sources/DataThespian/ModelActor+Database.swift index a9ffdef..6b1e1e1 100644 --- a/Sources/DataThespian/ModelActor+Database.swift +++ b/Sources/DataThespian/ModelActor+Database.swift @@ -27,16 +27,18 @@ // OTHER DEALINGS IN THE SOFTWARE. // -public import SwiftData +#if canImport(SwiftData) + public import SwiftData -extension ModelActor where Self: Database { - public static var assertIsBackground: Bool { false } + extension ModelActor where Self: Database { + public static var assertIsBackground: Bool { false } - public func withModelContext( - _ closure: @Sendable @escaping (ModelContext) throws -> T - ) async rethrows -> T { - assert(isMainThread: true, if: Self.assertIsBackground) - let modelContext = self.modelContext - return try closure(modelContext) + public func withModelContext( + _ closure: @Sendable @escaping (ModelContext) throws -> T + ) async rethrows -> T { + assert(isMainThread: true, if: Self.assertIsBackground) + let modelContext = self.modelContext + return try closure(modelContext) + } } -} +#endif diff --git a/Sources/DataThespian/ModelActorDatabase.swift b/Sources/DataThespian/ModelActorDatabase.swift index ce2d793..c451b99 100644 --- a/Sources/DataThespian/ModelActorDatabase.swift +++ b/Sources/DataThespian/ModelActorDatabase.swift @@ -27,54 +27,56 @@ // OTHER DEALINGS IN THE SOFTWARE. // -public import SwiftData +#if canImport(SwiftData) + public import SwiftData -// @ModelActor -// public actor ModelActorDatabase: Database {} + // @ModelActor + // public actor ModelActorDatabase: Database {} -public actor ModelActorDatabase: Database, ModelActor { - public nonisolated let modelExecutor: any SwiftData.ModelExecutor - public nonisolated let modelContainer: SwiftData.ModelContainer + public actor ModelActorDatabase: Database, ModelActor { + public nonisolated let modelExecutor: any SwiftData.ModelExecutor + public nonisolated let modelContainer: SwiftData.ModelContainer - private init(modelExecutor: any ModelExecutor, modelContainer: ModelContainer) { - self.modelExecutor = modelExecutor - self.modelContainer = modelContainer - } + private init(modelExecutor: any ModelExecutor, modelContainer: ModelContainer) { + self.modelExecutor = modelExecutor + self.modelContainer = modelContainer + } - public init(modelContainer: SwiftData.ModelContainer) { - self.init( - modelContainer: modelContainer, - modelContext: ModelContext.init - ) - } + public init(modelContainer: SwiftData.ModelContainer) { + self.init( + modelContainer: modelContainer, + modelContext: ModelContext.init + ) + } - public init( - modelContainer: SwiftData.ModelContainer, - modelContext closure: @Sendable @escaping (ModelContainer) -> ModelContext - ) { - self.init( - modelContainer: modelContainer, - modelExecutor: DefaultSerialModelExecutor.create(from: closure) - ) - } + public init( + modelContainer: SwiftData.ModelContainer, + modelContext closure: @Sendable @escaping (ModelContainer) -> ModelContext + ) { + self.init( + modelContainer: modelContainer, + modelExecutor: DefaultSerialModelExecutor.create(from: closure) + ) + } - public init( - modelContainer: SwiftData.ModelContainer, - modelExecutor closure: @Sendable @escaping (ModelContainer) -> any ModelExecutor - ) { - self.init( - modelExecutor: closure(modelContainer), - modelContainer: modelContainer - ) + public init( + modelContainer: SwiftData.ModelContainer, + modelExecutor closure: @Sendable @escaping (ModelContainer) -> any ModelExecutor + ) { + self.init( + modelExecutor: closure(modelContainer), + modelContainer: modelContainer + ) + } } -} -extension DefaultSerialModelExecutor { - fileprivate static func create( - from closure: @Sendable @escaping (ModelContainer) -> ModelContext - ) -> @Sendable (ModelContainer) -> any ModelExecutor { - { - DefaultSerialModelExecutor(modelContext: closure($0)) + extension DefaultSerialModelExecutor { + fileprivate static func create( + from closure: @Sendable @escaping (ModelContainer) -> ModelContext + ) -> @Sendable (ModelContainer) -> any ModelExecutor { + { + DefaultSerialModelExecutor(modelContext: closure($0)) + } } } -} +#endif diff --git a/Sources/DataThespian/ModelContext+Extension.swift b/Sources/DataThespian/ModelContext+Extension.swift index 21dd557..3013d03 100644 --- a/Sources/DataThespian/ModelContext+Extension.swift +++ b/Sources/DataThespian/ModelContext+Extension.swift @@ -27,54 +27,56 @@ // OTHER DEALINGS IN THE SOFTWARE. // -public import Foundation -public import SwiftData +#if canImport(SwiftData) + public import Foundation + public import SwiftData -extension ModelContext { - public func delete(_: T.Type, withID id: PersistentIdentifier) -> Bool { - guard let model: T = self.registeredModel(for: id) else { - return false + extension ModelContext { + public func delete(_: T.Type, withID id: PersistentIdentifier) -> Bool { + guard let model: T = self.registeredModel(for: id) else { + return false + } + self.delete(model) + return true } - self.delete(model) - return true - } - public func delete(where predicate: Predicate?) throws where T: PersistentModel { - try self.delete(model: T.self, where: predicate) - } + public func delete(where predicate: Predicate?) throws where T: PersistentModel { + try self.delete(model: T.self, where: predicate) + } - public func insert(_ closuer: @escaping @Sendable () -> some PersistentModel) - -> PersistentIdentifier - { - let model = closuer() - self.insert(model) - return model.persistentModelID - } - public func fetch( - _ selectDescriptor: @escaping @Sendable () -> FetchDescriptor, - with closure: @escaping @Sendable ([T]) throws -> U - ) throws -> U where T: PersistentModel, U: Sendable { - let models = try self.fetch(selectDescriptor()) - return try closure(models) - } - public func fetch( - _ selectDescriptorA: @escaping @Sendable () -> FetchDescriptor, - _ selectDescriptorB: @escaping @Sendable () -> FetchDescriptor, - with closure: @escaping @Sendable ([T], [U]) throws -> V - ) throws -> V { - let firstModels = try self.fetch(selectDescriptorA()) - let secondModels = try self.fetch(selectDescriptorB()) - return try closure(firstModels, secondModels) - } + public func insert(_ closuer: @escaping @Sendable () -> some PersistentModel) + -> PersistentIdentifier + { + let model = closuer() + self.insert(model) + return model.persistentModelID + } + public func fetch( + _ selectDescriptor: @escaping @Sendable () -> FetchDescriptor, + with closure: @escaping @Sendable ([T]) throws -> U + ) throws -> U where T: PersistentModel, U: Sendable { + let models = try self.fetch(selectDescriptor()) + return try closure(models) + } + public func fetch( + _ selectDescriptorA: @escaping @Sendable () -> FetchDescriptor, + _ selectDescriptorB: @escaping @Sendable () -> FetchDescriptor, + with closure: @escaping @Sendable ([T], [U]) throws -> V + ) throws -> V { + let firstModels = try self.fetch(selectDescriptorA()) + let secondModels = try self.fetch(selectDescriptorB()) + return try closure(firstModels, secondModels) + } - public func get( - for objectID: PersistentIdentifier, with closure: @escaping @Sendable (T?) throws -> U - ) throws -> U where T: PersistentModel, U: Sendable { - let model: T? = try self.existingModel(for: objectID) - return try closure(model) - } + public func get( + for objectID: PersistentIdentifier, with closure: @escaping @Sendable (T?) throws -> U + ) throws -> U where T: PersistentModel, U: Sendable { + let model: T? = try self.existingModel(for: objectID) + return try closure(model) + } - public func transaction(block: @escaping @Sendable (ModelContext) throws -> Void) throws { - try self.transaction { try block(self) } + public func transaction(block: @escaping @Sendable (ModelContext) throws -> Void) throws { + try self.transaction { try block(self) } + } } -} +#endif diff --git a/Sources/DataThespian/NSManagedObjectID.swift b/Sources/DataThespian/NSManagedObjectID.swift index 32aa376..f7e9303 100644 --- a/Sources/DataThespian/NSManagedObjectID.swift +++ b/Sources/DataThespian/NSManagedObjectID.swift @@ -29,9 +29,7 @@ #if canImport(CoreData) && canImport(SwiftData) public import CoreData - import Foundation - public import SwiftData // periphery:ignore diff --git a/Sources/DataThespian/Notification.swift b/Sources/DataThespian/Notification.swift index a338696..a1f1ce3 100644 --- a/Sources/DataThespian/Notification.swift +++ b/Sources/DataThespian/Notification.swift @@ -29,7 +29,6 @@ #if canImport(CoreData) import CoreData - import Foundation extension Notification { diff --git a/Sources/DataThespian/NotificationDataUpdate.swift b/Sources/DataThespian/NotificationDataUpdate.swift index 4fcce66..9d1133f 100644 --- a/Sources/DataThespian/NotificationDataUpdate.swift +++ b/Sources/DataThespian/NotificationDataUpdate.swift @@ -29,7 +29,6 @@ #if canImport(CoreData) && canImport(SwiftData) import CoreData - import Foundation internal struct NotificationDataUpdate: DatabaseChangeSet, Sendable { diff --git a/Sources/DataThespian/PublishingAgent.swift b/Sources/DataThespian/PublishingAgent.swift index a4e0909..ab019bb 100644 --- a/Sources/DataThespian/PublishingAgent.swift +++ b/Sources/DataThespian/PublishingAgent.swift @@ -28,9 +28,7 @@ // #if canImport(Combine) && canImport(SwiftData) - @preconcurrency import Combine - import Foundation internal actor PublishingAgent: DataAgent, Loggable { diff --git a/Sources/DataThespian/PublishingRegister.swift b/Sources/DataThespian/PublishingRegister.swift index 651ad47..a87f355 100644 --- a/Sources/DataThespian/PublishingRegister.swift +++ b/Sources/DataThespian/PublishingRegister.swift @@ -29,7 +29,6 @@ #if canImport(Combine) && canImport(SwiftData) @preconcurrency import Combine - import Foundation internal struct PublishingRegister: AgentRegister { diff --git a/Sources/DataThespian/RegistrationCollection.swift b/Sources/DataThespian/RegistrationCollection.swift index 721b32d..cadbbbd 100644 --- a/Sources/DataThespian/RegistrationCollection.swift +++ b/Sources/DataThespian/RegistrationCollection.swift @@ -28,7 +28,6 @@ // #if canImport(SwiftData) - import Foundation internal actor RegistrationCollection: Loggable {