Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSManagedObject data is <fault> and entity is null, after UITableViewCell comes back into view

I am seeing some very odd behavior with Core Data using Swift and Xcode 7.2. Below is an animation illustrating the issue.

Problem

I created a simple example with an entity named 'Person' that has only a 'name' attribute. I fetch all of the 'Person' entities and display them in a table view. If I force a cell to be reloaded then the textLabel that holds the name and NSManagedObject objectID displays without the name. The objectID, however, remains unchanged. This happens both on the device and in the simulator.

I'm printing the value of the Person entity to the console in "cellForRowAtIndexPath" so that I can show the value when the cell is initially loaded vs. when it's reloaded.

Person entity value

As you can see, it appears that the NSManagedObject is still present despite the fact that it seems to lose all references to its 'Person' subclass.

Here is the code for the affected ViewController:

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
  let cellIdentifier = "PersonCell"

  @IBOutlet weak var tableView: UITableView!

  var people: NSArray = []

  override func viewDidLoad() {
    super.viewDidLoad()

    people = fetchPeople()
  }

  func fetchPeople() -> NSArray {
    let context = AppDelegate().managedObjectContext
    let fetchRequest = NSFetchRequest(entityName: "Person")
    let sort = NSSortDescriptor(key: "name", ascending: true)
    fetchRequest.sortDescriptors = [sort]

    do {
      let results = try context.executeFetchRequest(fetchRequest)
      return results
    } catch {
      let error = error as NSError
      print("\(error.domain) \(error.code): \(error.localizedDescription)")
      return []
    }
  }

  // MARK: - UITableView methods

  func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return 1
  }

  func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return people.count
  }

  func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath)

    let person = people[indexPath.row]
    print("in cellForRowAtIndexPath:")
    print(people[0])

    cell.textLabel?.text = "\(person.name) - \(person.objectID)"
    return cell
  }
}

Any help is appreciated. Thank you.

like image 600
Erik Avatar asked Jan 06 '23 18:01

Erik


1 Answers

The problem is here:

let context = AppDelegate().managedObjectContext

You create a new instance of the application delegate, and with it a managed object context. As soon as program control returns from fetchPeople(), there is no reference to the context anymore and it is deallocated. The effect is that accessing properties of managed objects which were created in this context returns nil.

What you want is to get a reference to the (one and only) application delegate and its managed object context, something like

let context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext

Alternatively, pass the context to the view controller in the didFinishLaunchingWithOptions method or in prepareForSegue or whatever is suitable in your app.

like image 170
Martin R Avatar answered Jan 21 '23 16:01

Martin R