[Feature] Persistence #5
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