Generic NSFetchedResultsController with generic NSFetchRequest results in an error - ios

When trying to set up a generic fetched results controller I get Cannot convert value of type 'NSFetchRequest<T>' to expected argument type 'NSFetchRequest<_>', Insert ' as! NSFetchRequest<_> when initializing the controller.
fileprivate var fetchedResultsController: NSFetchedResultsController<T>!
guard let request: NSFetchRequest<T> = T.fetchRequest() as? NSFetchRequest<T> else {
assertionFailure("Can't set up NSFetchRequest")
return
}
request.sortDescriptors = [NSSortDescriptor(key: key, ascending: ascending)]
fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: dataStore.viewContext, sectionNameKeyPath: nil, cacheName: nil)
do {
try fetchedResultsController.performFetch()
} catch let error {
DDLogError("Error fetching entities: \(error)")
}
Anyone knows what's going on here?

For FRC you should explicitly specify that it have a type T.
Also, there is not enough of context from your code snippet, so it's unclear how did you define this class. However, here is how I think it should look like:
import CoreData
class Model<T> where T: NSManagedObject {
fileprivate var context: NSManagedObjectContext! // assume that somewhere during initialization we setting context
fileprivate lazy var fetchedResultscontroller: NSFetchedResultsController<T> = { [weak self] in
guard let this = self else {
fatalError("lazy property has been called after object has been descructed")
}
guard let request = T.fetchRequest() as? NSFetchRequest<T> else {
fatalError("Can't set up NSFetchRequest")
}
request.sortDescriptors = [NSSortDescriptor(key: "key", ascending: true)]
let tmp = NSFetchedResultsController<T>(fetchRequest: request, managedObjectContext: this.context, sectionNameKeyPath: nil, cacheName: nil)
return tmp
}()
}

Related

Value of type '(Class) -> () -> (Class)' has no member 'variable'

let fetchedResultsController: NSFetchedResultsController = { () -> NSFetchedResultsController<NSFetchRequestResult> in
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Message")
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)]
fetchRequest.predicate = NSPredicate(format: "friend.name = %#", self.friend!.name!)
let delegate = UIApplication.shared.delegate as! AppDelegate
let context = delegate.persistentContainer.viewContext
let frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
return frc
}()
At the line that says: fetchRequest.predicate = NSPredicate(format: "friend.name = %#", self.friend!.name!) I get the error: Value of type '(ChatLogController) -> () -> (ChatLogController)' has no member 'friend' So apparently it can not find friend in my class ChatLogController for some reason, or am I wrong? That's what I'm guessing since it wont find friend as a member of self.
Here is the friend variable, it is inside of ChatLogController class:
var friend: Friend?
{
didSet
{
navigationItem.title = friend?.name
messages = friend?.messages?.allObjects as? [Message]
messages = messages?.sorted(by: {$0.date!.compare($1.date! as Date) == .orderedAscending})
}
}
Anyone know why I'm getting this error and how to solve it?
Edit: I noticed that if I remove () -> NSFetchedResultsController<NSFetchRequestResult> in from the first line of the first code snippet, the error disappears, however instead I get this error: Unable to infer complex closure return type; add explicit type to disambiguate
You have declared friend as the optional property which has not initialised at the time of initialisation of fetchedResultsController which is a constant so it's giving the error. To solve this problem you can use lazy initialisation which means
delaying the creation of an object or some other expensive process
until it’s needed
make the fetchedResultsController property lazy which will delay creation of object until its required
lazy var fetchedResultsController: NSFetchedResultsController = { [unowned self] () -> NSFetchedResultsController<NSFetchRequestResult> in
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Message")
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)]
fetchRequest.predicate = NSPredicate(format: "friend.name = %#", self.friend!.name!)
let delegate = UIApplication.shared.delegate as! AppDelegate
let context = delegate.persistentContainer.viewContext
let frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
return frc
}()

Unable to infer complex closure return type; add explicit type to disambiguate

