diff --git a/Sources/CoreDataRepository/CoreDataRepository+Aggregate.swift b/Sources/CoreDataRepository/CoreDataRepository+Aggregate.swift index fa27ba5..b8e982c 100644 --- a/Sources/CoreDataRepository/CoreDataRepository+Aggregate.swift +++ b/Sources/CoreDataRepository/CoreDataRepository+Aggregate.swift @@ -73,15 +73,22 @@ extension CoreDataRepository { as _: Value.Type ) -> AsyncStream> { AsyncStream { continuation in - let subscription = AggregateSubscription( - function: .average, - context: context.childContext(), - predicate: predicate, - entityDesc: entityDesc, - attributeDesc: attributeDesc, - groupBy: groupBy, - continuation: continuation - ) + let subscription: AggregateSubscription + do throws(CoreDataError) { + subscription = try AggregateSubscription( + function: .average, + context: context.childContext(), + predicate: predicate, + entityDesc: entityDesc, + attributeDesc: attributeDesc, + groupBy: groupBy, + continuation: continuation + ) + } catch { + continuation.yield(.failure(error)) + continuation.finish() + return + } continuation.onTermination = { _ in subscription.cancel() } @@ -104,16 +111,23 @@ extension CoreDataRepository { as _: Value.Type ) -> AsyncStream> { AsyncStream { continuation in - let subscription = AggregateSubscription( - function: .average, - context: context.childContext(), - predicate: predicate, - changeTrackingRequest: changeTrackingRequest, - entityDesc: entityDesc, - attributeDesc: attributeDesc, - groupBy: groupBy, - continuation: continuation - ) + let subscription: AggregateSubscription + do throws(CoreDataError) { + subscription = try AggregateSubscription( + function: .average, + context: context.childContext(), + predicate: predicate, + changeTrackingRequest: changeTrackingRequest, + entityDesc: entityDesc, + attributeDesc: attributeDesc, + groupBy: groupBy, + continuation: continuation + ) + } catch { + continuation.yield(.failure(error)) + continuation.finish() + return + } continuation.onTermination = { _ in subscription.cancel() } @@ -131,15 +145,21 @@ extension CoreDataRepository { as _: Value.Type ) -> AsyncThrowingStream { AsyncThrowingStream { continuation in - let subscription = AggregateThrowingSubscription( - function: .average, - context: context.childContext(), - predicate: predicate, - entityDesc: entityDesc, - attributeDesc: attributeDesc, - groupBy: groupBy, - continuation: continuation - ) + let subscription: AggregateThrowingSubscription + do { + subscription = try AggregateThrowingSubscription( + function: .average, + context: context.childContext(), + predicate: predicate, + entityDesc: entityDesc, + attributeDesc: attributeDesc, + groupBy: groupBy, + continuation: continuation + ) + } catch { + continuation.finish(throwing: error) + return + } continuation.onTermination = { _ in subscription.cancel() } @@ -162,16 +182,22 @@ extension CoreDataRepository { as _: Value.Type ) -> AsyncThrowingStream { AsyncThrowingStream { continuation in - let subscription = AggregateThrowingSubscription( - function: .average, - context: context.childContext(), - predicate: predicate, - changeTrackingRequest: changeTrackingRequest, - entityDesc: entityDesc, - attributeDesc: attributeDesc, - groupBy: groupBy, - continuation: continuation - ) + let subscription: AggregateThrowingSubscription + do { + subscription = try AggregateThrowingSubscription( + function: .average, + context: context.childContext(), + predicate: predicate, + changeTrackingRequest: changeTrackingRequest, + entityDesc: entityDesc, + attributeDesc: attributeDesc, + groupBy: groupBy, + continuation: continuation + ) + } catch { + continuation.finish(throwing: error) + return + } continuation.onTermination = { _ in subscription.cancel() } @@ -194,6 +220,8 @@ extension CoreDataRepository { .countRequest(predicate: predicate, entityDesc: entityDesc) let count = try scratchPad.count(for: request) return Value(exactly: count) ?? Value.zero + } catch let error as CoreDataError { + throw error } catch let error as CocoaError { throw CoreDataError.cocoa(error) } catch { @@ -210,12 +238,19 @@ extension CoreDataRepository { as _: Value.Type ) -> AsyncStream> { AsyncStream { continuation in - let subscription = CountSubscription( - context: context.childContext(), - predicate: predicate, - entityDesc: entityDesc, - continuation: continuation - ) + let subscription: CountSubscription + do throws(CoreDataError) { + subscription = try CountSubscription( + context: context.childContext(), + predicate: predicate, + entityDesc: entityDesc, + continuation: continuation + ) + } catch { + continuation.yield(.failure(error)) + continuation.finish() + return + } continuation.onTermination = { _ in subscription.cancel() } @@ -236,13 +271,20 @@ extension CoreDataRepository { as _: Value.Type ) -> AsyncStream> { AsyncStream { continuation in - let subscription = CountSubscription( - context: context.childContext(), - predicate: predicate, - changeTrackingRequest: changeTrackingRequest, - entityDesc: entityDesc, - continuation: continuation - ) + let subscription: CountSubscription + do throws(CoreDataError) { + subscription = try CountSubscription( + context: context.childContext(), + predicate: predicate, + changeTrackingRequest: changeTrackingRequest, + entityDesc: entityDesc, + continuation: continuation + ) + } catch { + continuation.yield(.failure(error)) + continuation.finish() + return + } continuation.onTermination = { _ in subscription.cancel() } @@ -258,12 +300,18 @@ extension CoreDataRepository { as _: Value.Type ) -> AsyncThrowingStream { AsyncThrowingStream { continuation in - let subscription = CountThrowingSubscription( - context: context.childContext(), - predicate: predicate, - entityDesc: entityDesc, - continuation: continuation - ) + let subscription: CountThrowingSubscription + do { + subscription = try CountThrowingSubscription( + context: context.childContext(), + predicate: predicate, + entityDesc: entityDesc, + continuation: continuation + ) + } catch { + continuation.finish(throwing: error) + return + } continuation.onTermination = { _ in subscription.cancel() } @@ -284,13 +332,19 @@ extension CoreDataRepository { as _: Value.Type ) -> AsyncThrowingStream { AsyncThrowingStream { continuation in - let subscription = CountThrowingSubscription( - context: context.childContext(), - predicate: predicate, - changeTrackingRequest: changeTrackingRequest, - entityDesc: entityDesc, - continuation: continuation - ) + let subscription: CountThrowingSubscription + do { + subscription = try CountThrowingSubscription( + context: context.childContext(), + predicate: predicate, + changeTrackingRequest: changeTrackingRequest, + entityDesc: entityDesc, + continuation: continuation + ) + } catch { + continuation.finish(throwing: error) + return + } continuation.onTermination = { _ in subscription.cancel() } @@ -330,15 +384,22 @@ extension CoreDataRepository { as _: Value.Type ) -> AsyncStream> { AsyncStream { continuation in - let subscription = AggregateSubscription( - function: .max, - context: context.childContext(), - predicate: predicate, - entityDesc: entityDesc, - attributeDesc: attributeDesc, - groupBy: groupBy, - continuation: continuation - ) + let subscription: AggregateSubscription + do throws(CoreDataError) { + subscription = try AggregateSubscription( + function: .max, + context: context.childContext(), + predicate: predicate, + entityDesc: entityDesc, + attributeDesc: attributeDesc, + groupBy: groupBy, + continuation: continuation + ) + } catch { + continuation.yield(.failure(error)) + continuation.finish() + return + } continuation.onTermination = { _ in subscription.cancel() } @@ -362,16 +423,23 @@ extension CoreDataRepository { as _: Value.Type ) -> AsyncStream> { AsyncStream { continuation in - let subscription = AggregateSubscription( - function: .max, - context: context.childContext(), - predicate: predicate, - changeTrackingRequest: changeTrackingRequest, - entityDesc: entityDesc, - attributeDesc: attributeDesc, - groupBy: groupBy, - continuation: continuation - ) + let subscription: AggregateSubscription + do throws(CoreDataError) { + subscription = try AggregateSubscription( + function: .max, + context: context.childContext(), + predicate: predicate, + changeTrackingRequest: changeTrackingRequest, + entityDesc: entityDesc, + attributeDesc: attributeDesc, + groupBy: groupBy, + continuation: continuation + ) + } catch { + continuation.yield(.failure(error)) + continuation.finish() + return + } continuation.onTermination = { _ in subscription.cancel() } @@ -390,15 +458,21 @@ extension CoreDataRepository { as _: Value.Type ) -> AsyncThrowingStream { AsyncThrowingStream { continuation in - let subscription = AggregateThrowingSubscription( - function: .max, - context: context.childContext(), - predicate: predicate, - entityDesc: entityDesc, - attributeDesc: attributeDesc, - groupBy: groupBy, - continuation: continuation - ) + let subscription: AggregateThrowingSubscription + do { + subscription = try AggregateThrowingSubscription( + function: .max, + context: context.childContext(), + predicate: predicate, + entityDesc: entityDesc, + attributeDesc: attributeDesc, + groupBy: groupBy, + continuation: continuation + ) + } catch { + continuation.finish(throwing: error) + return + } continuation.onTermination = { _ in subscription.cancel() } @@ -422,16 +496,22 @@ extension CoreDataRepository { as _: Value.Type ) -> AsyncThrowingStream { AsyncThrowingStream { continuation in - let subscription = AggregateThrowingSubscription( - function: .max, - context: context.childContext(), - predicate: predicate, - changeTrackingRequest: changeTrackingRequest, - entityDesc: entityDesc, - attributeDesc: attributeDesc, - groupBy: groupBy, - continuation: continuation - ) + let subscription: AggregateThrowingSubscription + do { + subscription = try AggregateThrowingSubscription( + function: .max, + context: context.childContext(), + predicate: predicate, + changeTrackingRequest: changeTrackingRequest, + entityDesc: entityDesc, + attributeDesc: attributeDesc, + groupBy: groupBy, + continuation: continuation + ) + } catch { + continuation.finish(throwing: error) + return + } continuation.onTermination = { _ in subscription.cancel() } @@ -471,15 +551,22 @@ extension CoreDataRepository { as _: Value.Type ) -> AsyncStream> { AsyncStream { continuation in - let subscription = AggregateSubscription( - function: .min, - context: context.childContext(), - predicate: predicate, - entityDesc: entityDesc, - attributeDesc: attributeDesc, - groupBy: groupBy, - continuation: continuation - ) + let subscription: AggregateSubscription + do throws(CoreDataError) { + subscription = try AggregateSubscription( + function: .min, + context: context.childContext(), + predicate: predicate, + entityDesc: entityDesc, + attributeDesc: attributeDesc, + groupBy: groupBy, + continuation: continuation + ) + } catch { + continuation.yield(.failure(error)) + continuation.finish() + return + } continuation.onTermination = { _ in subscription.cancel() } @@ -503,16 +590,23 @@ extension CoreDataRepository { as _: Value.Type ) -> AsyncStream> { AsyncStream { continuation in - let subscription = AggregateSubscription( - function: .min, - context: context.childContext(), - predicate: predicate, - changeTrackingRequest: changeTrackingRequest, - entityDesc: entityDesc, - attributeDesc: attributeDesc, - groupBy: groupBy, - continuation: continuation - ) + let subscription: AggregateSubscription + do throws(CoreDataError) { + subscription = try AggregateSubscription( + function: .min, + context: context.childContext(), + predicate: predicate, + changeTrackingRequest: changeTrackingRequest, + entityDesc: entityDesc, + attributeDesc: attributeDesc, + groupBy: groupBy, + continuation: continuation + ) + } catch { + continuation.yield(.failure(error)) + continuation.finish() + return + } continuation.onTermination = { _ in subscription.cancel() } @@ -531,15 +625,21 @@ extension CoreDataRepository { as _: Value.Type ) -> AsyncThrowingStream { AsyncThrowingStream { continuation in - let subscription = AggregateThrowingSubscription( - function: .min, - context: context.childContext(), - predicate: predicate, - entityDesc: entityDesc, - attributeDesc: attributeDesc, - groupBy: groupBy, - continuation: continuation - ) + let subscription: AggregateThrowingSubscription + do { + subscription = try AggregateThrowingSubscription( + function: .min, + context: context.childContext(), + predicate: predicate, + entityDesc: entityDesc, + attributeDesc: attributeDesc, + groupBy: groupBy, + continuation: continuation + ) + } catch { + continuation.finish(throwing: error) + return + } continuation.onTermination = { _ in subscription.cancel() } @@ -563,16 +663,22 @@ extension CoreDataRepository { as _: Value.Type ) -> AsyncThrowingStream { AsyncThrowingStream { continuation in - let subscription = AggregateThrowingSubscription( - function: .min, - context: context.childContext(), - predicate: predicate, - changeTrackingRequest: changeTrackingRequest, - entityDesc: entityDesc, - attributeDesc: attributeDesc, - groupBy: groupBy, - continuation: continuation - ) + let subscription: AggregateThrowingSubscription + do { + subscription = try AggregateThrowingSubscription( + function: .min, + context: context.childContext(), + predicate: predicate, + changeTrackingRequest: changeTrackingRequest, + entityDesc: entityDesc, + attributeDesc: attributeDesc, + groupBy: groupBy, + continuation: continuation + ) + } catch { + continuation.finish(throwing: error) + return + } continuation.onTermination = { _ in subscription.cancel() } @@ -611,15 +717,22 @@ extension CoreDataRepository { as _: Value.Type ) -> AsyncStream> { AsyncStream { continuation in - let subscription = AggregateSubscription( - function: .sum, - context: context.childContext(), - predicate: predicate, - entityDesc: entityDesc, - attributeDesc: attributeDesc, - groupBy: groupBy, - continuation: continuation - ) + let subscription: AggregateSubscription + do throws(CoreDataError) { + subscription = try AggregateSubscription( + function: .sum, + context: context.childContext(), + predicate: predicate, + entityDesc: entityDesc, + attributeDesc: attributeDesc, + groupBy: groupBy, + continuation: continuation + ) + } catch { + continuation.yield(.failure(error)) + continuation.finish() + return + } continuation.onTermination = { _ in subscription.cancel() } @@ -642,16 +755,23 @@ extension CoreDataRepository { as _: Value.Type ) -> AsyncStream> { AsyncStream { continuation in - let subscription = AggregateSubscription( - function: .sum, - context: context.childContext(), - predicate: predicate, - changeTrackingRequest: changeTrackingRequest, - entityDesc: entityDesc, - attributeDesc: attributeDesc, - groupBy: groupBy, - continuation: continuation - ) + let subscription: AggregateSubscription + do throws(CoreDataError) { + subscription = try AggregateSubscription( + function: .sum, + context: context.childContext(), + predicate: predicate, + changeTrackingRequest: changeTrackingRequest, + entityDesc: entityDesc, + attributeDesc: attributeDesc, + groupBy: groupBy, + continuation: continuation + ) + } catch { + continuation.yield(.failure(error)) + continuation.finish() + return + } continuation.onTermination = { _ in subscription.cancel() } @@ -669,15 +789,21 @@ extension CoreDataRepository { as _: Value.Type ) -> AsyncThrowingStream { AsyncThrowingStream { continuation in - let subscription = AggregateThrowingSubscription( - function: .sum, - context: context.childContext(), - predicate: predicate, - entityDesc: entityDesc, - attributeDesc: attributeDesc, - groupBy: groupBy, - continuation: continuation - ) + let subscription: AggregateThrowingSubscription + do { + subscription = try AggregateThrowingSubscription( + function: .sum, + context: context.childContext(), + predicate: predicate, + entityDesc: entityDesc, + attributeDesc: attributeDesc, + groupBy: groupBy, + continuation: continuation + ) + } catch { + continuation.finish(throwing: error) + return + } continuation.onTermination = { _ in subscription.cancel() } @@ -700,16 +826,22 @@ extension CoreDataRepository { as _: Value.Type ) -> AsyncThrowingStream { AsyncThrowingStream { continuation in - let subscription = AggregateThrowingSubscription( - function: .sum, - context: context.childContext(), - predicate: predicate, - changeTrackingRequest: changeTrackingRequest, - entityDesc: entityDesc, - attributeDesc: attributeDesc, - groupBy: groupBy, - continuation: continuation - ) + let subscription: AggregateThrowingSubscription + do { + subscription = try AggregateThrowingSubscription( + function: .sum, + context: context.childContext(), + predicate: predicate, + changeTrackingRequest: changeTrackingRequest, + entityDesc: entityDesc, + attributeDesc: attributeDesc, + groupBy: groupBy, + continuation: continuation + ) + } catch { + continuation.finish(throwing: error) + return + } continuation.onTermination = { _ in subscription.cancel() } @@ -765,6 +897,8 @@ extension CoreDataRepository { return try Self.aggregate(context: scratchPad, request: request) } catch let error as CocoaError { throw CoreDataError.cocoa(error) + } catch let error as CoreDataError { + throw error } catch { throw CoreDataError.unknown(error as NSError) } diff --git a/Sources/CoreDataRepository/Internal/AggregateSubscription.swift b/Sources/CoreDataRepository/Internal/AggregateSubscription.swift index 60ae717..79a38bb 100644 --- a/Sources/CoreDataRepository/Internal/AggregateSubscription.swift +++ b/Sources/CoreDataRepository/Internal/AggregateSubscription.swift @@ -48,48 +48,25 @@ final class AggregateSubscription: Subscription>.Continuation - ) { - let request: NSFetchRequest - do { - request = try NSFetchRequest.request( - function: function, - predicate: predicate, - entityDesc: entityDesc, - attributeDesc: attributeDesc, - groupBy: groupBy - ) - } catch { - self.init( - fetchRequest: NSFetchRequest(), - fetchResultControllerRequest: NSFetchRequest(), - context: context, - continuation: continuation - ) - fail(error) - return - } + ) throws(CoreDataError) { + let request: NSFetchRequest = try NSFetchRequest.request( + function: function, + predicate: predicate, + entityDesc: entityDesc, + attributeDesc: attributeDesc, + groupBy: groupBy + ) guard entityDesc == attributeDesc.entity else { - self.init( - fetchRequest: NSFetchRequest(), - fetchResultControllerRequest: NSFetchRequest(), - context: context, - continuation: continuation - ) guard let entityName = entityDesc.name ?? entityDesc.managedObjectClassName else { - fail(.propertyDoesNotMatchEntity(description: nil)) - return + throw CoreDataError.propertyDoesNotMatchEntity(description: nil) } guard let attributeEntityName = attributeDesc.entity.name ?? attributeDesc.entity.managedObjectClassName else { - fail(.propertyDoesNotMatchEntity(description: entityName)) - return + throw CoreDataError.propertyDoesNotMatchEntity(description: entityName) } - fail( - .propertyDoesNotMatchEntity( - description: "\(entityName) != \(attributeDesc.name).\(attributeEntityName)" - ) + throw CoreDataError.propertyDoesNotMatchEntity( + description: "\(entityName) != \(attributeDesc.name).\(attributeEntityName)" ) - return } self.init(request: request, context: context, continuation: continuation) } @@ -104,48 +81,25 @@ final class AggregateSubscription: Subscription>.Continuation - ) { - let request: NSFetchRequest - do { - request = try NSFetchRequest.request( - function: function, - predicate: predicate, - entityDesc: entityDesc, - attributeDesc: attributeDesc, - groupBy: groupBy - ) - } catch { - self.init( - fetchRequest: NSFetchRequest(), - fetchResultControllerRequest: NSFetchRequest(), - context: context, - continuation: continuation - ) - fail(error) - return - } + ) throws(CoreDataError) { + let request: NSFetchRequest = try NSFetchRequest.request( + function: function, + predicate: predicate, + entityDesc: entityDesc, + attributeDesc: attributeDesc, + groupBy: groupBy + ) guard entityDesc == attributeDesc.entity else { - self.init( - fetchRequest: NSFetchRequest(), - fetchResultControllerRequest: NSFetchRequest(), - context: context, - continuation: continuation - ) guard let entityName = entityDesc.name ?? entityDesc.managedObjectClassName else { - fail(.propertyDoesNotMatchEntity(description: nil)) - return + throw CoreDataError.propertyDoesNotMatchEntity(description: nil) } guard let attributeEntityName = attributeDesc.entity.name ?? attributeDesc.entity.managedObjectClassName else { - fail(.propertyDoesNotMatchEntity(description: entityName)) - return + throw CoreDataError.propertyDoesNotMatchEntity(description: entityName) } - fail( - .propertyDoesNotMatchEntity( - description: "\(entityName) != \(attributeDesc.name).\(attributeEntityName)" - ) + throw CoreDataError.propertyDoesNotMatchEntity( + description: "\(entityName) != \(attributeDesc.name).\(attributeEntityName)" ) - return } self.init( fetchRequest: request, diff --git a/Sources/CoreDataRepository/Internal/AggregateThrowingSubscription.swift b/Sources/CoreDataRepository/Internal/AggregateThrowingSubscription.swift index 41af4da..0987b6f 100644 --- a/Sources/CoreDataRepository/Internal/AggregateThrowingSubscription.swift +++ b/Sources/CoreDataRepository/Internal/AggregateThrowingSubscription.swift @@ -52,48 +52,25 @@ final class AggregateThrowingSubscription: ThrowingSu attributeDesc: NSAttributeDescription, groupBy: NSAttributeDescription? = nil, continuation: AsyncThrowingStream.Continuation - ) { - let request: NSFetchRequest - do { - request = try NSFetchRequest.request( - function: function, - predicate: predicate, - entityDesc: entityDesc, - attributeDesc: attributeDesc, - groupBy: groupBy - ) - } catch { - self.init( - fetchRequest: NSFetchRequest(), - fetchResultControllerRequest: NSFetchRequest(), - context: context, - continuation: continuation - ) - fail(error) - return - } + ) throws(CoreDataError) { + let request: NSFetchRequest = try NSFetchRequest.request( + function: function, + predicate: predicate, + entityDesc: entityDesc, + attributeDesc: attributeDesc, + groupBy: groupBy + ) guard entityDesc == attributeDesc.entity else { - self.init( - fetchRequest: NSFetchRequest(), - fetchResultControllerRequest: NSFetchRequest(), - context: context, - continuation: continuation - ) guard let entityName = entityDesc.name ?? entityDesc.managedObjectClassName else { - fail(.propertyDoesNotMatchEntity(description: nil)) - return + throw CoreDataError.propertyDoesNotMatchEntity(description: nil) } guard let attributeEntityName = attributeDesc.entity.name ?? attributeDesc.entity.managedObjectClassName else { - fail(.propertyDoesNotMatchEntity(description: entityName)) - return + throw CoreDataError.propertyDoesNotMatchEntity(description: entityName) } - fail( - .propertyDoesNotMatchEntity( - description: "\(entityName) != \(attributeDesc.name).\(attributeEntityName)" - ) + throw CoreDataError.propertyDoesNotMatchEntity( + description: "\(entityName) != \(attributeDesc.name).\(attributeEntityName)" ) - return } self.init(request: request, context: context, continuation: continuation) } @@ -108,48 +85,25 @@ final class AggregateThrowingSubscription: ThrowingSu attributeDesc: NSAttributeDescription, groupBy: NSAttributeDescription? = nil, continuation: AsyncThrowingStream.Continuation - ) { - let request: NSFetchRequest - do { - request = try NSFetchRequest.request( - function: function, - predicate: predicate, - entityDesc: entityDesc, - attributeDesc: attributeDesc, - groupBy: groupBy - ) - } catch { - self.init( - fetchRequest: NSFetchRequest(), - fetchResultControllerRequest: NSFetchRequest(), - context: context, - continuation: continuation - ) - fail(error) - return - } + ) throws(CoreDataError) { + let request: NSFetchRequest = try NSFetchRequest.request( + function: function, + predicate: predicate, + entityDesc: entityDesc, + attributeDesc: attributeDesc, + groupBy: groupBy + ) guard entityDesc == attributeDesc.entity else { - self.init( - fetchRequest: NSFetchRequest(), - fetchResultControllerRequest: NSFetchRequest(), - context: context, - continuation: continuation - ) guard let entityName = entityDesc.name ?? entityDesc.managedObjectClassName else { - fail(.propertyDoesNotMatchEntity(description: nil)) - return + throw CoreDataError.propertyDoesNotMatchEntity(description: nil) } guard let attributeEntityName = attributeDesc.entity.name ?? attributeDesc.entity.managedObjectClassName else { - fail(.propertyDoesNotMatchEntity(description: entityName)) - return + throw CoreDataError.propertyDoesNotMatchEntity(description: entityName) } - fail( - .propertyDoesNotMatchEntity( - description: "\(entityName) != \(attributeDesc.name).\(attributeEntityName)" - ) + throw CoreDataError.propertyDoesNotMatchEntity( + description: "\(entityName) != \(attributeDesc.name).\(attributeEntityName)" ) - return } self.init( fetchRequest: request, diff --git a/Sources/CoreDataRepository/Internal/BaseSubscription.swift b/Sources/CoreDataRepository/Internal/BaseSubscription.swift index 822d9f0..e9a3bd1 100644 --- a/Sources/CoreDataRepository/Internal/BaseSubscription.swift +++ b/Sources/CoreDataRepository/Internal/BaseSubscription.swift @@ -23,6 +23,14 @@ class BaseSubscription< fetchResultControllerRequest: NSFetchRequest, context: NSManagedObjectContext ) { + if fetchRequest.sortDescriptors == nil { + // if `sortDescriptors` is `nil`, an exception is raised and causes a runtime crash + fetchRequest.sortDescriptors = [] + } + if fetchResultControllerRequest.sortDescriptors == nil { + // if `sortDescriptors` is `nil`, an exception is raised and causes a runtime crash + fetchResultControllerRequest.sortDescriptors = [] + } request = fetchRequest frc = NSFetchedResultsController( fetchRequest: fetchResultControllerRequest, diff --git a/Sources/CoreDataRepository/Internal/CountSubscription.swift b/Sources/CoreDataRepository/Internal/CountSubscription.swift index 29e6ad3..ab7f680 100644 --- a/Sources/CoreDataRepository/Internal/CountSubscription.swift +++ b/Sources/CoreDataRepository/Internal/CountSubscription.swift @@ -35,23 +35,11 @@ final class CountSubscription: Subscription>.Continuation - ) { - let request: NSFetchRequest - do { - request = try NSFetchRequest.countRequest( - predicate: predicate, - entityDesc: entityDesc - ) - } catch { - self.init( - fetchRequest: NSFetchRequest(), - fetchResultControllerRequest: NSFetchRequest(), - context: context, - continuation: continuation - ) - fail(error) - return - } + ) throws(CoreDataError) { + let request: NSFetchRequest = try NSFetchRequest.countRequest( + predicate: predicate, + entityDesc: entityDesc + ) self.init(request: request, context: context, continuation: continuation) } @@ -62,20 +50,11 @@ final class CountSubscription: Subscription, entityDesc: NSEntityDescription, continuation: AsyncStream>.Continuation - ) { - let request: NSFetchRequest - do { - request = try NSFetchRequest.countRequest(predicate: predicate, entityDesc: entityDesc) - } catch { - self.init( - fetchRequest: NSFetchRequest(), - fetchResultControllerRequest: NSFetchRequest(), - context: context, - continuation: continuation - ) - fail(error) - return - } + ) throws(CoreDataError) { + let request: NSFetchRequest = try NSFetchRequest.countRequest( + predicate: predicate, + entityDesc: entityDesc + ) self.init( fetchRequest: request, fetchResultControllerRequest: changeTrackingRequest, diff --git a/Sources/CoreDataRepository/Internal/CountThrowingSubscription.swift b/Sources/CoreDataRepository/Internal/CountThrowingSubscription.swift index aeb1ff5..74e264f 100644 --- a/Sources/CoreDataRepository/Internal/CountThrowingSubscription.swift +++ b/Sources/CoreDataRepository/Internal/CountThrowingSubscription.swift @@ -39,23 +39,11 @@ final class CountThrowingSubscription: ThrowingSubscr predicate: NSPredicate, entityDesc: NSEntityDescription, continuation: AsyncThrowingStream.Continuation - ) { - let request: NSFetchRequest - do { - request = try NSFetchRequest.countRequest( - predicate: predicate, - entityDesc: entityDesc - ) - } catch { - self.init( - fetchRequest: NSFetchRequest(), - fetchResultControllerRequest: NSFetchRequest(), - context: context, - continuation: continuation - ) - fail(error) - return - } + ) throws(CoreDataError) { + let request: NSFetchRequest = try NSFetchRequest.countRequest( + predicate: predicate, + entityDesc: entityDesc + ) self.init(request: request, context: context, continuation: continuation) } @@ -66,23 +54,11 @@ final class CountThrowingSubscription: ThrowingSubscr changeTrackingRequest: NSFetchRequest, entityDesc: NSEntityDescription, continuation: AsyncThrowingStream.Continuation - ) { - let request: NSFetchRequest - do { - request = try NSFetchRequest.countRequest( - predicate: predicate, - entityDesc: entityDesc - ) - } catch { - self.init( - fetchRequest: NSFetchRequest(), - fetchResultControllerRequest: NSFetchRequest(), - context: context, - continuation: continuation - ) - fail(error) - return - } + ) throws(CoreDataError) { + let request: NSFetchRequest = try NSFetchRequest.countRequest( + predicate: predicate, + entityDesc: entityDesc + ) self.init( fetchRequest: request, fetchResultControllerRequest: changeTrackingRequest, diff --git a/Tests/CoreDataRepositoryTests/AggregateTests.swift b/Tests/CoreDataRepositoryTests/AggregateTests.swift index 89d0371..7098faf 100644 --- a/Tests/CoreDataRepositoryTests/AggregateTests.swift +++ b/Tests/CoreDataRepositoryTests/AggregateTests.swift @@ -11,7 +11,6 @@ import Internal import Testing extension CoreDataRepositoryTests { - @Suite struct AggregateTests: CoreDataTestSuite, Sendable { let container: NSPersistentContainer let repositoryContext: NSManagedObjectContext @@ -313,6 +312,37 @@ extension CoreDataRepositoryTests { expectNoDifference(finalCount, 2) } + @Test + func countSubscriptionInvalidEntityDesc() async throws { + await #expect(throws: CoreDataError.atLeastOneAttributeDescRequired) { + let stream = repository + .countSubscription( + predicate: NSPredicate(value: true), + entityDesc: NSEntityDescription(), + as: Int.self + ) + for await result in stream { + _ = try result.get() + return + } + } + } + + @Test + func countThrowingSubscriptionInvalidEntityDesc() async throws { + await #expect(throws: CoreDataError.atLeastOneAttributeDescRequired) { + let stream = repository + .countThrowingSubscription( + predicate: NSPredicate(value: true), + entityDesc: NSEntityDescription(), + as: Int.self + ) + for try await _ in stream { + Issue.record("No values should be emitted") + } + } + } + @Test(arguments: [false, true]) func sumSuccess(inTransaction: Bool) async throws { let result = if inTransaction { @@ -617,6 +647,72 @@ extension CoreDataRepositoryTests { expectNoDifference(finalCount, 2) } + @Test + func sumSubscriptionInvalidEntityDesc() async throws { + await #expect(throws: CoreDataError.noEntityNameFound) { + let stream = repository + .sumSubscription( + predicate: NSPredicate(value: true), + entityDesc: NSEntityDescription(), + attributeDesc: NSAttributeDescription(), + as: Int.self + ) + for await result in stream { + _ = try result.get() + return + } + } + } + + @Test + func sumSubscriptionInvalidAttributeDesc() async throws { + await #expect(throws: CoreDataError.propertyDoesNotMatchEntity(description: "ManagedModel_UuidId")) { + let stream = repository + .sumSubscription( + predicate: NSPredicate(value: true), + entityDesc: ManagedModel_UuidId.entity(), + attributeDesc: NSAttributeDescription(), + as: Int.self + ) + for await result in stream { + _ = try result.get() + return + } + } + } + + @Test + func sumThrowingSubscriptionInvalidEntityDesc() async throws { + await #expect(throws: CoreDataError.noEntityNameFound) { + let stream = repository + .sumThrowingSubscription( + predicate: NSPredicate(value: true), + entityDesc: NSEntityDescription(), + attributeDesc: NSAttributeDescription(), + as: Int.self + ) + for try await _ in stream { + Issue.record("No values should be emitted") + } + } + } + + @Test + func sumThrowingSubscriptionInvalidAttributeDesc() async throws { + await #expect(throws: CoreDataError.propertyDoesNotMatchEntity(description: "ManagedModel_UuidId")) { + let stream = repository + .sumThrowingSubscription( + predicate: NSPredicate(value: true), + entityDesc: ManagedModel_UuidId.entity(), + attributeDesc: NSAttributeDescription(), + as: Int.self + ) + for try await _ in stream { + Issue.record("No values should be emitted") + } + } + } + @Test(arguments: [false, true]) func averageSuccess(inTransaction: Bool) async throws { let result = if inTransaction { @@ -929,6 +1025,72 @@ extension CoreDataRepositoryTests { expectNoDifference(finalCount, 2) } + @Test + func averageSubscriptionInvalidEntityDesc() async throws { + await #expect(throws: CoreDataError.noEntityNameFound) { + let stream = repository + .averageSubscription( + predicate: NSPredicate(value: true), + entityDesc: NSEntityDescription(), + attributeDesc: NSAttributeDescription(), + as: Int.self + ) + for await result in stream { + _ = try result.get() + return + } + } + } + + @Test + func averageSubscriptionInvalidAttributeDesc() async throws { + await #expect(throws: CoreDataError.propertyDoesNotMatchEntity(description: "ManagedModel_UuidId")) { + let stream = repository + .averageSubscription( + predicate: NSPredicate(value: true), + entityDesc: ManagedModel_UuidId.entity(), + attributeDesc: NSAttributeDescription(), + as: Int.self + ) + for await result in stream { + _ = try result.get() + return + } + } + } + + @Test + func averageThrowingSubscriptionInvalidEntityDesc() async throws { + await #expect(throws: CoreDataError.noEntityNameFound) { + let stream = repository + .averageThrowingSubscription( + predicate: NSPredicate(value: true), + entityDesc: NSEntityDescription(), + attributeDesc: NSAttributeDescription(), + as: Int.self + ) + for try await _ in stream { + Issue.record("No values should be emitted") + } + } + } + + @Test + func averageThrowingSubscriptionInvalidAttributeDesc() async throws { + await #expect(throws: CoreDataError.propertyDoesNotMatchEntity(description: "ManagedModel_UuidId")) { + let stream = repository + .averageThrowingSubscription( + predicate: NSPredicate(value: true), + entityDesc: ManagedModel_UuidId.entity(), + attributeDesc: NSAttributeDescription(), + as: Int.self + ) + for try await _ in stream { + Issue.record("No values should be emitted") + } + } + } + @Test(arguments: [false, true]) func minSuccess(inTransaction: Bool) async throws { let result = if inTransaction { @@ -1241,6 +1403,72 @@ extension CoreDataRepositoryTests { expectNoDifference(finalCount, 2) } + @Test + func minSubscriptionInvalidEntityDesc() async throws { + await #expect(throws: CoreDataError.noEntityNameFound) { + let stream = repository + .minSubscription( + predicate: NSPredicate(value: true), + entityDesc: NSEntityDescription(), + attributeDesc: NSAttributeDescription(), + as: Int.self + ) + for await result in stream { + _ = try result.get() + return + } + } + } + + @Test + func minSubscriptionInvalidAttributeDesc() async throws { + await #expect(throws: CoreDataError.propertyDoesNotMatchEntity(description: "ManagedModel_UuidId")) { + let stream = repository + .minSubscription( + predicate: NSPredicate(value: true), + entityDesc: ManagedModel_UuidId.entity(), + attributeDesc: NSAttributeDescription(), + as: Int.self + ) + for await result in stream { + _ = try result.get() + return + } + } + } + + @Test + func minThrowingSubscriptionInvalidEntityDesc() async throws { + await #expect(throws: CoreDataError.noEntityNameFound) { + let stream = repository + .minThrowingSubscription( + predicate: NSPredicate(value: true), + entityDesc: NSEntityDescription(), + attributeDesc: NSAttributeDescription(), + as: Int.self + ) + for try await _ in stream { + Issue.record("No values should be emitted") + } + } + } + + @Test + func minThrowingSubscriptionInvalidAttributeDesc() async throws { + await #expect(throws: CoreDataError.propertyDoesNotMatchEntity(description: "ManagedModel_UuidId")) { + let stream = repository + .minThrowingSubscription( + predicate: NSPredicate(value: true), + entityDesc: ManagedModel_UuidId.entity(), + attributeDesc: NSAttributeDescription(), + as: Int.self + ) + for try await _ in stream { + Issue.record("No values should be emitted") + } + } + } + @Test(arguments: [false, true]) func maxSuccess(inTransaction: Bool) async throws { let result = if inTransaction { @@ -1553,6 +1781,72 @@ extension CoreDataRepositoryTests { expectNoDifference(finalCount, 2) } + @Test + func maxSubscriptionInvalidEntityDesc() async throws { + await #expect(throws: CoreDataError.noEntityNameFound) { + let stream = repository + .maxSubscription( + predicate: NSPredicate(value: true), + entityDesc: NSEntityDescription(), + attributeDesc: NSAttributeDescription(), + as: Int.self + ) + for await result in stream { + _ = try result.get() + return + } + } + } + + @Test + func maxSubscriptionInvalidAttributeDesc() async throws { + await #expect(throws: CoreDataError.propertyDoesNotMatchEntity(description: "ManagedModel_UuidId")) { + let stream = repository + .maxSubscription( + predicate: NSPredicate(value: true), + entityDesc: ManagedModel_UuidId.entity(), + attributeDesc: NSAttributeDescription(), + as: Int.self + ) + for await result in stream { + _ = try result.get() + return + } + } + } + + @Test + func maxThrowingSubscriptionInvalidEntityDesc() async throws { + await #expect(throws: CoreDataError.noEntityNameFound) { + let stream = repository + .maxThrowingSubscription( + predicate: NSPredicate(value: true), + entityDesc: NSEntityDescription(), + attributeDesc: NSAttributeDescription(), + as: Int.self + ) + for try await _ in stream { + Issue.record("No values should be emitted") + } + } + } + + @Test + func maxThrowingSubscriptionInvalidAttributeDesc() async throws { + await #expect(throws: CoreDataError.propertyDoesNotMatchEntity(description: "ManagedModel_UuidId")) { + let stream = repository + .maxThrowingSubscription( + predicate: NSPredicate(value: true), + entityDesc: ManagedModel_UuidId.entity(), + attributeDesc: NSAttributeDescription(), + as: Int.self + ) + for try await _ in stream { + Issue.record("No values should be emitted") + } + } + } + @Test(arguments: [false, true]) func countWithPredicate(inTransaction: Bool) async throws { let result = if inTransaction { diff --git a/Tests/CoreDataRepositoryTests/BatchRequestTests.swift b/Tests/CoreDataRepositoryTests/BatchRequestTests.swift index 7da17ea..54545b2 100644 --- a/Tests/CoreDataRepositoryTests/BatchRequestTests.swift +++ b/Tests/CoreDataRepositoryTests/BatchRequestTests.swift @@ -11,7 +11,6 @@ import Internal import Testing extension CoreDataRepositoryTests { - @Suite struct BatchRequestTests: CoreDataTestSuite, Sendable { let container: NSPersistentContainer let repositoryContext: NSManagedObjectContext diff --git a/Tests/CoreDataRepositoryTests/CoreDataTestSuiteTests.swift b/Tests/CoreDataRepositoryTests/CoreDataTestSuiteTests.swift index 1bd95ea..c5392ac 100644 --- a/Tests/CoreDataRepositoryTests/CoreDataTestSuiteTests.swift +++ b/Tests/CoreDataRepositoryTests/CoreDataTestSuiteTests.swift @@ -10,7 +10,6 @@ import Internal import Testing extension CoreDataRepositoryTests { - @Suite struct CoreDataTestSuiteTests: CoreDataTestSuite { let container: NSPersistentContainer let repositoryContext: NSManagedObjectContext diff --git a/Tests/CoreDataRepositoryTests/CreateTests.swift b/Tests/CoreDataRepositoryTests/CreateTests.swift index 26561b2..65295f9 100644 --- a/Tests/CoreDataRepositoryTests/CreateTests.swift +++ b/Tests/CoreDataRepositoryTests/CreateTests.swift @@ -11,7 +11,6 @@ import Internal import Testing extension CoreDataRepositoryTests { - @Suite struct CreateTests: CoreDataTestSuite { let container: NSPersistentContainer let repositoryContext: NSManagedObjectContext diff --git a/Tests/CoreDataRepositoryTests/Create_BatchTests.swift b/Tests/CoreDataRepositoryTests/Create_BatchTests.swift index cbf1b13..506ba55 100644 --- a/Tests/CoreDataRepositoryTests/Create_BatchTests.swift +++ b/Tests/CoreDataRepositoryTests/Create_BatchTests.swift @@ -11,7 +11,6 @@ import Internal import Testing extension CoreDataRepositoryTests { - @Suite struct Create_BatchTests: CoreDataTestSuite { let container: NSPersistentContainer let repositoryContext: NSManagedObjectContext diff --git a/Tests/CoreDataRepositoryTests/CustomTests.swift b/Tests/CoreDataRepositoryTests/CustomTests.swift index 622f0e1..68984a3 100644 --- a/Tests/CoreDataRepositoryTests/CustomTests.swift +++ b/Tests/CoreDataRepositoryTests/CustomTests.swift @@ -11,7 +11,6 @@ import Internal import Testing extension CoreDataRepositoryTests { - @Suite struct CustomTests: CoreDataTestSuite { let container: NSPersistentContainer let repositoryContext: NSManagedObjectContext diff --git a/Tests/CoreDataRepositoryTests/DeleteTests.swift b/Tests/CoreDataRepositoryTests/DeleteTests.swift index 7914afc..fdb5d5a 100644 --- a/Tests/CoreDataRepositoryTests/DeleteTests.swift +++ b/Tests/CoreDataRepositoryTests/DeleteTests.swift @@ -11,7 +11,6 @@ import Internal import Testing extension CoreDataRepositoryTests { - @Suite struct DeleteTests: CoreDataTestSuite { let container: NSPersistentContainer let repositoryContext: NSManagedObjectContext diff --git a/Tests/CoreDataRepositoryTests/Delete_BatchTests.swift b/Tests/CoreDataRepositoryTests/Delete_BatchTests.swift index 191f796..c23c309 100644 --- a/Tests/CoreDataRepositoryTests/Delete_BatchTests.swift +++ b/Tests/CoreDataRepositoryTests/Delete_BatchTests.swift @@ -11,7 +11,6 @@ import Internal import Testing extension CoreDataRepositoryTests { - @Suite struct Delete_BatchTests: CoreDataTestSuite { let container: NSPersistentContainer let repositoryContext: NSManagedObjectContext diff --git a/Tests/CoreDataRepositoryTests/FetchTests.swift b/Tests/CoreDataRepositoryTests/FetchTests.swift index ebd3279..0e51abd 100644 --- a/Tests/CoreDataRepositoryTests/FetchTests.swift +++ b/Tests/CoreDataRepositoryTests/FetchTests.swift @@ -11,7 +11,6 @@ import Internal import Testing extension CoreDataRepositoryTests { - @Suite struct FetchRepositoryTests: CoreDataTestSuite, @unchecked Sendable { let container: NSPersistentContainer let repositoryContext: NSManagedObjectContext diff --git a/Tests/CoreDataRepositoryTests/ReadTests.swift b/Tests/CoreDataRepositoryTests/ReadTests.swift index 6c632fb..cb6d769 100644 --- a/Tests/CoreDataRepositoryTests/ReadTests.swift +++ b/Tests/CoreDataRepositoryTests/ReadTests.swift @@ -11,7 +11,6 @@ import Internal import Testing extension CoreDataRepositoryTests { - @Suite struct ReadTests: CoreDataTestSuite { let container: NSPersistentContainer let repositoryContext: NSManagedObjectContext diff --git a/Tests/CoreDataRepositoryTests/Read_BatchTests.swift b/Tests/CoreDataRepositoryTests/Read_BatchTests.swift index 5186944..0b79f04 100644 --- a/Tests/CoreDataRepositoryTests/Read_BatchTests.swift +++ b/Tests/CoreDataRepositoryTests/Read_BatchTests.swift @@ -11,7 +11,6 @@ import Internal import Testing extension CoreDataRepositoryTests { - @Suite struct Read_BatchTests: CoreDataTestSuite { let container: NSPersistentContainer let repositoryContext: NSManagedObjectContext diff --git a/Tests/CoreDataRepositoryTests/TransactionTests.swift b/Tests/CoreDataRepositoryTests/TransactionTests.swift index 14f19e8..83b81f1 100644 --- a/Tests/CoreDataRepositoryTests/TransactionTests.swift +++ b/Tests/CoreDataRepositoryTests/TransactionTests.swift @@ -11,7 +11,6 @@ import Internal import Testing extension CoreDataRepositoryTests { - @Suite struct TransactionTests: CoreDataTestSuite { let container: NSPersistentContainer let repositoryContext: NSManagedObjectContext diff --git a/Tests/CoreDataRepositoryTests/UpdateTests.swift b/Tests/CoreDataRepositoryTests/UpdateTests.swift index 0a99796..5e41b2b 100644 --- a/Tests/CoreDataRepositoryTests/UpdateTests.swift +++ b/Tests/CoreDataRepositoryTests/UpdateTests.swift @@ -11,7 +11,6 @@ import Internal import Testing extension CoreDataRepositoryTests { - @Suite struct UpdateTests: CoreDataTestSuite { let container: NSPersistentContainer let repositoryContext: NSManagedObjectContext diff --git a/Tests/CoreDataRepositoryTests/Update_BatchTests.swift b/Tests/CoreDataRepositoryTests/Update_BatchTests.swift index 62ad03e..5b582d6 100644 --- a/Tests/CoreDataRepositoryTests/Update_BatchTests.swift +++ b/Tests/CoreDataRepositoryTests/Update_BatchTests.swift @@ -11,7 +11,6 @@ import Internal import Testing extension CoreDataRepositoryTests { - @Suite struct Update_BatchTests: CoreDataTestSuite { let container: NSPersistentContainer let repositoryContext: NSManagedObjectContext