Core Data Project?

I’m in the very beginning phase for a new application to use in my job as an over-the-road truck driver. I’m thinking of writing an app that will allow me to keep track of load assignments electronically.

Each load assignment would consist of:
¢ info specific to that load, including pick and delivery dates
¢ one or more shippers
¢ one or more receivers

¢ each shipper and receiver would have information specific to them such as:
¢ address
¢ phone
¢ direction information.

I’d like to be able to auto-complete shipper/receiver data when available (i.e. they’ve been entered before).
I’d like to be able to look up previous load assignments.

This all sounds like a database to me. So, I’m wondering if core data can be used with ASOC. I’ve never used core data before so I’m not even sure if that is the way to go but I’ve always wanted to play with it.

Any suggestions would be greatly appreciated.

Thanks in advance,
Brad Bumgarner

Core Data may well be the way to go – I don’t know enough about it to judge – but it’s not really a database (although I gather it can be used as such), and it’s not something you would particularly use ASObjC with. FWIW, the comments I see on the Cocoa mailing lists suggests it’s not a technology for Cocoa beginners – but then, that’s from people who are probably pushing it harder than you intend.

Indeed it isn’t

I’ve just recently starting using Core Data for an app. I agree that Core Data is certainly not for beginners. If you decide you want to tackle it…

I think what you’ll want to do is start out with an Obj-C project that includes using Core Data (there’s a bunch of code automatically entered in your app delegate that would be very hard for a beginner to reproduce in ASOC), then add ASOC functionality to that app. You’ll first want to make sure you can manage that (one-liner in main.m and add the ASOC framework) before really getting dirty with Core Data.

I’m doing most of my work in Obj-C now, and using ASOC for when I want to talk to outside apps (tell application…). It looks like you could probably manage most of the code from that starting point with ASOC, but you’ll have to learn how Core Data works first, and all the examples you’re going to find use Obj-C. You’ll want to be real familiar with bindings, too, as there’s some slight variations on how you manage them.

Thanks for the input everybody. It sounds like Core Data is not the way to go at this time, so I’ll consider other options.

Thanks,
Brad Bumgarner

I would be interested in hearing more opinions about core data, particularly with regard to what its advantages are compared to just storing data in an array. If you have a fairly simple data structure, say an array (100s to 1000s of records) of dictionaries ( with <10 keys) is there any advantage to using core data? Are fetch requests significantly faster than filteredArrayUsingPredicate or indexesOfObjectsPassingTest for parsing the data? Is there an advantage in memory usage of one method over the other (it seems like there are a lot of objects used in core data)? And if you want to add iCloud support, is integrating that with core data going to be especially difficult?

Ric

If you just use dictionaries (as opposed to Core Data or other database), it all has to be memory-based. That impacts things like document opening times heavily, and makes things like autosaving much more intensive operations.

FWIW, I made a very simple database app for a client recently. Only three keys, and only one of them searchable. For what they wanted, it works beautifully, but I was surprised at (a) how big the files it saves are (not massive, but bigger than I expected), and (b) how long they take to open. Neither really matters in this case, but they might in some.

This might also be useful reading /cocoawithlove.com/2010/02/differences-between-core-data-and.html. Or not…

That URL seemed broken (closed the url tag too soon)… Try http://cocoawithlove.com/2010/02/differences-between-core-data-and.html

Thanks; I’ve fixed it, hopefully.

I am in the process of rewriting my big app, backuplist+, in OBj-C. Why, I don’t know since it is working very well in ASOC but I wanted mainly to add Authentication API to it which would be impossible in purely ASOC. I read “Cocoa programming” by Aaron Hillegass which got me started and couldn’t resist the core data chapter.

It took a few days to get the idea of it but it really is great for this app which has several tableViews bound to each other and 30+ options bound in there too. It handles the whole arrangement for you and takes care of the storage which is nice. It is also lightning fast compared to the ASOC version with the loading and storing of the dictionary (applescript record) which everything is bound too. It seems to popup before you finish clicking the app!

