the fetchedresultController does not return my custom nsmanagedObject in prepareForSegue.
When I add a new entry in my tableView (the "+" button that calls "insertNewObject"), the correct name, from my custom object, appears, with the correct text. But when I click on the line to perform the segue, there is a crash, exc breakpoint, I click on continue, and the program continues without adding more information.
The code to cast the nsmanagedObject as a Section class :
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow() {
println("idx: \(indexPath) , objet : \(self.fetchedResultsController.objectAtIndexPath(indexPath))") //outputs ""
let object = self.fetchedResultsController.objectAtIndexPath(indexPath) as Section//NSManagedObject
let controller = (segue.destinationViewController as UINavigationController).topViewController as DetailViewController
//controller.detailItem = object
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
var fetchedResultsController: NSFetchedResultsController {
if _fetchedResultsController != nil {
return _fetchedResultsController!
}
let fetchRequest = NSFetchRequest()
let entity = NSEntityDescription.entityForName("Section", inManagedObjectContext: self.managedObjectContext!)
fetchRequest.entity = entity
fetchRequest.fetchBatchSize = 20
let sortDescriptor = NSSortDescriptor(key: "name", ascending: false)
let sortDescriptors = [sortDescriptor]
fetchRequest.sortDescriptors = [sortDescriptor]
let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext!, sectionNameKeyPath: nil, cacheName: nil)
aFetchedResultsController.delegate = self
_fetchedResultsController = aFetchedResultsController
var error: NSError? = nil
if !_fetchedResultsController!.performFetch(&error) {
abort()
}
return _fetchedResultsController!
}
var _fetchedResultsController: NSFetchedResultsController? = nil
func insertNewObject(sender: AnyObject) { //this works
let context = self.fetchedResultsController.managedObjectContext
let entity = self.fetchedResultsController.fetchRequest.entity!
let newManagedObject = NSEntityDescription.insertNewObjectForEntityForName(entity.name!, inManagedObjectContext: context) as Section
newManagedObject.name = String("insert")
var error: NSError? = nil
if !context.save(&error) {
abort()
}
}
func configureCell(cell: UITableViewCell, atIndexPath indexPath: NSIndexPath) {
let section = self.fetchedResultsController.objectAtIndexPath(indexPath) as Section
cell.textLabel.text = section.name;//this works
}
Related
I have two view controllers (vc1 and vc2). On vcTwo, when I add a game, I save the item using core data. The item should be displayed immediately on vcOne from a collection view. But the collection view does not load the data immediately even with collectionView.reloadData. I have to restart the app in order to see the last added item on the collection view. How could I make it happen.
var game: GameMo?
var gamesMo: [GameMo]? = []
var fetchRequestController : NSFetchedResultsController<GameMo>!
extension ViewControllerOne: NSFetchedResultsControllerDelegate {
func fetechRequest (){
let fetchRequest = NSFetchRequest<GameMo>(entityName: "Game")
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "win", ascending: true)]
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate){
let context = appDelegate.persistentContainer.viewContext
// fetch result controller
fetchRequestController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
fetchRequestController.delegate = self
do{
try fetchRequestController.performFetch()
if let fetchedObjects = fetchRequestController.fetchedObjects {
gamesMo = fetchedObjects
print(fetchRequest)
}
}catch{
fatalError("Failed to fetch entities: \(error)")
}
}
}
ViewController One
class ViewControllerTwo: UIViewController {
var game: GameMo?
func saveToCoreData(){
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
game = GameMo(context: appDelegate.persistentContainer.viewContext)
game?.gameOutcome = gameOutcome.text
game?.goal = Int32(goal.text ?? "0") ?? 0
game?.rivalGoal = Int32(rivalGoal.text ?? "0") ?? 0
print("Saving data")
appDelegate.saveContext()
delegate?.reloadCollectionViewData()
}
}
You should call fetchRequest() of VC1 after the line appDelegate.saveContext() in the saveToCoreData() function of VC2.
I'm trying to add a SearchBar to a TableView, which is storing its data with CoreData. But when I try to search the TableView isn't updating. I think that the fetch is working, but table is not being updated.
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.isEmpty {
return
}
else {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let sRequest: NSFetchRequest<Note> = Note.fetchRequest()
sRequest.predicate = NSPredicate(format: "SELF.title CONTAINS[cd] %#", searchText)
sRequest.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)]
let fetchedResultsController = NSFetchedResultsController(fetchRequest: sRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
print(sRequest)
do {
try fetchedResultsController.performFetch()
} catch {
fatalError("Failed to fetch entities: \(error)")
}
self.mainTable.reloadData()
}
return
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:UITableViewCell = mainTable.dequeueReusableCell(withIdentifier: "prototypeCell")!
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let tableRequest: NSFetchRequest<Note> = Note.fetchRequest()
tableRequest.sortDescriptors = [NSSortDescriptor(key: "date", ascending: false)]
var fetchedResultsController = NSFetchedResultsController(fetchRequest: tableRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
do {
try fetchedResultsController.performFetch()
} catch {
fatalError("Failed to fetch entities: \(error)")
}
let noteCell = fetchedResultsController.object(at: indexPath)
cell.textLabel?.text = noteCell.title!
let formatter = DateFormatter()
formatter.dateFormat = "d.M.y, HH:mm"
let dateString = formatter.string(from: noteCell.date! as Date)
cell.detailTextLabel?.text = dateString
return cell
}
The problem is this line:
let fetchedResultsController = NSFetchedResultsController(...
let means that this is a local variable. So you create a new fetched results controller and then throw it away.
Meanwhile, in your cellForRowAt, you refer to something else called fetchedResultsController:
var fetchedResultsController = ...
These are two completely different objects.
The fact is that both are wrong. You should not be doing a fetch every time your table view data source is called upon to consider a row. Look at how the Xcode app templates work! There is one fetched results controller, permanently. It does its fetch once and then just sits there holding the data. That is what you should be doing.
I never imagined it'd be so hard to reorder a table and save the new order to Core Data (and perhaps I'm overthinking it). The bit of code below is throwing me the following error: "The number of rows contained in an existing section after the update must be equal to the number of rows contained in that section before the update."
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
initializeFetchedResultsController()
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context2: NSManagedObjectContext = appDel.managedObjectContext
let request2 = NSFetchRequest(entityName: "Activities")
let activityOrderSort = NSSortDescriptor(key: "activityOrder", ascending: true)
request2.sortDescriptors = [activityOrderSort]
let predicate = NSPredicate(format: "date == %#", date)
request2.predicate = predicate
var fetchResults2: [NSManagedObject]
do {
try fetchResults2 = (appDel.managedObjectContext.executeFetchRequest(request2) as! [NSManagedObject])
if fromIndexPath.row > toIndexPath.row {
for i in toIndexPath.row..<fromIndexPath.row {
fetchResults2[i].setValue(i+1, forKey: "activityOrder")
}
fetchResults2[fromIndexPath.row].setValue(toIndexPath.row, forKey: "activityOrder")
}
if fromIndexPath.row < toIndexPath.row {
for i in fromIndexPath.row + 1...toIndexPath.row {
fetchResults2[i].setValue(i-1, forKey: "activityOrder")
}
fetchResults2[fromIndexPath.row].setValue(toIndexPath.row, forKey: "activityOrder")
}
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
}
do {
try appDel.managedObjectContext.save()
} catch let error as NSError {
print("Saving error: \(error.localizedDescription)")
}
initializeFetchedResultsController()
}
Here is the initializeFetchedResultsController() code for reference:
func initializeFetchedResultsController() {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
context = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "Activities")
let orderSort = NSSortDescriptor(key: "activityOrder", ascending: true)
fetchRequest.sortDescriptors = [orderSort]
let predicate = NSPredicate(format: "date == %#", date)
fetchRequest.predicate = predicate
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest,
managedObjectContext: self.context,
sectionNameKeyPath: nil,
cacheName: nil)
fetchedResultsController.delegate = self
do {
try fetchedResultsController.performFetch()
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
}
}
I tried to pull as much as I could from the answer from a previous post here: Save new order to core data after the using the tableView:moveRowAtIndexPath:toIndexPath: method. Also, I have the canEditRowAtIndexPath function set up and it appears to be functioning properly. Is there anything obvious that I'm messing up in the provided code? Is there an easier solution? Thanks for the support.
I'm using the NSFetchedResultsController, to populate a UITableView, i'm trying to add a category filter, so if the user choose a category a need to reload the data on the UITableView, this is what i'm trying to do.
var categoriaAtual : Int?
var fetchedResultsController: NSFetchedResultsController {
if _fetchedResultsController != nil {
print("Already fetch")
return _fetchedResultsController!
} else {
print("New Fetch")
}
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
self.managedObjectContext = managedContext
let fetchRequest = NSFetchRequest()
// Edit the entity name as appropriate.
let entity = NSEntityDescription.entityForName("Frases", inManagedObjectContext: self.managedObjectContext!)
fetchRequest.entity = entity
// Set the batch size to a suitable number.
fetchRequest.fetchBatchSize = 20
// Edit the sort key as appropriate.
let sortDescriptor = NSSortDescriptor(key: "favorita", ascending: false)
fetchRequest.sortDescriptors = [sortDescriptor]
fetchRequest.fetchLimit = 20
if categoriaAtual != nil {
print("Categoria Atual \(categoriaAtual)")
fetchRequest.predicate = NSPredicate(format: "categoria = %d",categoriaAtual!)
fetchRequest.fetchLimit = 2
} else {
print("No predicate");
}
//
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext!, sectionNameKeyPath: nil, cacheName: nil)
aFetchedResultsController.delegate = self
_fetchedResultsController = aFetchedResultsController
do {
try _fetchedResultsController!.performFetch()
} catch {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
//print("Unresolved error \(error), \(error.userInfo)")
abort()
}
return _fetchedResultsController!
}
var _fetchedResultsController: NSFetchedResultsController? = nil
func setarCategoria(cat: Int) {
categoriaAtual = cat
_fetchedResultsController = nil
self.tableView.reloadData()
}
Now i have a slide out menu called MenuTableView.swift,i make the call like this: ( I Think the problem is here )
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let homeVC = storyboard.instantiateViewControllerWithIdentifier("HomeViewController") as! HomeTableViewController
homeVC.setarCategoria(indexPath.row)
self.slideMenuController()?.closeLeft()
}
I do get the print saying that this is a new fetch, but the TableView does not change at all.
instantiateViewControllerWithIdentifer creates a new instance of the viewController (and therefore a new instance of the fetchedResultsController), which is how you get the "New Fetch" log but your original table view doesn't change. You probably just need to give MenuTableView a delegate that your HomeViewController can implement.
I have a Core Data model called 'List' with 4 attributes and wanted to populate the data from the JSON file to an array of type 'List'
when I run the app, it gives me the error 'fatal error: Array index out of range'
var loadNames = [List]()
var context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext!
var frc: NSFetchedResultsController = NSFetchedResultsController()
func getFetchedResultsController() -> NSFetchedResultsController {
frc = NSFetchedResultsController(
fetchRequest: listFetchRequest(),
managedObjectContext: context,
sectionNameKeyPath: "done",
cacheName: nil)
return frc
}
func listFetchRequest() -> NSFetchRequest {
let fetchRequest = NSFetchRequest(entityName: "List")
let doneSortDescriptor = NSSortDescriptor(key: "done", ascending: false)
let nameSortDescriptor = NSSortDescriptor(key: "firstName", ascending: true)
fetchRequest.sortDescriptors = [doneSortDescriptor, nameSortDescriptor]
return fetchRequest
}
func isFrequent(item: List) -> Bool {
return item.frequent == true
}
override func viewWillAppear(animated: Bool) {
var readError:NSError?
let filePath = NSBundle.mainBundle().pathForResource("NameList", ofType:"json")
let data = NSData(contentsOfFile:filePath!, options: NSDataReadingOptions.DataReadingUncached, error:&readError)
var jData = NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers, error: &readError) as! NSArray
for i in 0..<jData.count {
if let jfName = jData[i]["firstName"] as? String {
if let jlName = jData[i]["lastName"] as? String {
if let jDone = jData[i]["done"] as? NSNumber {
if let jFrequent = jData[i]["frequent"] as? NSNumber {
loadNames[i].firstName = jfName //This is where the error is pointing.
loadNames[i].lastName = jlName
loadNames[i].done = jDone
loadNames[i].frequent = jFrequent
println(loadNames.count)
}
}
}
}
}
}
the variables all seem to be getting the data from the JSON file and the value of 'i' in the 'for' loop when the error is happening is '0'.
I don't know why this is happening.
the array count is returning 6, which is the actual amount of objects in the JSON file.
You should use the loadNames array's append method to add each List object. Trying to access index i >= 0 in a zero length array is what is causing your error.