Blog

Browsing Core Data databases using F-Script

F-Script, the Cocoa-based scripting environment, now provides some great tools for exploring Core Data databases.

I couldn’t figure out how to easily open up my databases, other than manually creating a managed object model, then a persistent store coordinator, then a managed object context on the console. I couldn’t find any existing tools, and I wanted a quick workflow for opening up my databases, so I put together a script that prompts for the application bundle or .xcdatamodel(d) data model file, then prompts for the XML (.xml), binary (.binary) or SQLite (.sql or anything else) database file, and opens up the inspector.

I wrote it as an Applescript that just calls upon F-Script to evaluate the script, and saved it in an application bundle so I can pull it up quickly.

Here it is:

Core Data Browser.app

201004101417.jpg

It just needs the F-Script app to be available.

Upon opening, the managed object context is available on the console as “context“. So, aside from using F-Script’s object browser, you can also do things like:

> request := (NSFetchRequest alloc) init
> request setEntity:(NSEntityDescription entityForName:'MyEntity' inManagedObjectContext:context)
> request setPredicate:(NSPredicate predicateWithFormat:'type = 3')
> result := context executeFetchRequest:request error:nil
> result
_PFArray {<NSManagedObject: 0x2006cf740> (entity: MyEntity; id: 0x20064c9e0 <x-coredata://BAC82A67-CC41-48C2-8A96-693B67A501D6/MyEntity/p1> ; data: <fault>), 
<NSManagedObject: 0x2006bdc80> (entity: MyEntity; id: 0x20064c9c0 <x-coredata://BAC82A67-CC41-48C2-8A96-693B67A501D6/MyEntity/p2> ; data: <fault>), 
<NSManagedObject: 0x2006bc680> (entity: MyEntity; id: 0x200651180 <x-coredata://BAC82A67-CC41-48C2-8A96-693B67A501D6/MyEntity/p3> ; data: <fault>)
...

Update: Now has better error reporting, and the option to load classes from a bundle.

For those interested, here’s the original F-Script:

panel := NSOpenPanel openPanel.
panel setAllowedFileTypes:{'xcdatamodel', 'app', 'framework'};setTitle:'Data model';setMessage:'Select a data model file or bundle containing a data model'.
classesButton := (NSButton alloc) initWithFrame:(0<>0 extent:200<>30).
classesButton setTitle:'Load classes in bundle';setButtonType:NSSwitchButton.
panel setAccessoryView:classesButton.
model := nil.
[model == nil] whileTrue:[
	error := FSPointer objectPointer.
	(panel runModal = NSFileHandlingPanelCancelButton) ifTrue:[ (NSException exceptionWithName:'CancelException' reason:'Cancelled' userInfo:nil) raise ] .
	(((panel URL) absoluteString) hasSuffix:'xcdatamodel') ifTrue:[
		model := ((NSManagedObjectModel alloc) initWithContentsOfURL:(panel URL))
	] ifFalse:[
		bundle := (NSBundle bundleWithURL:(panel URL)).
		(bundle == nil) ifFalse:[
			model := (NSManagedObjectModel mergedModelFromBundles:{bundle}).
			(model ~~ nil & (FSBoolean booleanWithBool:(classesButton state) = NSOnState)) ifTrue:[
				(FSBoolean booleanWithBool:(bundle loadAndReturnError:error)) ifFalse:[
					model := nil.
					(NSAlert alertWithMessageText:'Could not load bundle' defaultButton:'OK' alternateButton:nil otherButton:nil informativeTextWithFormat:((error at:0) localizedDescription)) runModal
				]
			]
		]
	].
	(model == nil & ((error at:0) == nil)) ifTrue:[ (NSAlert alertWithMessageText:'Could not load model from file or bundle' defaultButton:'OK' alternateButton:nil otherButton:nil informativeTextWithFormat:'') runModal ]
].
panel setAccessoryView:nil.
store := ((NSPersistentStoreCoordinator alloc) initWithManagedObjectModel:model).
panel setAllowedFileTypes:nil; setTitle:'Database file';setMessage:'Select Core Data SQLite database'.
opened := nil.
[opened == nil] whileTrue:[
	(panel runModal = NSFileHandlingPanelCancelButton) ifTrue:[ (NSException exceptionWithName:'CancelException' reason:'Cancelled' userInfo:nil) raise ] .
	type := NSSQLiteStoreType.
	((((panel URL) absoluteString) lowercaseString) hasSuffix:'xml') ifTrue:[type := NSXMLStoreType].
	((((panel URL) absoluteString) lowercaseString) hasSuffix:'binary') ifTrue:[type := NSBinaryStoreType].
	error := FSPointer objectPointer.
	opened := (store addPersistentStoreWithType:type configuration:nil URL:(panel URL) options:nil error:error).
	(opened == nil) ifTrue:[ (NSAlert alertWithMessageText:'Could not load database' defaultButton:'OK' alternateButton:nil otherButton:nil informativeTextWithFormat:((error at:0) localizedDescription)) runModal ]
].
context := ((NSManagedObjectContext alloc) init).
context setPersistentStoreCoordinator:store.
context inspectWithSystem:sys
Tagged , , , , . Bookmark the permalink. Both comments and trackbacks are currently closed.

12 Comments

  1. Posted November 16, 2010 at 5:12 pm | Permalink

    This is totally awesome. It will help my testing immensely. Thanks for your contribution!!

  2. Randy Mennie
    Posted January 10, 2011 at 7:40 am | Permalink

    I tried running your app against several xcdatamodel and sqlite database files and it always says that the filename is invalid. Very frustrating. Lots of swearing. Not impressed.

    Any ideas?

    Thanks

    • Posted January 10, 2011 at 9:39 am | Permalink

      None, I’m afraid! Have you tried doing it the old fashioned way, in F-Script? (you can use the code above as a guide)

      • Posted January 10, 2011 at 3:14 pm | Permalink

        Works great for me on multiple projects. Considering it is, you know, FREE I would stop hating on someone who helps out the dev community.

    • Posted January 10, 2011 at 3:15 pm | Permalink

      Sorry for the confusion, my comment was directed at Randy.

  3. Bzouchir
    Posted August 4, 2011 at 2:55 am | Permalink

    This is brilliant thanks! how would you write a predicate to check dates? Say for User.createdDate

    how can I write the predicate to fetch: createDate < [NSDate date]

    Thanks

    • Posted August 4, 2011 at 10:09 am | Permalink

      Cheers =)

      Try something like:

      request setPredicate:(NSPredicate predicateWithFormat:'createDate < %@', (NSDate date))
  4. Matthew Marshall
    Posted November 10, 2011 at 10:24 am | Permalink

    Can I suggest that you also allow the latest file type of “xcdatamodeld” too, just as a very minor tweak! :)

  5. Posted December 16, 2011 at 12:03 am | Permalink

    I still get a message from FScript 2.x saying “Could not load model from file or bundle”. This is from a model created with Xcode 4.2.

  6. Jose Franca
    Posted September 15, 2012 at 2:48 am | Permalink

    I’m getting same message as Mark. Any ideas?

  7. Posted September 5, 2013 at 2:19 pm | Permalink

    If you like this you may also like the GitHub project CoreDataPro. You can find it here: https://github.com/yepher/CoreDataUtility

    CoreDataPro lets you view explore your core data data model and view data that your application has stored.