Friday, August 1, 2008

Checking with CoreData (2)


I've continued development of the Checkbook application using CoreData. The major advantage of CoreData is its heavy duty database prowess, as the name suggests. For me, it looks promising because it takes care of Undo, which I found quite awkward to implement (I haven't actually done it for a real app). It also takes care of archiving, but for a checking account that would be trivial using a text file.

I ran into one major surprise, but the fix turned out to be not so bad. Because it is a db thingie, CoreData does not keep the data in a fixed order. So, when you save and reload, the order of objects is virtually guaranteed to be different. In fact, it's different every time you load the same data. It's amusing that Apple didn't find a reason to tell me this before page 188 of the CoreData programming guide. Anyway, since we definitely want to keep our transactions in order, it is easily solved by adding an item number to each Transaction (and could be solved by using this NSDate value). Whenever I fetch the objects, I sort them, like so:

def getSortedObjects(NSApp):
def f(o): return o.item()
Doc = NSApp.mainWindow().delegate()
AC = Doc.myArrayController
objects = list(AC.arrangedObjects())
if not objects: return None
objects = sorted(objects, key=f)
return objects

Another issue is responding to changes in the table view. Some delegate methods no longer work, including:
tableView_didClickTableColumn_
tableView_shouldEditTableColumn_row_

although this does:


tableViewSelectionDidChange_

One thing that was fun was to write a value transformer that makes the entry text for checks colored red:

class ColorTransformer(NSValueTransformer):
@classmethod
def transformedValueClass(cls): return NSColor

@classmethod
def allowsReverseTransformation(cls): return False

def init(self):
self = super(ColorTransformer,self).init()
return self

# boolean to color
def transformedValue_(self,value):
if not value:
return NSColor.blackColor()
else:
return NSColor.redColor()

You just have to set the name for it in the Document class:
self.t = MyTransformers.ColorTransformer.alloc().init()
NSValueTransformer.setValueTransformer_forName_(
self.t, u'ColorTransformer')

And in the end, I opted out of all the NSDecimalNumber stuff. I'm using utility functions that transform a decimal number to a string (and back again), and I cache both values in the Transaction entity. I talked about that here.