Created
June 10, 2019 23:08
-
-
Save josephlord/0d6a9d0871bd2e1b3a3bdbf20c184f88 to your computer and use it in GitHub Desktop.
Making a Combine publisher from a FetchedResultsController
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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 ?? []) | |
} | |
} |
Yes. This is probably completely redundant now
It's still nice to process results from an FRC using Combine, especially when not driving views.
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
A @FetchRequest property wrapper is now available in iOS 13 beta 5.