Does anybody know how I can solve this error that I'm getting? The error is received on the first line in the following chunk of code:
let fetchedResultsController: NSFetchedResultsController = {
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Message")
let delegate = UIApplication.shared.delegate as! AppDelegate
let context = delegate.persistentContainer.viewContext
let frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
return frc
}()
Try adding the return type in the closure like this code:
let fetchedResultsController: NSFetchedResultsController = { () -> NSFetchedResultsController<NSFetchRequestResult> in
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Message")
let delegate = UIApplication.shared.delegate as! AppDelegate
let context = delegate.persistentContainer.viewContext
let frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
return frc
}()
The error message is a bit misleading, the problem is that you did
not specify the generic placeholder type for the variable.
You can either add an explicit return type to the closure, as #Mukesh
suggested, in that case the type annotation on the variable is not
needed:
let fetchedResultsController = { () -> NSFetchedResultsController<NSFetchRequestResult> in
// ...
return frc
}()
Or fully specify the type of the variable, then the closure return
type is inferred automatically:
let fetchedResultsController: NSFetchedResultsController<NSFetchRequestResult> = {
// ...
return frc
}()
Two issues:
As mentioned in the other answers you have to specify the generic type, in your case the ideal type is the concrete NSManagedObject subclass.
The declaration of an NSFetchedResultsController requires at least one sort descriptor.
let fetchedResultsController: NSFetchedResultsController<Message> = {
let fetchRequest = NSFetchRequest<Message>(entityName: "Message")
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "somekey", ascending: true]
let delegate = UIApplication.shared.delegate as! AppDelegate
let context = delegate.persistentContainer.viewContext
let frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
return frc
}()

Return as CustomManagedObjectClass NSFetchedResultsController<NSManagedObject> at base NSManagedObject class

This is my first Swift project also with CoreData. Swift doesn't have return instancetype like in Objective C.
In Swift - CoreData class, I am using common base class for NSManagedObject. There I have implemented a class function.
class func FRCWith(sortArray: [NSSortDescriptor], predicate: NSPredicate? = nil, mainContext: NSManagedObjectContext = CoreDataManager.shared.managedObjectContext) -> NSFetchedResultsController<NSManagedObject> {
let className = self.nameOfTheClass
let fetchRequest: NSFetchRequest<NSManagedObject> = NSFetchRequest.init(entityName: className)
fetchRequest.sortDescriptors = sortArray
fetchRequest.fetchBatchSize = 4
if let predicateValue = predicate {
fetchRequest.predicate = predicateValue
}
let fetchedResultsController: NSFetchedResultsController<NSManagedObject> = NSFetchedResultsController.init(fetchRequest: fetchRequest, managedObjectContext: mainContext, sectionNameKeyPath: nil, cacheName: nil)
do {
try fetchedResultsController.performFetch()
} catch {
print(error.localizedDescription)
}
return fetchedResultsController
}
I want to return NSFetchedResultsController<customCoreDataClass> instead of common NSFetchedResultsController<NSManagedObject>.
var schoolvalue: NSFetchedResultsController<SchoolDetails> = SchoolDetails.FRCWith([sort])
var schoolvalue: NSFetchedResultsController<StudentDetails> = StudentDetails.FRCWith([sort], preidcate)
Right now I am using in for loop like this and checking is that FRC as! customClass
let loopValue: SchoolDetails = someFRC.object(at: IndexPath.init(row: value, section: 0)) as! SchoolDetails
Found some kind of solution in StackOverflow. That T thing, I do not understand that.
Is that possible make a method like that?
Thanks, #Paulw11 for an answer.
Randomly app crash when init
let fetchedResultsController: NSFetchedResultsController<T> = NSFetchedResultsController<T>(fetchRequest: fetchRequest, managedObjectContext: mainContext, sectionNameKeyPath: nil, cacheName: nil)
from below answer.
Because
let fetchRequest:NSFetchRequest<T> = T.fetchRequest() as! NSFetchRequest<T>\\ line return nil. There is no crash below iOS 10.x.
It only occurs when downloading from App store or Testflight.
You can use Swift generics for this. You specify a placeholder, often T is used but it doesn't have to be, that "stands in" for the actual class that will be used. In this case you need to specify that T must be a NSManagedObject or a subclass of NSManagedObect
Declare your function as:
class func FRCWith<T: NSManagedObject>(sortArray: [NSSortDescriptor], predicate: NSPredicate? = nil, mainContext: NSManagedObjectContext) throws -> NSFetchedResultsController<T> {
let fetchRequest:NSFetchRequest<T> = T.fetchRequest() as! NSFetchRequest<T>
fetchRequest.sortDescriptors = sortArray
fetchRequest.fetchBatchSize = 4
fetchRequest.predicate = predicate
let fetchedResultsController: NSFetchedResultsController<T> =
NSFetchedResultsController<T>(fetchRequest: fetchRequest, managedObjectContext: mainContext, sectionNameKeyPath: nil, cacheName: nil)
try fetchedResultsController.performFetch()
return fetchedResultsController
}
Then you can invoke it using:
if let frc: NSFetchedResultsController<SchoolDetails> = try? CD.FRCWith(sortArray: [], mainContext: moc) {
// Do something with results
if let schoolDetails = frc.fetchedObjects?.first {
// schoolDetails will be an instance of SchoolDetails
}
}
Note that I have declared the function as throws rather than hiding the error inside the function.

