0

Hi just wondering if this is possible, i have a simple counter app where a counter variable is incremented by 1 every time a button is pushed. is it possible to save the counter when the user exits the app using core data? i know NSUserdefaults would work here but im exploring core data and was wondering if it could be used in cases like this

class ViewController: UIViewController {

var counter = Int()


@IBOutlet var label: UILabel!

@IBAction func button(sender: AnyObject) {
    counter += 1
    label.text = "\(counter)"



}
override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view, typically from a nib.
}
}

NSManagedObject subclass Error

class IntegerEntity: NSManagedObject {


convenience init(entity: NSEntityDescription, insertIntoManagedObjectContext context: NSManagedObjectContext?, value: Int) {
    self.init(entity: entity, insertIntoManagedObjectContext: context)
    self.value
    // ERROR value of type IntegerEntity has no member value
}
}

ViewController.swift errors

import UIKit
import CoreData

class ViewController: UIViewController {

var counter = Int()


@IBOutlet var label: UILabel!

@IBAction func button(sender: AnyObject) {
    counter += 1
    label.text = "\(counter)"



}
override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view, typically from a nib.
}

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

    let dataContext: NSManagedObjectContext! = (UIApplication.sharedApplication().delegate as? AppDelegate)?.managedObjectContext
    var integerEntity: IntegerEntity!
    if dataContext != nil {
        let entity = NSEntityDescription.entityForName("IntegerEntity", inManagedObjectContext: dataContext)
        let request = NSFetchRequest()
        request.entity = entity
        let integer = try? dataContext.executeFetchRequest(request)

        if integer != nil && !integer!.isEmpty {
            (integer?.first! as! IntegerEntity).value = counter
            // ERROR value of type IntegerEntity has no member value
        } else {

            let newInt = IntegerEntity(entity: entity, insertIntoManagedObjectContext: context, value: counter)
            // ERROR Use of unresolved identifier context

            dataContext.saveData()
        }
    }
}

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    let dataContext: NSManagedObjectContext! = (UIApplication.sharedApplication().delegate as? AppDelegate)?.managedObjectContext
    var integerEntity: IntegerEntity!
    if dataContext != nil {
        let entity = NSEntityDescription.entityForName("IntegerEntity", inManagedObjectContext: dataContext)
        let request = NSFetchRequest()
        request.entity = entity
        let integer = try? dataContext.executeFetchRequest(request)

        if integer != nil && !integer!.isEmpty {
            counter = (integer!.first! as! IntegerEntity).value
            // ERROR value of type IntegerEntity has no member value
        } else {

            counter = 0
        }
    }


}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}


}
extension NSManagedObjectContext {
func saveData() -> Bool {
    do {
        try self.save()
        return true
    } catch let error as NSError {
        print(error)
        return false;
    }
}
}
2
  • Yes, of course. However, it's a very bad match for the task. It's like starting up a 747 to go across the street to the store for a bottle of milk. Core Data is very powerful, but has a steep learning curve and a fair amount of overhead. Using it to store a single integer is silly. Commented May 21, 2016 at 0:11
  • Use do - catch and handle errors properly. On success you get a guaranteed non-optional array. And the NSManagedObjectContext object is supposed to be non-optional, too. And how can AppDelegate ever be nil?? If there is no application delegate the app won't launch at all. Commented May 22, 2016 at 12:43

1 Answer 1

1

This is possible.

First, create a Core Data model and add an entity called IntegerEntity. In the entity, add a property called value whose type is Integer 64. Now generate the NSManagedObject subclass.

In IntegerEntity,swift, add the following initializer:

convenience init(entity: NSEntityDescription, insertIntoManagedObjectContext context: NSManagedObjectContext?, value: Int) {
    self.init(entity: entity, insertIntoManagedObjectContext: context)
    self.value = value
}

This is because later on, we can use this directly to create an entity with a value.

I suppose your app has only one view controller, so you have two options of when to save the data:

  • Use an NSTimer to save the data every 60 seconds or some other time interval
  • override viewDidDisappear in the view controller or applicationWillResignActive in the app delegate.

To save the data, you first need to get the saved integer back (if present).

// Just some boilerplate code here
let dataContext: NSManagedObjectContext! = (UIApplication.sharedApplication().delegate as? AppDelegate)?.managedObjectContext
var integerEntity: IntegerEntity!
if dataContext != nil {
    let entity = NSEntityDescription.entityForName("IntegerEntity", inManagedObjectContext: dataContext)
    let request = NSFetchRequest()
    request.entity = entity
    let integer = try? dataContext.executeFetchRequest(request)
    // If some integer has already been saved...
    if integer != nil && !integer.isEmpty {
        (integer?.first! as! IntegerEntity).value = counter
    } else {
        // If no integer has been saved before...
        let newInt = IntegerEntity(entity: entity, insertIntoManagedObjectContext: context, value: counter)
        dataContext.saveData()
    }
}

You may be wondering, what's saveData? I think my version of Xcode has a bug. Xcode always insists that save() method throws an error and returns Void. So I created this extension:

extension NSManagedObjectContext {
    func saveData() -> Bool {
        do {
            try self.save()
            return true
        } catch let error as NSError {
            print(error)
            return false;
        }
    }
}

And BOOM! that's it!

"But how do I fetch the data back?" you asked. Actually, you did it before! Here's the code:

let dataContext: NSManagedObjectContext! = (UIApplication.sharedApplication().delegate as? AppDelegate)?.managedObjectContext
    var integerEntity: IntegerEntity!
    if dataContext != nil {
        let entity = NSEntityDescription.entityForName("IntegerEntity", inManagedObjectContext: dataContext)
        let request = NSFetchRequest()
        request.entity = entity
        let integer = try? dataContext.executeFetchRequest(request)
        // If some integer has already been saved...
        if integer != nil && !integer.isEmpty {
            counter = (integer!.first! as! IntegerEntity).value.integerValue
        } else {
            // If no integer has been saved before...
            counter = 0
        }
    }

Looks familiar? It's almost the same code as before!

WARNING

IMO, it's not worth it to write so much code just to save one single integer, just so you know. Use user defaults instead. If you want to explore Core Data, you can try saving lists of words in Core Data. And with the press of a button , AVSpeechSynthesizer read them all aloud.

Sign up to request clarification or add additional context in comments.

5 Comments

Hi thanks for the awesome response, ive got some errors implementing your code though. ive edited my question with the errors using comments marked with //ERROR. sorry im pretty fresh with swift so bear with me :)
Did you generate IntegerEntity.swift or did you just created one using File -> New -> New File? You should not do that. First delete the file you created yourself. Then open your data model, then click Editor -> Generate NSManagedObject Subclass... And then it should be OK. @MattWyeth
@MattWyeth I mean did you generate the NSManagedObject subclass using Editor -> Generate NSManagedObject subclass or did you just create the files manually? If you created the files manually, you should delete it and use the correct method as I have said before.
Ok thanks those errors are gone, now I get a SIGABRT error when launching the app. It appears at the lazy var persistentStoreCoordinator in the app delegate , when abort() is executed?
It looks like that you need to do some Core Data migration. Just search Core Data lightweight migration on Google and you'll find how to do this. @MattWyeth

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.