Javier Cicchelli e6e82b3b40 [Improvement] Naming of libraries and package (#22)
This PR contains the work done to fix the naming of the libraries and the package, along with tweaks to the folder structure.

Reviewed-on: #22
Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
2023-08-13 21:53:47 +00:00

172 lines
5.7 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftLibs open source project
//
// Copyright (c) 2023 Röck+Cöde VoF. and the SwiftLibs project authors
// Licensed under the EUPL 1.2 or later.
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftLibs project authors
//
//===----------------------------------------------------------------------===//
#if canImport(Combine) && canImport(CoreData)
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 changesToNotify: [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 numberOfObjects(in section: Int) throws -> Int {
guard let sections = fetchedResultsController.sections else {
throw FetcherError.fetchNotExecuted
}
guard sections.endIndex > section else {
throw FetcherError.sectionNotFound
}
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) throws -> Model {
guard fetchedResultsController.sections != nil else {
throw FetcherError.fetchNotExecuted
}
return fetchedResultsController.object(at: indexPath)
}
// MARK: NSFetchedResultsControllerDelegate
public func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
changesToNotify.removeAll()
}
public func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
didChangePublisher.send(changesToNotify)
}
public func controller(
_ controller: NSFetchedResultsController<NSFetchRequestResult>,
didChange sectionInfo: NSFetchedResultsSectionInfo,
atSectionIndex sectionIndex: Int,
for type: NSFetchedResultsChangeType
) {
if type == .insert {
changesToNotify.append(.section(.inserted(sectionIndex)))
} else if type == .delete {
changesToNotify.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 }
changesToNotify.append(.object(.inserted(at: newIndexPath)))
case .delete:
guard let indexPath else { return }
changesToNotify.append(.object(.deleted(from: indexPath)))
case .move:
guard let indexPath, let newIndexPath else { return }
changesToNotify.append(.object(.moved(from: indexPath, to: newIndexPath)))
case .update:
guard let indexPath else { return }
changesToNotify.append(.object(.updated(at: indexPath)))
default:
break
}
}
}
// MARK: - Errors
public enum FetcherError: Error {
case fetchNotExecuted
case sectionNotFound
}
// 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)
}
#endif