NSFetchedResultsController fetchedObjects returning nil

I used below code fetch the list of Article from core data and it is giving me the expected result.
let moc = CoreDataHelper().backgroundContext
let employeesFetch = NSFetchRequest<NSFetchRequestResult>(entityName:"Article")
do {
let fetchedEmployees = try moc?.fetch(employeesFetch) as! [Article]
print(fetchedEmployees.count)
} catch {
fatalError("Failed to fetch employees: \(error)")
}
Output : 376
But when I am trying to fetch with NSFetchedResultsController. The fetchedObjects always returning nil. I used below code.
fileprivate lazy var fetchedResultsController: NSFetchedResultsController<Article> = {
// Create Fetch Request
let moc = CoreDataHelper().backgroundContext
// let fetchRequest: NSFetchRequest<Article> = Article.fetchRequest()
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName:"Article")
// Configure Fetch Request
fetchRequest.sortDescriptors = [NSSortDescriptor(key:"category", ascending: true)]
// Create Fetched Results Controller
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext:moc!, sectionNameKeyPath: nil, cacheName: nil)
// Configure Fetched Results Controller
fetchedResultsController.delegate = self
let quotes = fetchedResultsController.fetchedObjects
return fetchedResultsController as! NSFetchedResultsController<Article>
}()
The quotes is always nil. I am not able to figure out what is the problem?
try calling performFetch() on fetchedResultsController outside the closure.
hope that will work.

How to initialize a NSFetchedResultsController in iOS 10 Swift 3

I can't get my NSFetchedResultsController initialized in iOS 10 using Swift 3 within CoreDataTableViewController from AECoreDataUI.
let request = NSFetchRequest<NasaPicture>(entityName:"NasaPicture")
request.predicate = Predicate(format: "isPagedResult == YES")
request.sortDescriptors = [SortDescriptor(key: "date", ascending: false)]
fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: moc, sectionNameKeyPath: nil, cacheName: nil)
Compiler is complaining that
"Cannot convert value of type NSFetchedResultsController<NasaPicture> to expected type NSFetchedResultsController<_>"
The controller is now using generic type for swift, but it is not inferring the entity type correctly. I've tried explicitly:
fetchedResultsController = NSFetchedResultsController<NasaPicture>(fetchRequest: request, managedObjectContext: moc, sectionNameKeyPath: nil, cacheName: nil)
No luck either.
Thanks!
NSFetchRequest is now a generic. NSFetchedResultsController is a generic too. Therefore, when declaring variables, you have to use a generic declaration, e.g.:
var fetchedResultsController: NSFetchedResultsController<NasaPicture>?
when we need to declare variable, then we have to use a generic declaration
please check below code for NSFetchedResultsController in swift 3..
override func viewDidLoad() {
super.viewDidLoad()
do {
try self.fetchedResultsController.performFetch()
} catch {
let fetchError = error as NSError
print("Unable to Perform Fetch Request")
print("\(fetchError), \(fetchError.localizedDescription)")
}
}
// MARK: - NSFetchedResultsController
fileprivate lazy var fetchedResultsController: NSFetchedResultsController<UserExistenceOnXMPPCD> = {
let fetchRequest = NSFetchRequest<UserExistenceOnXMPPCD>(entityName: "UserExistenceOnXMPPCD")
fetchRequest.sortDescriptors = [
NSSortDescriptor(key: "name", ascending: true)]
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext:CoreDataController.sharedInstance.managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
try! fetchedResultsController.performFetch()
fetchedResultsController.delegate = self
if let quotes = fetchedResultsController.fetchedObjects {
if quotes.count > 0 {
print(quotes.count)
}
}
return fetchedResultsController
}()
// MARK: - NSFetchedResultsController delegate methods
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.reloadData()
}
Simply call the function:
private func setupFetchedResultsController() {
let context = NSManagedObjectContext.mr_default()
let fetchRequest = NSFetchRequest<Month>(entityName: "Month")
let dateDescriptor = NSSortDescriptor(key: "date", ascending: false)
fetchRequest.sortDescriptors = [dateDescriptor]
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: "year", cacheName: nil)
fetchedResultsController.delegate = self
try! fetchedResultsController.performFetch()
tableView.reloadData()
}

Resources