Implemented the Fetcher class.
This commit is contained in:
parent
825354ef6d
commit
879b6d6bbd
154
Sources/Persistence/Classes/Fetcher.swift
Normal file
154
Sources/Persistence/Classes/Fetcher.swift
Normal file
@ -0,0 +1,154 @@
|
||||
//
|
||||
// Fetcher.swift
|
||||
// Persistence
|
||||
//
|
||||
// Created by Javier Cicchelli on 16/04/2023.
|
||||
// Copyright © 2023 Röck+Cöde. All rights reserved.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import CoreData
|
||||
|
||||
/// This class fetches objects from a given managed object context and it notifies of changes in the object fetched if any.
|
||||
public class Fetcher<Model: NSManagedObject>: NSObject, NSFetchedResultsControllerDelegate {
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
/// The publisher that emits the changes detected to the Location entities in a given object context.
|
||||
public let didChangePublisher = PassthroughSubject<[Change], Never>()
|
||||
|
||||
private let fetchedResultsController: NSFetchedResultsController<Model>
|
||||
|
||||
/// The number of sections in the data.
|
||||
public var numberOfSections: Int {
|
||||
fetchedResultsController.sections?.count ?? 0
|
||||
}
|
||||
|
||||
private var inProgressChanges: [Change] = []
|
||||
|
||||
// MARK: Initialisers
|
||||
|
||||
/// Initialises the fetcher give the given parameters.
|
||||
/// - Parameters:
|
||||
/// - fetchRequest: The fetch request to use to get the objects.
|
||||
/// - managedObjectContext: The managed object context against the fetch request is executed.
|
||||
/// - sectionNameKeyPath: A key path on result objects that returns the section name.
|
||||
/// - cacheName: The name of the cache file the receiver should use.
|
||||
public init(
|
||||
fetchRequest: NSFetchRequest<Model>,
|
||||
managedObjectContext: NSManagedObjectContext,
|
||||
sectionNameKeyPath: String? = nil,
|
||||
cacheName: String? = nil
|
||||
) {
|
||||
self.fetchedResultsController = .init(
|
||||
fetchRequest: fetchRequest,
|
||||
managedObjectContext: managedObjectContext,
|
||||
sectionNameKeyPath: sectionNameKeyPath,
|
||||
cacheName: cacheName
|
||||
)
|
||||
|
||||
super.init()
|
||||
|
||||
self.fetchedResultsController.delegate = self
|
||||
}
|
||||
|
||||
// MARK: Functions
|
||||
|
||||
/// Perform the fetching.
|
||||
public func fetch() throws {
|
||||
try fetchedResultsController.performFetch()
|
||||
}
|
||||
|
||||
/// Retrieve the number of objects in a given section number.
|
||||
/// - Parameter section: The section number to inquiry about.
|
||||
/// - Returns: A number of objects in the given section number.
|
||||
public func numberOfObjectsInSection(_ section: Int) -> Int {
|
||||
guard
|
||||
let sections = fetchedResultsController.sections,
|
||||
sections.endIndex > section
|
||||
else {
|
||||
return 0
|
||||
}
|
||||
|
||||
return sections[section].numberOfObjects
|
||||
}
|
||||
|
||||
/// Retrieve an object out of a given index path.
|
||||
/// - Parameter indexPath: The index path to use to retrieve an object.
|
||||
/// - Returns: A `NSManagedObject` entity positioned in the given index path.
|
||||
public func object(at indexPath: IndexPath) -> Model {
|
||||
return fetchedResultsController.object(at: indexPath)
|
||||
}
|
||||
|
||||
// MARK: NSFetchedResultsControllerDelegate
|
||||
|
||||
public func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
|
||||
inProgressChanges.removeAll()
|
||||
}
|
||||
|
||||
public func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
|
||||
didChangePublisher.send(inProgressChanges)
|
||||
}
|
||||
|
||||
public func controller(
|
||||
_ controller: NSFetchedResultsController<NSFetchRequestResult>,
|
||||
didChange sectionInfo: NSFetchedResultsSectionInfo,
|
||||
atSectionIndex sectionIndex: Int,
|
||||
for type: NSFetchedResultsChangeType
|
||||
) {
|
||||
if type == .insert {
|
||||
inProgressChanges.append(.section(.inserted(sectionIndex)))
|
||||
} else if type == .delete {
|
||||
inProgressChanges.append(.section(.deleted(sectionIndex)))
|
||||
}
|
||||
}
|
||||
|
||||
public func controller(
|
||||
_ controller: NSFetchedResultsController<NSFetchRequestResult>,
|
||||
didChange anObject: Any,
|
||||
at indexPath: IndexPath?,
|
||||
for type: NSFetchedResultsChangeType,
|
||||
newIndexPath: IndexPath?
|
||||
) {
|
||||
switch type {
|
||||
case .insert:
|
||||
guard let newIndexPath else { return }
|
||||
|
||||
inProgressChanges.append(.object(.inserted(at: newIndexPath)))
|
||||
case .delete:
|
||||
guard let indexPath else { return }
|
||||
|
||||
inProgressChanges.append(.object(.deleted(from: indexPath)))
|
||||
case .move:
|
||||
guard let indexPath, let newIndexPath else { return }
|
||||
|
||||
inProgressChanges.append(.object(.moved(from: indexPath, to: newIndexPath)))
|
||||
case .update:
|
||||
guard let indexPath else { return }
|
||||
|
||||
inProgressChanges.append(.object(.updated(at: indexPath)))
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Enumerations
|
||||
|
||||
public enum Change: Hashable {
|
||||
public enum SectionUpdate: Hashable {
|
||||
case inserted(Int)
|
||||
case deleted(Int)
|
||||
}
|
||||
|
||||
public enum ObjectUpdate: Hashable {
|
||||
case inserted(at: IndexPath)
|
||||
case deleted(from: IndexPath)
|
||||
case updated(at: IndexPath)
|
||||
case moved(from: IndexPath, to: IndexPath)
|
||||
}
|
||||
|
||||
case section(SectionUpdate)
|
||||
case object(ObjectUpdate)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user