New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

xCode 6.3 and Swift 1.2: saveToPersistentStoreAndWait and NSFetchedResultsControllerDelegate #963

Closed
andreyslyusar opened this Issue Feb 16, 2015 · 9 comments

Comments

Projects
None yet
6 participants
@andreyslyusar

andreyslyusar commented Feb 16, 2015

I tried to migrate my project to xCode 6.3 with swift 1.2 support. But I start to experience strange issue:

  1. I am creating and updating new Core Data objects:
    private lazy var moc: NSManagedObjectContext = {
        return NSManagedObjectContext.MR_context();
    }()
  1. In code I create new objects in 'moc'
  2. As a final step
 moc.MR_saveToPersistentStoreAndWait()

In another place I have a UITableViewControoler with NSFetchedResultsControllerDelegate, but none of delegates fire after migration to xCode 6.3. Rolling back to swift 1.1 in xCode 6.1.1 everything works as expected.

Can anybody confirm the same behaviour? Or maybe any ideas why it can happen and possible workaround ?

@Spokane-Dude

This comment has been minimized.

Show comment
Hide comment
@Spokane-Dude

Spokane-Dude Feb 16, 2015

Workaround: stay with Objective-C! IMHO, Swift is not mature enough yet...

Andrey Slyusar wrote:

I tried to migrate my project to xCode 6.3 with swift 1.2 support. But
I start to experience strange issue:

  1. I am creating and updating new Core Data objects:

private lazyvar moc: NSManagedObjectContext= {
return NSManagedObjectContext.MR_context();
}()

  1. In code I create new objects in 'moc'
  2. As a final step

moc.MR_saveToPersistentStoreAndWait()

In another place I have a UITableViewControoler with
NSFetchedResultsControllerDelegate, but none of delegates fire after
migration to xCode 6.3. Rolling back to swift 1.1 in xCode 6.1.1
everything works as expected.

Can anybody confirm the same behaviour? Or maybe any ideas why it can
happen and possible workaround ?


Reply to this email directly or view it on GitHub
#963.

Spokane-Dude commented Feb 16, 2015

Workaround: stay with Objective-C! IMHO, Swift is not mature enough yet...

Andrey Slyusar wrote:

I tried to migrate my project to xCode 6.3 with swift 1.2 support. But
I start to experience strange issue:

  1. I am creating and updating new Core Data objects:

private lazyvar moc: NSManagedObjectContext= {
return NSManagedObjectContext.MR_context();
}()

  1. In code I create new objects in 'moc'
  2. As a final step

moc.MR_saveToPersistentStoreAndWait()

In another place I have a UITableViewControoler with
NSFetchedResultsControllerDelegate, but none of delegates fire after
migration to xCode 6.3. Rolling back to swift 1.1 in xCode 6.1.1
everything works as expected.

Can anybody confirm the same behaviour? Or maybe any ideas why it can
happen and possible workaround ?


Reply to this email directly or view it on GitHub
#963.

@andreyslyusar

This comment has been minimized.

Show comment
Hide comment
@andreyslyusar

andreyslyusar Feb 17, 2015

It is already to late to switch back to ObjC, too many code in swift to rewrite. It just working in xCode 6.1.1 without incremental updates is very painful, but without solving this issue I will need to rollback from swift 1.2 to 1.1.

andreyslyusar commented Feb 17, 2015

It is already to late to switch back to ObjC, too many code in swift to rewrite. It just working in xCode 6.1.1 without incremental updates is very painful, but without solving this issue I will need to rollback from swift 1.2 to 1.1.

@tonyarnold

This comment has been minimized.

Show comment
Hide comment
@tonyarnold

tonyarnold Feb 17, 2015

Contributor

It would be wise to play with Swift 1.2, but not commit to it until later in the seed cycle for Xcode 6.3. The beta we have right now seems to have some glaring bugs, many of which Apple engineers have acknowledged they are working on fixing for Swift 1.2's actual release.

Keep an eye on this issue all the same — if it comes up later in the seeds of Xcode 6.3, I'll need to look at fixing it.

Contributor

tonyarnold commented Feb 17, 2015

It would be wise to play with Swift 1.2, but not commit to it until later in the seed cycle for Xcode 6.3. The beta we have right now seems to have some glaring bugs, many of which Apple engineers have acknowledged they are working on fixing for Swift 1.2's actual release.

Keep an eye on this issue all the same — if it comes up later in the seeds of Xcode 6.3, I'll need to look at fixing it.

@andreyslyusar

This comment has been minimized.

Show comment
Hide comment
@andreyslyusar

andreyslyusar Feb 17, 2015

I try to reimplement similar logic without MagicRecord and delegate method is called. Swift 1.2 has incremental updates and this is a great benefit for pure swift project >300 classes. I will spent some time trying to find by myself or switch to stable xCode. Maybe you have any idea why it could happen?