I still don’t understand retrieving data from the core data model so well and am just getting it out of the array controllers for now which works (not sure that is kosher.)

And yes, you would not want to translate the core data stuff that is supplied automatically in the OBJ-c delegate file for the app. I would go OBJ-c with the “Core” and add ASOC in for the scripting.

Aarons’ intro though is very good.

For a small app with not a lot of data, bindings, relationships, I wouldn’t bother.

Cheers, Rob

If anyone wants to try a core data project in ASOC, I have translated the core data stack that you get in objective-c when you choose a core data project. I’ve tested it (and added log statements so it logs all the objects it creates) and it seems to work fine. You need to go to add file, and add a core data file to create the model. Then you use the data modeling tool to add your entities and attributes. The model must be created first before running the program. The name of my app is CoreDataASOCStub, so I called the store’s location “CoreDataASOCStub.sqlite”. The name of my model was “Stub”. Here is the code in the app delegate:

script AppDelegate
	property parent : class "NSObject"
	property managedObjectContext : missing value
	property managedObjectModel : missing value
	property persistentStoreCoordinator : missing value
	
	on applicationWillFinishLaunching_(aNotification)
		isManagedObjectContext()
	end applicationWillFinishLaunching_
	
	on applicationShouldTerminate_(sender)
		return current application's NSTerminateNow
	end applicationShouldTerminate_
	
	#Core Data stack
	
	--Returns the managed object context for the application.
	--If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
	on isManagedObjectContext()
		if managedObjectContext is not missing value then
			return managedObjectContext
		else
			set coordinator to self()'s persistentStoreCoordinator 
			if coordinator is not missing value then
				set managedObjectContext to current application's NSManagedObjectContext's alloc()'s init()
				managedObjectContext's setPersistentStoreCoordinator_(coordinator)
                log managedObjectContext
				return managedObjectContext
			end if
		end if
	end isManagedObjectContext
	
	
	--     Returns the managed object model for the application.
	--     If the model doesn't already exist, it is created from the application's model.
	on isManagedObjectModel()
		if managedObjectModel is not missing value then
			return managedObjectModel
		else
			set modelURL to current application's NSBundle's mainBundle()'s URLForResource_withExtension_("Stub", "momd")
			set managedObjectModel to current application's NSManagedObjectModel's alloc()'s initWithContentsOfURL_(modelURL)
            log managedObjectModel
			return managedObjectModel
		end if
	end isManagedObjectModel
	
	
	--     Returns the persistent store coordinator for the application.
	--     If the coordinator doesn't already exist, it is created and the application's store added to it.  
	on isPersistentStoreCoordinator()
		if persistentStoreCoordinator is not missing value then
			return persistentStoreCoordinator
		else
			set storeURL to applicationDocumentsDirectory()'s URLByAppendingPathComponent_("CoreDataASOCStub.sqlite")
			set persistentStoreCoordinator to current application's NSPersistentStoreCoordinator's alloc()'s initWithManagedObjectModel_(self()'s managedObjectModel)
            log persistentStoreCoordinator
			set theStore to persistentStoreCoordinator's addPersistentStoreWithType_configuration_URL_options_error_(current application's NSSQLiteStoreType, missing value, storeURL, missing value, missing value)
			if theStore is not missing value then
                log theStore
				return persistentStoreCoordinator
			else
				log "Failed to create the persistentStoreCoordinator"
				abort() --abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development
				--     Typical reasons for an error here include:
				--     * The persistent store is not accessible;
				--     * The schema for the persistent store is incompatible with current managed object model.
				--       
				--     If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory.
				--     
				--     If you encounter schema incompatibility errors during development, you can reduce their frequency by:
				--     * Simply deleting the existing store:
				--     [[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]
				--     
				--     * Performing automatic lightweight migration by passing the following dictionary as the options parameter: 
				--     [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
				--     
				--     Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.
			end if
		end if
	end isPersistentStoreCoordinator
	
	on applicationDocumentsDirectory()
		set fm to current application's NSFileManager's defaultManager()
		return fm's URLsForDirectory_inDomains_(current application's NSDocumentDirectory, current application's NSUserDomainMask)'s lastObject()
	end applicationDocumentsDirectory
	
	
end script

Ric

Thanks Ric,

That’s great. Can’t wait to convert my ASOC app version to Core Data.

Best, Rob

Hi Ric,

Well, I tried your core data “Stack” set up my model file and got crash on start:

2012-03-12 22:06:39.456 backuplist+[11213:110b] -[NSAppleEventDescriptor _setIsEditable:]: unrecognized selector sent to instance 0x4019714c0
2012-03-12 22:06:39.487 backuplist+[11213:110b] *** -[backupCAppDelegate isPersistentStoreCoordinator]: -[NSAppleEventDescriptor _setIsEditable:]: unrecognized selector sent to instance 0x4019714c0 (error -10000)
2012-03-12 22:06:39.489 backuplist+[11213:110b] *** -[backupCAppDelegate isManagedObjectContext]: Can't get persistentStoreCoordinator of «class ocid» id «data kptr00000000A06CD80004000000». (error -1728)
2012-03-12 22:06:39.495 backuplist+[11213:110b] (null)
2012-03-12 22:06:39.507 backuplist+[11213:110b] *** -[backupCAppDelegate copyWithZone:]: unrecognized selector sent to object <backupCAppDelegate @0x400d86ca0: OSAID(40)>
2012-03-12 22:06:39.507 backuplist+[11213:110b] An uncaught exception was raised

Doesn’t seem to be recognizing the MOContext. Do I need to link up those properties to something?

property managedObjectContext : missing value
property managedObjectModel : missing value
property persistentStoreCoordinator : missing value

Any thoughts appreciated. I’ll double check my dataModel file and tinker some more.

Thanks, rob

No, you don’t need to hook up those properties to anything. It’s hard to tell what might be wrong without seeing your code. But I was able to replicate your error message by miss-typing the URL that points to the managed object model, so I think there might be something wrong with your model. Try putting “log managedObjectModel” in just above the “return managedObjectModel” line in the isManagedObjectModel method and see what you get in the log.

Ric

Hi Ric,

Yes - I think I’v emixed things up. I actually used the model from my OBJ-c app which is called

backupList_DataModel.xcdatamodel

It uses the NSXMLStoreType

and I called the store “storeData”

Here’s the part of your code that I modified. Your code is different from the auto generated OBJ-c version I have. For one thing it uses the app support folder for the store…

I don’t think I have this quite right:


on isManagedObjectModel()
		if managedObjectModel is not missing value then
			return managedObjectModel
		else
			set modelURL to current application's NSBundle's mainBundle()'s URLForResource_withExtension_("backupList_DataModel", "xcdatamodel")
			set managedObjectModel to current application's NSManagedObjectModel's alloc()'s initWithContentsOfURL_(modelURL)
			log managedObjectModel
			return managedObjectModel
		end if
	end isManagedObjectModel
	
	
	-- Returns the persistent store coordinator for the application.
	-- If the coordinator doesn't already exist, it is created and the application's store added to it. 
	on isPersistentStoreCoordinator()
		if persistentStoreCoordinator is not missing value then
			return persistentStoreCoordinator
		else
			set storeURL to applicationDocumentsDirectory()'s URLByAppendingPathComponent_("storedata")
			set persistentStoreCoordinator to current application's NSPersistentStoreCoordinator's alloc()'s initWithManagedObjectModel_(self()'s managedObjectModel)
			log persistentStoreCoordinator
			set theStore to persistentStoreCoordinator's addPersistentStoreWithType_configuration_URL_options_error_(current application's NSXMLStoreType, missing value, storeURL, missing value, missing value)
			if theStore is not missing value then
				log theStore
				return persistentStoreCoordinator
			else
				log "Failed to create the persistentStoreCoordinator"
				abort() --abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development
			end if
		end if
	end isPersistentStoreCoordinator

