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...so I think Container setup as well as model must be being shared correctly via AppContainer.
NSFetchedResultsControllerDelegate controllerDidChangeContent is never called.
Any ideas or suggestions?
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)")
}
}
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"
}
}