If you are like me then you followed all the core data tutorials and you created your cocoa applications without any problem. But very quickly into creating your own application, you start to move outside the confines of the tutorials and need to get into the code and figure out what to do. This is normally a struggling point for anyone learning a new language, but when you throw in the fact that to tutorials, guides and documentation are in a separate language from the one your are writing in your task become harder. This is how I felt when I tried to retrieve core data objects through code in my brand new RubyCocoa application.
There are plenty of tutorials out there for core data and even some for RubyCocoa, but in my experience most of these tutorials show you how to interface with your object model using bindings. Most of the time this is the way I would prefer to handle my data interactions. But then again sometimes you just have to dive into the code and get your hands dirty. Below is a little snippet of code I wrote to retrieve a list of "Projects" from my core data model. After the code, I will go through and point out a coupe of spots that caused me a little grief in hopes of sharing my knowledge. Of course this posting assumes that you have read the CoreData Programming Guide from apple and are familiar with the concepts of the core data framework.
The Code: # Create Fetch Request # Create Predicate (where clause) # Add Sorting rows = managedObjectContext.executeFetchRequest_error(request)
managedObjectContext = self.managedObjectContext()
entity = OSX::NSEntityDescription.entityForName_inManagedObjectContext("Project",managedObjectContext)
request = OSX::NSFetchRequest.alloc.init
request.entity = entity
predicate = OSX::NSPredicate.predicateWithFormat("(isActive = true)")
request.predicate = predicate
sort = OSX::NSSortDescriptor.alloc.initWithKey_ascending("name",true)
request.sortDescriptors = [sort]
rows.each { | row |
case (row.class.to_s())
when /NSError/i
OSX::NSLog("Error Retriving Data: %@", row)
break;
when /Array/i
row.each { | dataRow |
OSX::NSLog("Item: %@ Class: %@", dataRow, dataRow.class)
... your code here ...
}
end
}
The Breakdown:
These lines of code establish the link to the managedObjectContext and create an entity description for the model you would like to retrieve. Later we will use this description in our fetch request, but for now this just creates the pointer.
managedObjectContext = self.managedObjectContext()
entity = OSX::NSEntityDescription.entityForName_inManagedObjectContext("Project",managedObjectContext)
Next we create our fetch request and assign our entity description to the fetch request. One of the things that had me stumped for alittle whlie is the "alloc.init" part of the code. In most of the RubyCocoa tutorials I have scene, I have not run accross this construct, but for my core data programming this is required to create and initialize the cocoa objects.
request = OSX::NSFetchRequest.alloc.init
request.entity = entity
Now that our fetch request has been created and our entity is assigned, we can add a predicate to our request to limit our results. In this examples I was looking for all projects that have the attribute "isActive" set to true. Predicate programming is similar to SQL where clauses, but there are subtle differences, so make sure to check the Predicate Programming Guide if you are having problems.
predicate = OSX::NSPredicate.predicateWithFormat("(isActive = true)")
request.predicate = predicate
Lastly ever request must have a sort description added to our request, even if this is just a blank array. Unlike our predicate example above the sortDescriptors property is added as an array. You will notice below I create my sort description object and then just add it to an array while setting the descriptor on the request object. Not setting a sort descriptor will cause a runtime error, so if you don't need one just set this request property to a blank array. Also pay attention to the "alloc.init" syntax again. In this case we want to sort our data by the projects name.
sort = OSX::NSSortDescriptor.alloc.initWithKey_ascending("name",true)
request.sortDescriptors = [sort]
Now that we have created the fetch request, it is time to execute our fetch and return our rows. Once we have our rows, we have to loop through the results to retrieve the list of objects. The object returned is an array of objects. I first expected this to be an array of data but quickly found out that it can be an error or an array of data. The case clause helps figure out if the request worked correctly. Within the case statemnt you will notice that I using a regular expression to check the object type. I found this to be the easiest way for myself to parse the results, but feel freel to change this to the style that most suites you. Finally once we have our data array, it is time to loop through the objects and do what we need to with them.
rows = managedObjectContext.executeFetchRequest_error(request)
rows.each { | row |
case (row.class.to_s())
when /NSError/i
OSX::NSLog("Error Retriving Data: %@", row)
break;
when /Array/i
row.each { | dataRow |
OSX::NSLog("Item: %@ Class: %@", dataRow, dataRow.class)
... your code here ...
}
end
}
I hope this example helps you out with your RubyCocoa project. I will be posting other quick examples when I find something that has caused me a little bit of a headache, so check back soon. My next post will be about a custom formater class that I created for an NSTableView cell.
I'm trying this in my (pretty much brand new, very little code other than that described here) non-document-based RubyCocoa Core Data app, and I am getting EXC_BAD_ACCESS errors when I try to run `request.setEntity(entity)`... and EXC_BAD_INSTRUCTION when I try to run `OSX::NSFetchRequest.alloc.init.autorelease`.
I'm new to the world of Cocoa, these trace-less errors that don't look at all Ruby-ish are scaring me.
Posted by: elliottcable | September 07, 2008 at 06:22 AM
My bad, I was partially following your tutorial and partially following an ObjC one. Apparently `autorelease` isn't necessary here. My bad! (-:
Posted by: elliottcable | September 07, 2008 at 07:20 PM