Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save josephlord/0d6a9d0871bd2e1b3a3bdbf20c184f88 to your computer and use it in GitHub Desktop.
Save josephlord/0d6a9d0871bd2e1b3a3bdbf20c184f88 to your computer and use it in GitHub Desktop.
Making a Combine publisher from a FetchedResultsController
//
// FetchedResultsControllerPublisher.swift
// ListsModel
//
// Created by Joseph Lord on 09/06/2019.
// Copyright © 2019 Joseph Lord. All rights reserved.
//
import Foundation
import Combine
import CoreData
/// Create by passing in a FetchedResultsController
/// This will perform the fetch request on the correct queue and publish the resutls on the
/// publishers.
final public class FetchedResultsControllerPublisher<FetchType>
where FetchType : NSFetchRequestResult {
private let internalFRCP: FetchedResultsControllerPublisherInternal<FetchType>
/// Pass in a configured fetchResults controller and this class will provide a choice of publishers
/// for you depending on how you like your errors
public init(fetchedResultsController: NSFetchedResultsController<FetchType>,
performFetchNotRequired: Bool = false) {
self.internalFRCP = FetchedResultsControllerPublisherInternal(
fetchedResultsController: fetchedResultsController,
performFetch: !performFetchNotRequired)
}
public lazy var publisherWithErrors: AnyPublisher<[FetchType], Error> = {
return self.internalFRCP.publisher.eraseToAnyPublisher()
}()
public lazy var publisher: AnyPublisher<[FetchType], Never> = {
return self.internalFRCP.publisher.replaceError(with: []).eraseToAnyPublisher()
}()
}
final private class FetchedResultsControllerPublisherInternal<FetchType>
: NSObject, NSFetchedResultsControllerDelegate
where FetchType : NSFetchRequestResult {
let publisher: PassthroughSubject<[FetchType], Error>
let fetchedResultsController: NSFetchedResultsController<FetchType>
init(fetchedResultsController: NSFetchedResultsController<FetchType>,
performFetch: Bool) {
self.fetchedResultsController = fetchedResultsController
publisher = PassthroughSubject<[FetchType], Error>()
super.init()
fetchedResultsController.delegate = self
fetchedResultsController.managedObjectContext.perform {
do {
if performFetch {
try fetchedResultsController.performFetch()
}
self.publisher.send(fetchedResultsController.fetchedObjects ?? [])
} catch {
self.publisher.send(completion: .failure(error))
}
}
}
@objc func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
publisher.send(fetchedResultsController.fetchedObjects ?? [])
}
}
@michzio
Copy link

michzio commented Feb 24, 2020

Yes @FetchRequest works inside View where is environment managedObjectContex. But If I want to get obsarvable request inside ViewModel (ex. ObservableObject) such publisher is very nice and it also works with old UIKit

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment