Ignore unmappable Core Data entities (#660)

But implement .discard for testing.
This commit is contained in:
Davide 2024-10-02 13:35:49 +02:00 committed by GitHub
parent f602655568
commit 3fbf803518
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 25 additions and 10 deletions

View File

@ -35,7 +35,7 @@ extension AppData {
registry: Registry, registry: Registry,
coder: ProfileCoder, coder: ProfileCoder,
context: NSManagedObjectContext, context: NSManagedObjectContext,
onResultError: ((Error) -> Bool)? onResultError: ((Error) -> CoreDataResultAction)?
) -> any ProfileRepository { ) -> any ProfileRepository {
let repository = CoreDataRepository<CDProfile, Profile>(context: context) { let repository = CoreDataRepository<CDProfile, Profile>(context: context) {
$0.sortDescriptors = [ $0.sortDescriptors = [
@ -57,7 +57,7 @@ extension AppData {
cdProfile.lastUpdate = Date() cdProfile.lastUpdate = Date()
return cdProfile return cdProfile
} onResultError: { } onResultError: {
onResultError?($0) ?? true onResultError?($0) ?? .ignore
} }
return repository return repository

View File

@ -31,10 +31,19 @@ public protocol CoreDataUniqueEntity: NSManagedObject, UniqueEntity {
// Core Data entity must have a unique "uuid" field // Core Data entity must have a unique "uuid" field
} }
public enum CoreDataResultAction {
case ignore
case discard
case halt
}
public actor CoreDataRepository<CD, T>: NSObject, public actor CoreDataRepository<CD, T>: NSObject,
Repository, Repository,
NSFetchedResultsControllerDelegate where CD: CoreDataUniqueEntity, NSFetchedResultsControllerDelegate where CD: CoreDataUniqueEntity,
T: UniqueEntity { T: UniqueEntity {
private let entityName: String private let entityName: String
private let context: NSManagedObjectContext private let context: NSManagedObjectContext
@ -43,7 +52,7 @@ public actor CoreDataRepository<CD, T>: NSObject,
private let toMapper: (T, NSManagedObjectContext) throws -> CD private let toMapper: (T, NSManagedObjectContext) throws -> CD
private let onResultError: ((Error) -> Bool)? // true = keep going private let onResultError: ((Error) -> CoreDataResultAction)?
private let entitiesSubject: CurrentValueSubject<EntitiesResult<T>, Never> private let entitiesSubject: CurrentValueSubject<EntitiesResult<T>, Never>
@ -55,7 +64,7 @@ public actor CoreDataRepository<CD, T>: NSObject,
beforeFetch: ((NSFetchRequest<CD>) -> Void)? = nil, beforeFetch: ((NSFetchRequest<CD>) -> Void)? = nil,
fromMapper: @escaping (CD) throws -> T?, fromMapper: @escaping (CD) throws -> T?,
toMapper: @escaping (T, NSManagedObjectContext) throws -> CD, toMapper: @escaping (T, NSManagedObjectContext) throws -> CD,
onResultError: ((Error) -> Bool)? = nil onResultError: ((Error) -> CoreDataResultAction)? = nil
) { ) {
guard let entityName = CD.entity().name else { guard let entityName = CD.entity().name else {
fatalError("Unable to find entity name for \(CD.self)") fatalError("Unable to find entity name for \(CD.self)")
@ -193,16 +202,22 @@ private extension CoreDataRepository {
do { do {
return try self?.fromMapper($0) return try self?.fromMapper($0)
} catch { } catch {
if let onResultError = self?.onResultError { switch self?.onResultError?(error) {
case .discard:
self?.context.delete($0)
// on false, discard results case .halt:
guard onResultError(error) else {
throw ResultError.mapping(error) throw ResultError.mapping(error)
}
default:
break
} }
return nil return nil
} }
} }
try self?.context.save()
let result = EntitiesResult(entities, isFiltering: controller.fetchRequest.predicate != nil) let result = EntitiesResult(entities, isFiltering: controller.fetchRequest.predicate != nil)
self?.entitiesSubject.send(result) self?.entitiesSubject.send(result)
} catch { } catch {

View File

@ -50,7 +50,7 @@ extension ProfileManager {
context: store.context context: store.context
) { error in ) { error in
pp_log(.app, .error, "Unable to decode result: \(error)") pp_log(.app, .error, "Unable to decode result: \(error)")
return true return .ignore
} }
return ProfileManager(repository: repository) return ProfileManager(repository: repository)