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