class ViewController: UIViewController, UITableViewDataSource, NSFetchedResultsControllerDelegate {

    @IBOutlet weak var tableView: UITableView!

    var fetchedResultsController: NSFetchedResultsController!

    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate

    lazy var managedContext: NSManagedObjectContext = {
        return self.appDelegate.managedObjectContext!
    }()

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)

        let fetchRequest = NSFetchRequest(entityName: "Person")
        fetchRequest.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]

        fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: appDelegate.managedObjectContext!, sectionNameKeyPath: nil, cacheName: nil)
        self.fetchedResultsController.delegate = self

        var error: NSError?
        fetchedResultsController.performFetch(&error)

        tableView.reloadData()
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
    }

    // MARK: UITableViewDataSource
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return fetchedResultsController.fetchedObjects!.count
    }

    func tableView(tableView: UITableView,cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
      let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! UITableViewCell

      let person = fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject
      cell.textLabel!.text = person.valueForKey("name") as! String?

      return cell
    }

    //Implement the addName IBAction
    @IBAction func addName(sender: AnyObject) {
        var alert = UIAlertController(title: "New name", message: "Add a new name", preferredStyle: .Alert)

        let saveAction = UIAlertAction(title: "Save", style: .Default) { (action: UIAlertAction!) -> Void in
            let textField = alert.textFields![0] as! UITextField
            self.saveName(textField.text)
            self.tableView.reloadData()
        }

        let cancelAction = UIAlertAction(title: "Cancel", style: .Default) { (action: UIAlertAction!) -> Void in
        }

        alert.addTextFieldWithConfigurationHandler {
          (textField: UITextField!) -> Void in
        }

        alert.addAction(saveAction)
        alert.addAction(cancelAction)

        presentViewController(alert, animated: true, completion: nil)
    }

    func saveName(name: String) {
        //2
        let entity =  NSEntityDescription.entityForName("Person", inManagedObjectContext: managedContext)

        let person = NSManagedObject(entity: entity!, insertIntoManagedObjectContext:managedContext)

        //3
        person.setValue(name, forKey: "name")

        //4
        var error: NSError?
        if !managedContext.save(&error) {
          println("Could not save \(error), \(error?.userInfo)")
        }
    }


    // ========================================
    // MARK: - NSFetchedResultsController
    // ========================================
    func controllerWillChangeContent(controller: NSFetchedResultsController) {
        tableView.beginUpdates()
    }
}

andreyslyusar commented Feb 17, 2015

I try to reimplement similar logic without MagicRecord and delegate method is called. Swift 1.2 has incremental updates and this is a great benefit for pure swift project >300 classes. I will spent some time trying to find by myself or switch to stable xCode. Maybe you have any idea why it could happen?

class ViewController: UIViewController, UITableViewDataSource, NSFetchedResultsControllerDelegate {

    @IBOutlet weak var tableView: UITableView!

    var fetchedResultsController: NSFetchedResultsController!

    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate

    lazy var managedContext: NSManagedObjectContext = {
        return self.appDelegate.managedObjectContext!
    }()

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)

        let fetchRequest = NSFetchRequest(entityName: "Person")
        fetchRequest.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]

        fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: appDelegate.managedObjectContext!, sectionNameKeyPath: nil, cacheName: nil)
        self.fetchedResultsController.delegate = self

        var error: NSError?
        fetchedResultsController.performFetch(&error)

        tableView.reloadData()
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
    }

    // MARK: UITableViewDataSource
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return fetchedResultsController.fetchedObjects!.count
    }

    func tableView(tableView: UITableView,cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
      let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! UITableViewCell

      let person = fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject
      cell.textLabel!.text = person.valueForKey("name") as! String?

      return cell
    }

    //Implement the addName IBAction
    @IBAction func addName(sender: AnyObject) {
        var alert = UIAlertController(title: "New name", message: "Add a new name", preferredStyle: .Alert)

        let saveAction = UIAlertAction(title: "Save", style: .Default) { (action: UIAlertAction!) -> Void in
            let textField = alert.textFields![0] as! UITextField
            self.saveName(textField.text)
            self.tableView.reloadData()
        }

        let cancelAction = UIAlertAction(title: "Cancel", style: .Default) { (action: UIAlertAction!) -> Void in
        }

        alert.addTextFieldWithConfigurationHandler {
          (textField: UITextField!) -> Void in
        }

        alert.addAction(saveAction)
        alert.addAction(cancelAction)

        presentViewController(alert, animated: true, completion: nil)
    }

    func saveName(name: String) {
        //2
        let entity =  NSEntityDescription.entityForName("Person", inManagedObjectContext: managedContext)

        let person = NSManagedObject(entity: entity!, insertIntoManagedObjectContext:managedContext)

        //3
        person.setValue(name, forKey: "name")

        //4
        var error: NSError?
        if !managedContext.save(&error) {
          println("Could not save \(error), \(error?.userInfo)")
        }
    }


    // ========================================
    // MARK: - NSFetchedResultsController
    // ========================================
    func controllerWillChangeContent(controller: NSFetchedResultsController) {
        tableView.beginUpdates()
    }
}
@davetroy