Rob

The extension needs to be “momd” (as I had in my original post) not “xcdatamodel”. I’m not sure why that is, since the file you get from the model builder has the extension .xcdatamodel, but that’s what works. However, I found this quote on the web:

“During the build process, xcdatamodeld directories get converted to momd files, but xcdatamodel files by themselves get turned into mom files.” From other things I saw on the web, it seems that .xcdatamodel is the extension for the source file, but that gets changed to .mom or .momd in the compiled file, and that’s what you need to use in the URLForResource_withExtension_ method.

My model file has an .xcdatamodeld extension, and I used .momd as the extension in the above method and that worked. So, if your file has an xcdatamodel extension (without the final “d”) you might try just “.mom” if “.momd” doesn’t work for you.

Ric

mom saved the day! I also had an error in my arrayController binding which I fixed.

set modelURL to current application's NSBundle's mainBundle()'sURLForResource_withExtension_("backupList_DataModel", "mom")

It works!

One thing I am not sure about are the methods for

- (IBAction) saveAction:(id)sender {

    NSError *error = nil;
    
    if (![[self managedObjectContext] commitEditing]) {
        NSLog(@"%@:%s unable to commit editing before saving", [self class], _cmd);
    }

    if (![[self managedObjectContext] save:&error]) {
        [[NSApplication sharedApplication] presentError:error];
    }
}

