My app is quite simple: a TableViewController displays the content stored in Core Data. An add button pushes a second view modally which allows me to save stuff into Core Data. I am using NSFetchedResultsController to populate the main VC. If I save stuff to Core Data and present the main view controller again, my tableView doesn't refresh its data. I think it is because the delegate methods are not called. Any idea why?
import UIKit
import CoreData
class MainTableViewController: UITableViewController, NSFetchedResultsControllerDelegate {
var managedContext: NSManagedObjectContext!
var fetchedResultsController: NSFetchedResultsController!
lazy var coreDataStack = CoreDataStack()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.setToolbarItems(toolBar().cons, animated: true)
self.navigationController?.setToolbarHidden(false, animated: true)
let fetchRequest = NSFetchRequest(entityName: "CDPodcast")
let sort = NSSortDescriptor(key: "artist", ascending: true)
fetchRequest.sortDescriptors = [sort]
managedContext = coreDataStack.context
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedContext, sectionNameKeyPath: nil, cacheName: nil)
fetchedResultsController.delegate = self
var error: NSError? = nil
if (!fetchedResultsController.performFetch(&error)) {
println("Error: \(error?.localizedDescription)")
}
}
None of the below are called
func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
println("change section")
}
func controllerWillChangeContent(controller: NSFetchedResultsController) {
tableView.reloadData()
println("begin updates")
}
func controllerDidChangeContent(controller: NSFetchedResultsController) {
tableView.reloadData()
println("end updates")
}
Edit: More code
func saveToCoreData(name: String, artist: String, summary: String, feedURL: String, artworkURL: String, date: NSDate, episodes: [MWFeedItem]) {
self.managedContext = coreDataStack.context
let podcastEntity = NSEntityDescription.entityForName("CDPodcast", inManagedObjectContext: self.managedContext)
currentPodcast = CDPodcast(entity: podcastEntity!, insertIntoManagedObjectContext: self.managedContext)
currentPodcast.name = name
currentPodcast.artist = artist
currentPodcast.summary = summary
currentPodcast.feedURL = feedURL
currentPodcast.artworkURL = artworkURL
currentPodcast.date = date
let episodesEntity = NSEntityDescription.entityForName("CDEpisode", inManagedObjectContext: self.managedContext)
for var i = 0; i < episodes.count; i++ {
let episodesToSave = CDEpisode(entity: episodesEntity!, insertIntoManagedObjectContext: self.managedContext)
var episode: AnyObject = currentPodcast.episode.mutableCopy() as! NSMutableOrderedSet
println("There are \(episodes.count) items in the episodes array")
episodesToSave.title = episodes[i].title
episodesToSave.downloadURL = episodes[i].enclosures[0].valueForKey("url") as! String
episodesToSave.showNotes = episodes[i].summary
episode.addObject(episodesToSave)
currentPodcast.episode = episode.copy() as! NSOrderedSet
}
self.coreDataStack.saveContext()
}
The issue with this was that my ManagedObjectContext wasn't being passed around properly. println(context) returned different memory addresses between the App Delegate and View Controller #3. I didn't find a proper way to fix this so I am just passing the context between view controllers. Having the right context fixed it.
Related
I have two relationship entities in Core Data. In the first entity I add names of companies in attribute. Then I move to second UIViewController and I add other attributes of specific company in the second entity which is relationship with the first entity. Also I have two UITableViewControllers, the first UITableViewController shows names of every company in cell. When I selected the specific company I move to the second UITableViewController. I want the second UITableViewController shows data of the specific company which I selected. I tried several different methods but I could not to make how I want. How can I make it?
The second UITableViewController code
override func viewDidLoad() {
super.viewDidLoad()
self.definesPresentationContext = true
println(currentCompany)
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest(), managedObjectContext: managedObjectContext, sectionNameKeyPath: "activityCompany", cacheName: nil)
fetchedResultsController.delegate = self
fetchedResultsController.performFetch(nil)
}
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest(), managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
self.tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - var and let
var currentCompany: String!
var managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext!
// MARK: - fetchResultsController
var fetchedResultsController: NSFetchedResultsController!
func fetchRequest() -> NSFetchRequest {
let fetchRequest = NSFetchRequest(entityName: "Information")
var sortDescriptor = NSSortDescriptor(key: "company", ascending: true)
fetchRequest.fetchBatchSize = 50
fetchRequest.sortDescriptors = [sortDescriptor]
fetchRequest.predicate = NSPredicate(format: "company contains [c] %#", currentCompany)
return fetchRequest
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return fetchedResultsController.sections?.count ?? 0
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fetchedResultsController.sections?[section].numberOfObjects ?? 0
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell
if var dataForCell = fetchedResultsController.objectAtIndexPath(indexPath) as? Information {
cell.textLabel?.text = dataForCell.activityCompany
cell.detailTextLabel?.text = dataForCell.foundedCompany
}
return cell
}
Entities
import Foundation
import CoreData
#objc(Company)
class Company: NSManagedObject {
#NSManaged var nameCompany: String
#NSManaged var information: NSSet
}
import Foundation
import CoreData
#objc(Information)
class Information: NSManagedObject {
#NSManaged var activityCompany: String
#NSManaged var foundedCompany: String
#NSManaged var company: Company
}
I save data into entites the following method.
// MARK: - #IBActions
#IBAction func saveData(sender: UIBarButtonItem) {
var companyEntity = NSEntityDescription.insertNewObjectForEntityForName("Company", inManagedObjectContext: managedObjectContext) as! NSManagedObject
var informationEntity = NSEntityDescription.insertNewObjectForEntityForName("Information", inManagedObjectContext: managedObjectContext) as! NSManagedObject
companyEntity.setValue(currentName, forKey: "nameCompany")
informationEntity.setValue(activityTextField.text, forKey: "activityCompany")
informationEntity.setValue(foundedTextField.text, forKey: "foundedCompany")
companyEntity.setValue(NSSet(object: informationEntity), forKey: "information")
var error: NSError?
managedObjectContext.save(&error)
var story = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
var naviController = story.instantiateViewControllerWithIdentifier("firstNavi") as! UINavigationController
presentViewController(naviController, animated: true, completion: nil)
}
I solved it.
func fetchRequest() -> NSFetchRequest {
let fetchRequest = NSFetchRequest(entityName: "Information")
var sortDescriptor = NSSortDescriptor(key: "activityCompany", ascending: true)
fetchRequest.fetchBatchSize = 50
fetchRequest.sortDescriptors = [sortDescriptor]
fetchRequest.predicate = NSPredicate(format: "company.nameCompany contains [c] %#", currentCompany)
return fetchRequest
}
I made two relationship entities in Xcode, I use the Swift. I made a simple relationship in Core Data.
Company's code
import Foundation
import CoreData
#objc(Company)
class Company: NSManagedObject {
#NSManaged var nameCompany: String
#NSManaged var additional: NSSet
}
The additional's code
import Foundation
import CoreData
#objc(Additional)
class Additional: NSManagedObject {
#NSManaged var acitvityCompany: String
#NSManaged var founded: String
#NSManaged var company: Company
}
I wrote a following code which adds data into entities. It works.
#IBAction func saveData(sender: UIBarButtonItem) {
var companyEntity = NSEntityDescription.insertNewObjectForEntityForName("Company", inManagedObjectContext: managedObjectContext) as! NSManagedObject
var additionalEntity = NSEntityDescription.insertNewObjectForEntityForName("Additional", inManagedObjectContext: managedObjectContext) as! NSManagedObject
companyEntity.setValue(nameCompanyTextField.text, forKey: "nameCompany")
additionalEntity.setValue(activityTextField.text, forKey: "acitvityCompany")
additionalEntity.setValue(foundedTextField.text, forKey: "founded")
companyEntity.setValue(NSSet(object: additionalEntity), forKey: "additional")
managedObjectContext.save(nil)
var storyboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
var navi = storyboard.instantiateViewControllerWithIdentifier("navi") as! UINavigationController
self.presentViewController(navi, animated: true, completion: nil)
// println("company \(companyEntity)")
// println("additional \(additionalEntity)")
}
When I move to UITableViewController which shows data from entities. If I fetch data from Additional entity I get an error. CoreData: FATAL ERROR: The persistent cache of section information does not match the current configuration. You have illegally mutated the NSFetchedResultsController's fetch request, its predicate, or its sort descriptor without either disabling caching or using +deleteCacheWithName:
If I fetch data only from Company entity it works good.
My UITableViewController
override func viewDidLoad() {
super.viewDidLoad()
self.definesPresentationContext = true
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest(), managedObjectContext: managedObjectContext, sectionNameKeyPath: "nameCompany", cacheName: "nameCompany")
fetchedResultsController.delegate = self
fetchedResultsController.performFetch(nil)
fetchedResultsController2 = NSFetchedResultsController(fetchRequest: fetchRequest2(), managedObjectContext: managedObjectContext, sectionNameKeyPath: "company", cacheName: "company")
fetchedResultsController2.delegate = self
fetchedResultsController2.performFetch(nil)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.tableView.reloadData()
}
// MARK: - var and let
var managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext!
// MARK: - fetchedResultsController
var fetchedResultsController: NSFetchedResultsController!
var fetchedResultsController2: NSFetchedResultsController!
func fetchRequest() -> NSFetchRequest {
let fetchRequest = NSFetchRequest(entityName: "Company")
let sortDescriptor = NSSortDescriptor(key: "nameCompany", ascending: true)
fetchRequest.fetchBatchSize = 50
fetchRequest.predicate = nil
fetchRequest.sortDescriptors = [sortDescriptor]
return fetchRequest
}
func fetchRequest2() -> NSFetchRequest {
let fetchRequest = NSFetchRequest(entityName: "Additional")
let sortDescriptor = NSSortDescriptor(key: "company", ascending: true)
fetchRequest.fetchBatchSize = 50
fetchRequest.predicate = nil
fetchRequest.sortDescriptors = [sortDescriptor]
return fetchRequest
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return fetchedResultsController.sections?.count ?? 0
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fetchedResultsController.sections?[section].numberOfObjects ?? 0
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell
if var dataForCell = fetchedResultsController.objectAtIndexPath(indexPath) as? Company {
var additionalCell = fetchedResultsController2.objectAtIndexPath(indexPath) as! Additional
cell.textLabel?.text = dataForCell.nameCompany
cell.detailTextLabel?.text = "\(additionalCell.acitvityCompany) - \(additionalCell.founded)"
}
return cell
}
I understood how to make it.
var currentDate: NSDate!
// MARK: - NSFetchedResultsController
var fetchedResultsController: NSFetchedResultsController!
func fetchRequest() -> NSFetchRequest {
let fetchRequest = NSFetchRequest(entityName: "Car")
let sortDescriptor = NSSortDescriptor(key: "personCar", ascending: true)
fetchRequest.predicate = NSPredicate(format: "personRelationship.datePerson contains[c] %#", currentDate)
fetchRequest.fetchBatchSize = 50
fetchRequest.fetchLimit = 50
fetchRequest.sortDescriptors = [sortDescriptor]
println(fetchRequest)
return fetchRequest
}
So, I'm struggling to learn how to handle CoreData properly.
Here is my code:
import UIKit
import CoreData
class IngredientsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, NSFetchedResultsControllerDelegate {
let moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
var fetchedResultsController: NSFetchedResultsController?
override func viewDidLoad() {
super.viewDidLoad()
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchIngredients(), managedObjectContext: moc!, sectionNameKeyPath: nil, cacheName: nil)
fetchedResultsController?.delegate = self
fetchedResultsController?.performFetch(nil)
}
func fetchIngredients() -> NSFetchRequest {
var fetchRequest = NSFetchRequest(entityName: "DetailsForRecipe")
let sortDescriptor = NSSortDescriptor(key: "ingredients", ascending: true)
fetchRequest.predicate = nil
fetchRequest.sortDescriptors = [sortDescriptor]
fetchRequest.fetchBatchSize = 20
return fetchRequest
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fetchedResultsController?.sections?[section].numberOfObjects ?? 0
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("ingCell", forIndexPath: indexPath) as! UITableViewCell
if let ingCell = fetchedResultsController?.objectAtIndexPath(indexPath) as? DetailsForRecipe {
cell.textLabel?.text = ingCell.ingredients
}
return cell
}
}
and
import UIKit
import CoreData
class SingleIngredientViewController: UIViewController {
#IBOutlet var ingField: UITextField!
var moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func addIng(sender: AnyObject) {
let entityDescription = NSEntityDescription.entityForName("DetailsForRecipe", inManagedObjectContext: moc!)
let details = DetailsForRecipe(entity: entityDescription!, insertIntoManagedObjectContext: moc)
details.ingredients = ingField.text
var error: NSError?
moc?.save(&error)
if let err = error {
var status = err.localizedFailureReason
println(status)
} else {
println("Ingredient \(ingField.text) saved successfully!")
}
if let navigation = navigationController {
navigation.popViewControllerAnimated(true)
}
}
}
My model:
import Foundation
import CoreData
class DetailsForRecipe: NSManagedObject {
#NSManaged var name: String
#NSManaged var ingredients: String
#NSManaged var image: NSData
}
The app should insert the ingredient name in the text field, save it into coreData, then it should be retrieved in the table view. When I text the ingredient name and press "add", the prinln message says it was successfully saved, but the table view does not update.
What am I doing wrong here? I'm not a developer, I've read many tutorials on how to do this, but it is very confusing! So forgive me about this.
Thanks in advance!
I haven't read your full source code, but by doing an immediate glance, I don't see that you've called the tableview's reloadData function. If you don't have an IBOutlet set up for your table view, you will want to do that and then call reloadData() on it.
Call reloadData() on your table view after the change is made.
my app is very simple: I have one tableview to store recipe *names and, for each recipe name, another tableview with several *ingredients for each recipe.
I already managed to save the name and the ingredients with CoreData, but here is the problem: when I press to add a NEW recipe name and enter the ingredients table view area, the ingredients saved for the previous recipe are there! How do I clear the table view to start a new one?
Also, my table views does not get updated immediately, I have to close the app and open it again. How do I fix it?
Note: If my question is too hard to understand, I can post some code! Thanks in advance, everyone =)
EDIT
code:
import UIKit
import CoreData
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, NSFetchedResultsControllerDelegate {
#IBOutlet var tableView: UITableView!
var imageList: [UIImage] = []
var moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
var fetchedResultsController: NSFetchedResultsController?
override func viewDidLoad() {
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchName(), managedObjectContext: moc!, sectionNameKeyPath: nil, cacheName: nil)
fetchedResultsController?.delegate = self
fetchedResultsController?.performFetch(nil)
tableView.reloadData()
}
func fetchName() -> NSFetchRequest {
var fetchRequest = NSFetchRequest(entityName: "Details")
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.predicate = nil
fetchRequest.sortDescriptors = [sortDescriptor]
fetchRequest.fetchBatchSize = 20
return fetchRequest
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fetchedResultsController?.sections?[section].numberOfObjects ?? 0
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("recipeCell", forIndexPath: indexPath) as! UITableViewCell
if let recipeCell = fetchedResultsController?.objectAtIndexPath(indexPath) as? Details {
cell.textLabel?.text = recipeCell.name
}
return cell
}
}
-
import UIKit
import CoreData
class InfoViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UITableViewDelegate {
#IBOutlet var nameField: UITextField!
#IBOutlet var imageView: UIImageView!
var moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
override func viewDidLoad() {
super.viewDidLoad()
//Pick the image by tap
let tapGestureRecognizer: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "chooseImage:")
tapGestureRecognizer.numberOfTapsRequired = 1
imageView.addGestureRecognizer(tapGestureRecognizer)
imageView.userInteractionEnabled = true
}
//Pick the image by tapping, accessing the photoLibrary
func chooseImage(recognizer: UITapGestureRecognizer) {
let imagePicker: UIImagePickerController = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
self.presentViewController(imagePicker, animated: true, completion: nil)
}
//Put the selected image into the screen
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject:AnyObject]) {
let pickedImage: UIImage = (info as NSDictionary).objectForKey(UIImagePickerControllerOriginalImage) as! UIImage
// small picture
let smallPicture = scaleImageWith(pickedImage, newSize: CGSizeMake(288,148))
var sizeOfImageView:CGRect = imageView.frame
sizeOfImageView.size = smallPicture.size
imageView.frame = sizeOfImageView
imageView.image = smallPicture
picker.dismissViewControllerAnimated(true, completion: nil)
}
func imagePickerControllerDidCancel(picker: UIImagePickerController) {
picker.dismissViewControllerAnimated(true, completion: nil)
}
func scaleImageWith(image:UIImage, newSize: CGSize) -> UIImage {
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
image.drawInRect(CGRectMake(0,0, newSize.width, newSize.height))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
#IBAction func addButton(sender: AnyObject) {
let entityDescription = NSEntityDescription.entityForName("Details", inManagedObjectContext: moc!)
let details = Details(entity: entityDescription!, insertIntoManagedObjectContext: moc)
details.name = nameField.text
var error: NSError?
moc?.save(&error)
if let err = error {
var status = err.localizedFailureReason
println("\(status)")
} else {
println("Ingredient \(nameField.text) saved successfully!")
}
if let navigation = navigationController {
navigation.popViewControllerAnimated(true)
}
}
}
-
import UIKit
import CoreData
class IngredientListViewController: UIViewController, NSFetchedResultsControllerDelegate {
#IBOutlet var tableView: UITableView!
var moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
var fetchedResultsController: NSFetchedResultsController?
override func viewDidLoad() {
super.viewDidLoad()
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchIngredient(), managedObjectContext: moc!, sectionNameKeyPath: nil, cacheName: nil)
fetchedResultsController?.delegate = self
fetchedResultsController?.performFetch(nil)
tableView.reloadData()
}
func fetchIngredient() -> NSFetchRequest {
var fetchRequest = NSFetchRequest(entityName: "Ingredients")
let sortDescriptor = NSSortDescriptor(key: "ingredients", ascending: true)
fetchRequest.predicate = nil
fetchRequest.sortDescriptors = [sortDescriptor]
fetchRequest.fetchBatchSize = 20
return fetchRequest
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fetchedResultsController?.sections?[section].numberOfObjects ?? 0
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("ingCell", forIndexPath: indexPath) as! UITableViewCell
if let recipeCell = fetchedResultsController?.objectAtIndexPath(indexPath) as? Ingredients {
cell.textLabel?.text = recipeCell.ingredients
}
return cell
}
}
-
import UIKit
import CoreData
class IngredientViewController: UIViewController {
#IBOutlet var nameField: UITextField!
var moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func addButton(sender: AnyObject) {
let entityDescription = NSEntityDescription.entityForName("Ingredients", inManagedObjectContext: moc!)
let details = Ingredients(entity: entityDescription!, insertIntoManagedObjectContext: moc)
details.ingredients = nameField.text
var error: NSError?
moc?.save(&error)
if let err = error {
var status = err.localizedFailureReason
println("\(status)")
} else {
println("Ingredient \(nameField.text) saved successfully!")
}
if let navigation = navigationController {
navigation.popViewControllerAnimated(true)
}
}
}
and models:
import Foundation
import CoreData
class Ingredients: NSManagedObject {
#NSManaged var ingredients: String
#NSManaged var relationship: NSSet
}
import Foundation
import CoreData
class Details: NSManagedObject {
#NSManaged var name: String
#NSManaged var relationship: Ingredients
}
In outline:
You need to amend your model: currently each Details object can have only one Ingredients. I suspect you need this relationship to be "to many" so a recipe can have many ingredients.
You need to add a var (of type Details?) to your IngredientListViewController. This will represent the chosen Recipe. (eg. var chosenRecipe : Details?)
In fetchIngredient, you need to add a predicate to the fetch, to limit the results to the chosen recipe. eg. fetch.predicate = NSPredicate(format:"ANY relationship == %#", chosenRecipe)
Before segueing to this VC, you need to set the chosenRecipe (probably in prepareForSegue, or didSelectRowAtIndexPath in the preceding table view).
To get your TV to update automatically, use the fetchedResultsController delegate methods. (Have you implemented these?)
I used Core Data for a table view with Swift Language. It can launch successfully, but after clicking Done button in AddView, there is an error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+entityForName: nil is not a legal NSManagedObjectContext parameter searching for entity name 'OneItemCD''
I don't know why. Could anybody help me figure it out? Thanks very much!
Here is the code. There is an add bar button item. Clicking this button transfer to AddViewController. After typing something in the text view and clicking Done button, transfer back to TableViewController.
TableViewController:
import UIKit
import CoreData
class TableViewController: UITableViewController, NSFetchedResultsControllerDelegate {
var managedObjectContext: NSManagedObjectContext? = nil
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView!) -> Int {
return 1
}
override func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
return 0
}
#IBAction func unwindToList(segue: UIStoryboardSegue) {
var source: AddViewController = segue.sourceViewController as AddViewController
source.managedObjectContext = self.managedObjectContext
var noteContent: String? = source.contentBody.text
if noteContent != nil {
let context = self.fetchedResultsController.managedObjectContext
let entity = self.fetchedResultsController.fetchRequest.entity
let newManagedObject = NSEntityDescription.insertNewObjectForEntityForName(entity.name, inManagedObjectContext: context) as NSManagedObject
newManagedObject.setValue(noteContent, forKey: "content")
var error: NSError? = nil
if !context.save(&error) {
abort()
}
self.tableView.reloadData()
}
}
// MARK: - Fetched results controller
var fetchedResultsController: NSFetchedResultsController {
if _fetchedResultsController != nil {
return _fetchedResultsController!
}
let fetchRequest = NSFetchRequest()
// Edit the entity name as appropriate.
let entity = NSEntityDescription.entityForName("OneItemCD", 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: "content", ascending: false)
let sortDescriptors = [sortDescriptor]
fetchRequest.sortDescriptors = [sortDescriptor]
// 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: "Master")
aFetchedResultsController.delegate = self
_fetchedResultsController = aFetchedResultsController
var error: NSError? = nil
if !_fetchedResultsController!.performFetch(&error) {
abort()
}
return _fetchedResultsController!
}
var _fetchedResultsController: NSFetchedResultsController? = nil
}
AddViewController:
import UIKit
import CoreData
class AddViewController: UIViewController {
#IBOutlet weak var contentBody: UITextView!
var managedObjectContext: NSManagedObjectContext? = nil
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I put my entire code in github:
https://github.com/snowffer/testTableViewForCoreData
When you set var fetchedResultsController, your managedObjectContext is still nil. Therefore, your app crashes when you declare:
let entity = NSEntityDescription.entityForName("OneItemCD", inManagedObjectContext: self.managedObjectContext)
Make sure to set managedObjectContext correctly before you call the previous line of code.
One more thing:
To use a Swift subclass of OneItemCD with your Core Data model, prefix the class name in the Class field of the model entity inspector with the name of your module, just like this: "testTableViewCoreData.OneItemCD" (See more about Core Data and namespaces here).