How to initialize a NSFetchedResultsController in iOS 10 Swift 3 - ios

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()
}

Related

Generic NSFetchedResultsController with generic NSFetchRequest results in an error

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
}()
}

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.

Ios Swift3 : lazy var fetchrequestcontroller showing an error

I was using a lazy var in table view controller to get the data from CoreData. It was working good in swift2. But When I upgraded to Swift3, Its showing an error as below. Kindly help me to change for Swift3
Error : Cannot convert value of type 'Error' to specified type 'NSFetchedResultsController'
lazy var fetchedResultsController: NSfetchedResultsController =
{
let fetchRequest = NSFetchRequest(entityName: "EvtIvtTbl")
let sortDescriptor = NSSortDescriptor(key: "bym_kol", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
let fetchedResultsController = NSfetchedResultsController(fetchRequest: fetchRequest,
managedObjectContext: self.managedObjectContext,
sectionNameKeyPath: nil,
cacheName: nil)
fetchedResultsController.delegate = self
return fetchedResultsController
}()
In Swift 3.0, NSFetchedResultsController and NSFetchRequest want type parameters. Assuming that EvtIvtTbl is your NSManagedObject subclass, your code should look like this:
lazy var fetchedResultsController: NSFetchedResultsController<EvtIvtTbl> = {
let fetchRequest = NSFetchRequest<EvtIvtTbl>(entityName: "EvtIvtTbl")
let sortDescriptor = NSSortDescriptor(key: "bym_kol", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
let fetchedResultsController = NSFetchedResultsController<EvtIvtTbl>(fetchRequest: fetchRequest,
managedObjectContext: self.managedObjectContext,
sectionNameKeyPath: nil,
cacheName: nil)
fetchedResultsController.delegate = self
return fetchedResultsController
}()

NSFetchedResultsController crashing when creating it, objc_exception_throw

I am receiving a objc_exception_throw when creating my NSFetchedResultsController. I am relatively new to iOS programming so I am still getting the hang of debugging iOS crashes.
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
lazy var parentContext: NSManagedObjectContext? = {
if let managedObjectContext = self.appDelegate.managedObjectContext {
return managedObjectContext
}
else {
return nil
}
}()
lazy var fetchedResultsController: NSFetchedResultsController = {
let request = NSFetchRequest()
let entity = NSEntityDescription.entityForName("GameDate", inManagedObjectContext: self.parentContext!)
request.entity = entity
let frc = NSFetchedResultsController(fetchRequest: request, managedObjectContext: self.parentContext!, sectionNameKeyPath: nil, cacheName: nil)
frc.delegate = self
return frc
}()
override func viewDidLoad() {
super.viewDidLoad()
performFetchFromDb()
setupTableView()
}
I am getting the crash on this line:
let frc = NSFetchedResultsController(fetchRequest: request, managedObjectContext: self.parentContext!, sectionNameKeyPath: nil, cacheName: nil)
Does this crash have something to do with my context?
I was missing an NSSortDescriptor in my fetch request. This fixed it:
lazy var fetchedResultsController: NSFetchedResultsController = {
let request = NSFetchRequest()
let entity = NSEntityDescription.entityForName("GameDate", inManagedObjectContext: self.parentContext!)
request.entity = entity
let gameTimeSort = NSSortDescriptor(key: "gameTime", ascending: false)
request.sortDescriptors = [gameTimeSort]
let frc = NSFetchedResultsController(fetchRequest: request, managedObjectContext: self.parentContext!, sectionNameKeyPath: nil, cacheName: nil)
frc.delegate = self
return frc
}()

Trouble calling NSFetchedResultsControllerDelegate methods

I've managed to create my UICollectionViewController with swift, and implement the NSFetchedResultsController and fetch stuff from my CoreData model. All good there... But...
I can't seem to call any of the NSFetchedResultsControllerDelegate methods from within my extension. Code is as follows:
extension MainsViewController: NSFetchedResultsControllerDelegate {
func fetchedResultsController() -> NSFetchedResultsController {
// if(fetchedResultsController != nil){
// return fetchedResultsController()
// }
var fetchRequest = NSFetchRequest()
var context: NSManagedObjectContext = RKObjectManager.sharedManager().managedObjectStore.mainQueueManagedObjectContext
var entity = NSEntityDescription.entityForName("Dessert", inManagedObjectContext: context)
fetchRequest.entity = entity
fetchRequest.fetchBatchSize = 20
var sortDescriptor = NSSortDescriptor(key: "createdAt", ascending: false)
let sortDescriptors = [sortDescriptor]
fetchRequest.sortDescriptors = sortDescriptors
fetchedResultsController().delegate = self
var aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: "Master")
var error: NSError?
if !aFetchedResultsController.performFetch(&error){
println("An error:\(error) occured.")
}
return aFetchedResultsController
}
//Here is where i would call *#optional func controllerWillChangeContent(_ controller: NSFetchedResultsController!)* etc. etc.
}
But swift doesnt recognise theese methods. I can't figure out what it is I am missing.
Would be great if someone could help me out.
Thanks in advance
Chris
I think it should something like this:
var fetchedResultsController: NSFetchedResultsController {
get {
var fetchRequest = NSFetchRequest()
var context: NSManagedObjectContext = RKObjectManager.sharedManager().managedObjectStore.mainQueueManagedObjectContext
var entity = NSEntityDescription.entityForName("Dessert", inManagedObjectContext: context)
fetchRequest.entity = entity
fetchRequest.fetchBatchSize = 20
var sortDescriptor = NSSortDescriptor(key: "createdAt", ascending: false)
let sortDescriptors = [sortDescriptor]
fetchRequest.sortDescriptors = sortDescriptors
self.fetchedResultsController.delegate = self
var aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: "Master")
var error: NSError?
if !aFetchedResultsController.performFetch(&error){
println("An error:\(error) occured.")
}
return aFetchedResultsController
}
}
So your MainsViewController is a class, as I said in the comment above, so the first line has to look like this:
class MainsViewController: UIViewController, NSFetchedResultsControllerDelegate {
Unfortunatly I cannot test the code, but this can be understood like this: You have a property called fetchedResultsController and this will execute, when the fetchedResultsController is getting. So in the getter like you would do it in objective-c with a lazy getting method.
I found out that it was simply the auto complete feature that wasn't suggesting the method to me. Typing it out manually 'fixed' the issue. Sorry for wasting everybody's time...
Chris

Resources