This comment has been minimized.

Show comment
Hide comment
@davetroy

davetroy Mar 6, 2015

I found a possible cure for this.

  1. Be sure that you have implemented -controllerDidChangeContent (even if empty), otherwise the fetchedResultsController track-changes will not be enabled (per docs).

  2. Set the @objc directive on -controllerDidChangeContent and other NSFetchedResultsControllerDelegate methods, so the FRC can 'see' them (as it's an ObjC class).

I was having this problem and #2 above saved the day. Still fighting various other weird bugs, but that was by far the worst.

davetroy commented Mar 6, 2015

I found a possible cure for this.

  1. Be sure that you have implemented -controllerDidChangeContent (even if empty), otherwise the fetchedResultsController track-changes will not be enabled (per docs).

  2. Set the @objc directive on -controllerDidChangeContent and other NSFetchedResultsControllerDelegate methods, so the FRC can 'see' them (as it's an ObjC class).

I was having this problem and #2 above saved the day. Still fighting various other weird bugs, but that was by far the worst.

@andreyslyusar

This comment has been minimized.

Show comment
Hide comment
@andreyslyusar

andreyslyusar Apr 7, 2015

@davetroy Thanks for response.

  1. In my original implementation I has a -controllerDidChangeContent:
    func controllerDidChangeContent(controller: NSFetchedResultsController) {
        if ignoreNextUpdates {
            ignoreNextUpdates = false
        } else {
            tableView.endUpdates()
        }
    }
  1. @objc could be a case, but I had the issue only in case I implement delegate methods as a part of private extension. I cannot verify it now for the reason we switch to xCode 6.2 and wait for official release of xCode 6.3 (we need to make beta build to iTunes)

andreyslyusar commented Apr 7, 2015

@davetroy Thanks for response.

  1. In my original implementation I has a -controllerDidChangeContent:
    func controllerDidChangeContent(controller: NSFetchedResultsController) {
        if ignoreNextUpdates {
            ignoreNextUpdates = false
        } else {
            tableView.endUpdates()
        }
    }
  1. @objc could be a case, but I had the issue only in case I implement delegate methods as a part of private extension. I cannot verify it now for the reason we switch to xCode 6.2 and wait for official release of xCode 6.3 (we need to make beta build to iTunes)
@BrianSemiglia

This comment has been minimized.

Show comment
Hide comment
@BrianSemiglia

BrianSemiglia Apr 9, 2015

Adding @objc seemed to fix the issue for me as well. Thanks for the help. I was using Xcode 6.3, Swift 1.2.

BrianSemiglia commented Apr 9, 2015

Adding @objc seemed to fix the issue for me as well. Thanks for the help. I was using Xcode 6.3, Swift 1.2.

@andreyslyusar

This comment has been minimized.

Show comment
Hide comment
@andreyslyusar

andreyslyusar Apr 14, 2015

Confirm. Thanks a lot for help. Does anybody now the reason we need to add @objc ?

andreyslyusar commented Apr 14, 2015

Confirm. Thanks a lot for help. Does anybody now the reason we need to add @objc ?

@carlodurso

This comment has been minimized.

Show comment
Hide comment
@carlodurso

carlodurso Jun 24, 2015

It seems that this is a behavior change in Swift 1.2: methods in non-Objective-C-derived classes will no longer be implicitly marked @objc even if they match an Objective-C protocol. You can explicitly mark the methods with the @objc attribute if you don't want to extend NSObject. This is described in the Xcode 6.3 release notes at https://developer.apple.com/library/ios/releasenotes/DeveloperTools/RN-Xcode/Chapters/xc6_release_notes.html#//apple_ref/doc/uid/TP40001051-CH4-SW3.

Source: http://stackoverflow.com/questions/29536314/fetched-result-controller-delegate-not-called-after-swift-1-2-xcode-6-3-update

carlodurso commented Jun 24, 2015

It seems that this is a behavior change in Swift 1.2: methods in non-Objective-C-derived classes will no longer be implicitly marked @objc even if they match an Objective-C protocol. You can explicitly mark the methods with the @objc attribute if you don't want to extend NSObject. This is described in the Xcode 6.3 release notes at https://developer.apple.com/library/ios/releasenotes/DeveloperTools/RN-Xcode/Chapters/xc6_release_notes.html#//apple_ref/doc/uid/TP40001051-CH4-SW3.

Source: http://stackoverflow.com/questions/29536314/fetched-result-controller-delegate-not-called-after-swift-1-2-xcode-6-3-update

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment