Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • 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 ?? [])
}
}
@kielgillard
Copy link

kielgillard commented Jul 30, 2019

@josephlord
Copy link
Author

Yes. This is probably completely redundant now

@jjatie
Copy link

jjatie commented Aug 26, 2019

It's still nice to process results from an FRC using Combine, especially when not driving views.

@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