Skip to content

Instantly share code, notes, and snippets.

@darrarski
Last active November 9, 2023 08:31
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save darrarski/28d2f5a28ef2c5669d199069c30d3d52 to your computer and use it in GitHub Desktop.
Save darrarski/28d2f5a28ef2c5669d199069c30d3d52 to your computer and use it in GitHub Desktop.
Swift-Combine-CoreData-Fetched-Results-Publisher
import Combine
import CoreData
public final class FetchedResultsPublisher
<ResultType>
: Publisher
where
ResultType: NSFetchRequestResult
{
public init(request: NSFetchRequest<ResultType>,
context: NSManagedObjectContext) {
self.request = request
self.context = context
}
let request: NSFetchRequest<ResultType>
let context: NSManagedObjectContext
// MARK: - Publisher
public typealias Output = [ResultType]
public typealias Failure = NSError
public func receive<S>(subscriber: S) where S: Subscriber, S.Failure == Failure, S.Input == Output {
subscriber.receive(subscription: FetchedResultsSubscription(
subscriber: subscriber,
request: request,
context: context
))
}
}
final class FetchedResultsSubscription
<SubscriberType, ResultType>
: NSObject, Subscription, NSFetchedResultsControllerDelegate
where
SubscriberType: Subscriber,
SubscriberType.Input == [ResultType],
SubscriberType.Failure == NSError,
ResultType: NSFetchRequestResult
{
init(subscriber: SubscriberType,
request: NSFetchRequest<ResultType>,
context: NSManagedObjectContext) {
self.subscriber = subscriber
self.request = request
self.context = context
}
private(set) var subscriber: SubscriberType?
private(set) var request: NSFetchRequest<ResultType>?
private(set) var context: NSManagedObjectContext?
private(set) var controller: NSFetchedResultsController<ResultType>?
// MARK: - Subscription
func request(_ demand: Subscribers.Demand) {
guard demand > 0,
let subscriber = subscriber,
let request = request,
let context = context else { return }
controller = NSFetchedResultsController(
fetchRequest: request,
managedObjectContext: context,
sectionNameKeyPath: nil,
cacheName: nil
)
controller?.delegate = self
do {
try controller?.performFetch()
if let fetchedObjects = controller?.fetchedObjects {
_ = subscriber.receive(fetchedObjects)
}
} catch {
subscriber.receive(completion: .failure(error as NSError))
}
}
// MARK: - Cancellable
func cancel() {
subscriber = nil
controller = nil
request = nil
context = nil
}
// MARK: - NSFetchedResultsControllerDelegate
func controllerDidChangeContent(
_ controller: NSFetchedResultsController<NSFetchRequestResult>
) {
guard let subscriber = subscriber,
controller == self.controller else { return }
if let fetchedObjects = self.controller?.fetchedObjects {
_ = subscriber.receive(fetchedObjects)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment