'iOS Share Extension + NSFetchedResultsController not returning results - controllerDidChangeContent not called
I am sharing my CoreData model between my iOS main app target and a new Share Extension target like this post
This is working well for the most part except for one thing. NSFetchedResultsController is not returning results when called from the Shared Extension. What is strange though is that if I do a plain NSFetchRequest in my Share Extension, I do get CoreData results returned that were originally saved from the main app. I think Container setup as well as model must be being shared correctly via AppContainer.
NSFetchedResultsControllerDelegate controllerDidChangeContent is never called
ShareViewController:
import UIKit
import MobileCoreServices
class ShareViewController: UIViewController {
private(set) lazy var resultsController: NSFetchedResultsController<Person> = createFetchedResultsController()
override func viewDidLoad() {
super.viewDidLoad()
let fetchRequest = NSFetchRequest<Person>(entityName: "Person")
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "date", ascending: false)]
do {
/// this works!
let persons = try CoreDataManager.shared.managedObjectContext.fetch(fetchRequest)
print("Got \(persons.count) Persons")
} catch {
print("Fetch failed")
}
activateResultsController()
}
func createFetchedResultsController() -> NSFetchedResultsController<Person> {
CoreDataManager.shared.container.viewContext.stalenessInterval = 0
CoreDataManager.shared.container.viewContext.refreshAllObjects()
CoreDataManager.shared.container.viewContext.stalenessInterval = -1
let fetchRequest = NSFetchRequest<Person>(entityName: "Person")
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "date", ascending: false)]
////managedObjectContext: CoreDataManager.shared.managedObjectContext,
let controller = NSFetchedResultsController(
fetchRequest: fetchRequest,
managedObjectContext: CoreDataManager.shared.managedObjectContext,
sectionNameKeyPath: nil,
cacheName: nil
)
controller.delegate = self
return controller
}
private func activateResultsController() {
do {
try resultsController.performFetch()
} catch {
fatalError("Failed to fetch entities: \(error)")
}
}
}
// MARK: - Results Controller Delegate
extension ShareViewController: NSFetchedResultsControllerDelegate {
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
guard let sections = resultsController.sections else {
return
}
let section = sections[0]
let rows = section.numberOfObjects
print("rows=\(rows)")
}
}
CoreDataManager:
import UIKit
import CoreData
class CoreDataManager {
static let shared = CoreDataManager()
internal var container: NSPersistentContainer
var managedObjectContext: NSManagedObjectContext {
container.viewContext
}
init() {
container = NSPersistentContainer(name: Constants.name)
guard let storeDirectory = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first else {
// We'll throw a fatalError() because we can't really proceed without storeDirectory
fatalError(file: "Could not find .applicationSupportDirectory - exiting")
}
let storeURL = storeURL(for: "group.mygroup.testshareextensioncoredata", databaseName: "\(Constants.name)")
let storeDescription = NSPersistentStoreDescription(url: storeURL)
container.persistentStoreDescriptions = [storeDescription]
container.loadPersistentStores(completionHandler: { storeDescription, error in
if let error = error as NSError? {
// We'll throw a fatalError() because we can't really proceed without loading the PersistentStore
fatalError("loadPersistentStore failed \(error), \(error.userInfo)")
}
})
}
// MARK: - Core Data Saving support
func saveContext() {
managedObjectContext.performAndWait {
if managedObjectContext.hasChanges {
do {
try managedObjectContext.save()
} catch {
}
}
}
}
/// Returns a URL for the given app group and database pointing to the sqlite database.
func storeURL(for appGroup: String, databaseName: String) -> URL {
guard let fileContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup) else {
fatalError("Shared file container could not be created.")
}
return fileContainer.appendingPathComponent("\(databaseName).sqlite")
}
}
internal extension CoreDataManager {
enum Constants {
static let name = "ShareExtensionCoreDataTest"
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