but it looks like these are just error reporting if the MOC can’t be saved before quitting. And I suppose I could include this too.

thanks Ric. This is great because I can now continue with my main ASOC app and include core data rather than rewriting everything in OBJ-c. I have two tableViews bound to the main array controller which has about 40+ bound attributes. A big spiderweb but it is easy for coreData and it is way more responsive and the app launches instantly.

Cheers, Rob

I use the following way to check for an error on saving:

on applicationShouldTerminate_(sender)
        set {theBool,theError} to managedObjectContext's save_(reference)
        if theBool as boolean is false then current application's NSApp's presentError_(theError)
		return current application's NSTerminateNow
	end applicationShouldTerminate_

I’ve tested this by changing the name of the store location after launching the app, and I got the error alert.

Ric

I am close, but not quite there and would appreciate any help you could give me.

I have included the log as well as a copy of MY version of the code.

----- here is the contents of the activity log -------

objc[7392]: Object 0x10011dc00 of class NSPathStore2 autoreleased with no pool in place - just leaking - break on objc_autoreleaseNoPool() to debug
objc[7392]: Object 0x10011e020 of class NSPathStore2 autoreleased with no pool in place - just leaking - break on objc_autoreleaseNoPool() to debug
objc[7392]: Object 0x10030b070 of class __NSArrayM autoreleased with no pool in place - just leaking - break on
objc_autoreleaseNoPool() to debug
objc[7392]: Object 0x10030b930 of class __NSCFString autoreleased with no pool in place - just leaking - break on objc_autoreleaseNoPool() to debug
2012-08-21 16:50:15.510 USG Portfolio Metadata Tagger A 1.0[7392:403] -----storeURL-----
2012-08-21 16:50:15.513 USG Portfolio Metadata Tagger A 1.0[7392:403] file://localhost/Users/jhaney/Documents/USGPortfolioMetaTaggerData.momd
2012-08-21 16:50:15.518 USG Portfolio Metadata Tagger A 1.0[7392:403] -----modelURL-----
2012-08-21 16:50:15.523 USG Portfolio Metadata Tagger A 1.0[7392:403] (null)
2012-08-21 16:50:15.525 USG Portfolio Metadata Tagger A 1.0[7392:403] ------mom-----
2012-08-21 16:50:15.527 USG Portfolio Metadata Tagger A 1.0[7392:403] (null)
2012-08-21 16:50:15.530 USG Portfolio Metadata Tagger A 1.0[7392:403] -[NSAppleEventDescriptor _setIsEditable:]: unrecognized selector sent to instance 0x100162a70
2012-08-21 16:50:15.541 USG Portfolio Metadata Tagger A 1.0[7392:403] *** -[LCCAppDelegate isPersistentStoreCoordinator]: -[NSAppleEventDescriptor _setIsEditable:]: unrecognized selector sent to instance 0x100162a70 (error -10000)
2012-08-21 16:50:15.598 USG Portfolio Metadata Tagger A 1.0[7392:403] *** -[LCCAppDelegate applicationWillFinishLaunching:]: Can’t get persistentStoreCoordinator of «class ocid» id «data kptr0000000090AC1B0001000000». (error -1728)
2012-08-21 16:50:15.640 USG Portfolio Metadata Tagger A 1.0[7392:403] Cannot perform operation without a managed object context

 #Core Data stack
    -----------------------
    --Returns the managed object context for the application.
    --If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
    on isManagedObjectContext()
        if managedObjectContext is not missing value then
            return managedObjectContext
            else
            set coordinator to self()'s persistentStoreCoordinator
            if coordinator is not missing value then
                set managedObjectContext to current application's NSManagedObjectContext's alloc()'s init()
                managedObjectContext's setPersistentStoreCoordinator_(coordinator)
                log managedObjectContext
                return managedObjectContext
            end if
        end if
    end isManagedObjectContext
    
    
    -- Returns the managed object model for the application.
    -- If the model doesn't already exist, it is created from the application's model.
    on isManagedObjectModel()
        if managedObjectModel is not missing value then
            return managedObjectModel
            else
            set modelURL to current application's NSBundle's mainBundle()'s URLForResource_withExtension_("TreeViewDataModel", "mom")
            log "-----modelURL-----"
            log modelURL
            set managedObjectModel to current application's NSManagedObjectModel's alloc()'s initWithContentsOfURL_(modelURL)
            log "------mom-----"
            log managedObjectModel
            return managedObjectModel
        end if
    end isManagedObjectModel
    
    
    -- Returns the persistent store coordinator for the application.
    -- If the coordinator doesn't already exist, it is created and the application's store added to it.
    on isPersistentStoreCoordinator()
        if persistentStoreCoordinator is not missing value then
            return persistentStoreCoordinator
            else
            set storeURL to applicationDocumentsDirectory()'s URLByAppendingPathComponent_("USGPortfolioMetaTaggerData.momd")
            log "-----storeURL-----"
            log storeURL
            set persistentStoreCoordinator to current application's NSPersistentStoreCoordinator's alloc()'s initWithManagedObjectModel_(self()'s managedObjectModel)
            log "-----PSC-----"
            log persistentStoreCoordinator
            set theStore to persistentStoreCoordinator's addPersistentStoreWithType_configuration_URL_options_error_(current application's NSXMLStoreType, missing value, storeURL, missing value, missing value)
            if theStore is not missing value then
                log "-----theStore-----"
                log theStore
                return persistentStoreCoordinator
            else
                log "Failed to create the persistentStoreCoordinator"
                abort() --abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development
                -- Typical reasons for an error here include:
                -- * The persistent store is not accessible;
                -- * The schema for the persistent store is incompatible with current managed object model.
                --
                -- If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory.
                --
                -- If you encounter schema incompatibility errors during development, you can reduce their frequency by:
                -- * Simply deleting the existing store:
                -- [[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]
                --
                -- * Performing automatic lightweight migration by passing the following dictionary as the options parameter:
                -- [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
                --
                -- Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.
            end if
        end if
    end isPersistentStoreCoordinator
    
    on applicationDocumentsDirectory()
        set fm to current application's NSFileManager's defaultManager()
        return fm's URLsForDirectory_inDomains_(current application's NSDocumentDirectory, current application's NSUserDomainMask)'s lastObject()
    end applicationDocumentsDirectory