Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Core Data Problems?

I am trying to use CoreData but I already went through the setup for my project and forgot to check off the box to utilize it.

Is there a way to implement the use of core data when the CoreData box was not checked previously during the setup?

If I start a new project will have to transfer a lot of information and it would be time consuming so I would like to stay on the same project and not create a new one.

like image 532
Bigfoot11 Avatar asked Mar 17 '23 17:03

Bigfoot11


2 Answers

To be frank, you did the right think by not checking the Use CoreData box on project creation. I feel that just bloats the project with a bunch of stuff that is easier (and more insightful) to do manually.

To be short, you can implement CoreData the same regardless of what option you selected at project creation.

Here are the steps I usually go through when I want to add CoreData support to my project (manually/programatically):

Define a Data Model

These are just NSManagedObjects which represent your application's data structure. For example, a User, Message, BlogPost, etc. I also make one for my user settings.

Example:

import CoreData

class User : NSManagedObject
{
    // @NSManaged is the replacement for @dynamic when using CoreData in Swift

    @NSManaged var identifier : String
    @NSManaged var firstName : String?
    @NSManaged var lastName : String?

    // This is called when a new User object is inserted to CoreData
    override func awakeFromInsert()
    {
        super.awakeFromInsert()
        self.identifier = NSUUID().UUIDString // generate a random unique ID
    }
}

Add Core Data Model

This is another file you add to your project via: File -> New-> iOS-> CoreData -> Data Model. I usually store this same xcmodeldata file in my Models project folder (along with my actual model classes).

Upon selecting this new file, you'll see the CoreData model editor. You will want to see the right-hand side inspector pane is visible (hotkey is ⌥⌘1). For the core data editor, you will also primarily use the third tab (data model inspector) which is switchable with ⌥⌘3.

Now you can add an entity object to this data model (via Add Entity at the bottom). Assuming the example above, add a User entity. With the User entity selected, add the three attributes that are defined in the above class: identifier, firstName, and lastName. They should match the class definition, using String types.

Next step is to tell CoreData that this User entity defined here maps to our actual class file. With the User selected and the data model inspector pane open, set the Name to User and Class to YourAppName.User.

This is the "gotcha" with Swift and CoreData, your classes are prefixed with the module name in order to namespace them (avoiding name collisions). The nice part is that you no longer need to add "XYZ" class prefixes to your objects.

Initialize Core Data Stack

With your data model defined, you need to initialize the CoreData stack itself (database store and context). The most basic example is a global singleton for your NSManagedObjectContext, which will be lazy-loaded when needed.

You can put this in its own Swift file (CoreDataStack.swift):

import CoreData

let managedObjectContext : NSManagedObjectContext =
{
    // This is your xcdatamodeld file
    let modelURL = NSBundle.mainBundle().URLForResource("MyApp", withExtension: "momd")
    let dataModel = NSManagedObjectModel(contentsOfURL: modelURL!)

    // This is where you are storing your SQLite database file
    let documentsDirectory : NSURL! = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).last as? NSURL
    let storeURL = documentsDirectory.URLByAppendingPathComponent("MyApp.sqlite")

    let psc = NSPersistentStoreCoordinator(managedObjectModel: dataModel!)

    var error : NSError?
    let store = psc.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil, error: &error)

    if let error = error
    {
        println("Uhoh, something happened! \(error), \(error.userInfo)")
    }

    let context = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)

    context.persistentStoreCoordinator = psc
    context.undoManager = nil

    return context
}()

Using Core Data

So now that you have a working Core Data stack, some data models defined and mapped... what next?

Let's fetch some objects!

func getUsersByFirstName(firstName: String) -> [User]
{
    let fetchRequest = NSFetchRequest(entityName: "User")

    // The [c] part indicates case-insensitive, "Bob" == "bob"
    fetchRequest.predicate = NSPredicate(format: "firstName ==[c] %@", firstName)

    var error : NSError?
    let results = context.executeFetchRequest(fetchRequest, error: &error) as [User]

    // Handle errors here

    return results
}

Oh right, we have nothing to fetch. You can also insert objects...

func insertNewUser() -> User
{
    return NSEntityDescription.insertNewObjectForEntityForName("User", inManagedObjectContext: context) as User
}

And of course you can delete objects...

func deleteUser(user: User)
{
    context.deleteObject(user)
}

The key is to remember that CoreData contexts (NSManagedObjectContext) keep track of changes in memory. While you can perform these CRUD operations on a context and see the changes instantly (within the same context), they will not persist in the database until you save the changes:

func saveContext() -> Bool
{
    var error : NSError?

    if context.hasChanges && !context.save(&error)
    {
        println("Something happened when saving! \(error!), \(error!.userInfo)")
        return false
    }

    return true
}

You can also rollback changes from the last save by using context.rollback().

Feel free to explore CoreData and experiment with the more advanced features like predicates (NSPredicate), sort descriptors (NSSortDescriptor), and setting up object relationships.

like image 91
Erik Avatar answered Mar 28 '23 17:03

Erik


Basically all the tick box for Core Data does is add the core data framework (CoreData.framework) to your project and setup your AppDelegate.m with the core data stack, add in a data file and possibly give you a sample view controller (depending on which project type you start with).

If you want your existing project to be setup like the template would set you up, then the quickest way is to just create a new project as an example and select Core Data tick box. Open the new project and review the AppDelegate.m file and grab the code for initializing core data stack. It's about 80 lines and has a comment calling out the Core Data Stack.

Take that over to your existing project and drop it in to your AppDelegate file. Also in your existing project, add the CoreData.framework, then add in a new file (File->New File->CoreData), under Core Data called a "Data Model" file. This file is used to define the equivalent of your data schema. You select it to use the graphical controls.

Then use your sample project to review how you access the core data stack by reviewing the sample ViewController.

Note: some people are not fond of how Apple sets up the Core Data stack in the AppDelegate.m and you'll find many comments about it and how to do it better, if you search for it (I feel compelled to make this disclaimer). There are also some 3rd party libraries on GitHub that can assist you in that regard as well. (MagicalRecord,SLCoreDataStack, etc).

hope that helps! be well!

like image 42
CocoaEv Avatar answered Mar 28 '23 16:03

CocoaEv