I’ve been poking at Cocoa development for quite a while now. That said, I haven’t finished anything I’ve started as of yet. I start a lot of things and then move to something else. Stacy thinks I have a problem. To that end, I’ve started a small application for Stacy. Nothing special, it’s just designed to do simple tracking of invoices and payments for her freelance business.
Since I’ve actually got someone who wants to use the application I’ve actually got a reason to finish it. I’ve imported the initial application, Reckoning, into GitHub for all the world to marvel over my ability to click icons in Xcode and Interface Builder.
Since I’m working on the application I may as well write about it a bit as well. Hm, I think I mentioned something about not being able to do just one thing?
With that in mind, let’s begin.
As mentioned, Reckoning is a simple application to track invoices and payments. There are four main items that will be tracked: Clients, Invoices, Line Items and Payments. To get this party started fire up Xcode and create a new Core Data Application. (This maybe better as a Core Data Document based Application but I picked Core Data Application.)
I got started by developing the data model (well, developing the data model
after reformatting the default code to match my personal style). Double click on
Reckoning_DataModel.xcdatamodel file to launch the data modeling tool.
Before we get into the data model tool I guess I should tell you a bit more about my data. The system will have the following structure:
- name :: string (default: New Client)
- invoice_period :: integer (default: 30)
- invoice_notes :: string
- contact_name :: string
- contact_email :: string
- address :: string
- city :: string (default: Toronto)
- province :: string (default: Ontario)
- postal_code :: string
- country :: string (default: Canada)
invoices :: to-many relationship to the Invoice table
- date :: date (default: “today”) [note the quotes matter for the date fields]
- due_date :: date (default: “today”)
- notes :: string
- taxes_saved :: float
- total_invoiced :: float
- total_received :: float
- client :: relationship to the Client table
- line_items :: to-many relationship to the LineItem table
payments :: to-many relationship to the Payment table
- Line Items
- date :: date (default “today”)
- description :: string
- hours_worked :: float
- rate :: float
invoice :: relationship to the Invoice table
- amount :: float
- date :: date (default: “today”)
- invoice :: relationship to the Invoice table
About the only item not mentioned in the above is the regular expression used to
validate the email address. After a bit of Googling I found a
great resource on matching email
addresses and entered
contact_email regular expression.
Simple enough. Although, writing this I realized I forgot the
field in the
Client table. Oh well, something to do tomorrow.
The next step in our application is to start the user interface. For the first revision I’ve created the ability to add clients, invoices and payments. You can’t edit many of the details yet but the basic creation is there. (I’ll actually have to write some code to get that part done.)
Closing the data modeler tool we can now click on the
MainMenu.xib (or .nib
if you’re using an earlier version of Xcode) to launch Interface Builder.
Currently everything happens in the main Reckoning window. I’ll need to add sheets and property dialogs and things as time goes on but for now, one window.
The three fields in the Income section are set to non-editable text fields
with a number formatter attached. All three number formatters are set to the
currency style. (These three boxes do nothing in the current version.) The
Due Date and
Date columns have a date formatter attached. The
column has a
currency formatter similar to the Income fields.
About the only other thing that needs explanation is the 7 outstanding invoices. This line will dynamically update in the final version of the app to give information on the outstanding and overdue invoices for each client. I’m also hoping to be able to colour the table cell text differently depending on the invoice status.
Edit buttons are currently unused. The
- buttons are used
to add and remove items from their section.
Ok, lets see how much of this thing we can hookup without diving into any code.
First up, we need some controllers. We have three Core Data tables we’re going
to want to pull data from for this first iteration. To that end we’ll drag three
array controllers from the library down to the information window. Set the name
of one to
Client Controller, one to
Invoice Controller and one to
In the inspector set the mode of all three to
Entity and set the
Name to the corresponding entity name in our data model (
Next up are the bindings for our array controllers. We’ll start with the
This is the simplest controller to hookup. We just have to tell the controller
that it will be getting its
Managed Object Context from the
Reckoning_AppDelegate using the
Model Key Path.
Reckoning_AppDelegate was created for us when we created the
Next up is the
You’ll notice an extra section to our bindings for this one. The bottom,
Managed Object Context binding, is setup exactly the same as our
Controller. The extra binding, the
Content Set binding, allows us to bind
the content of the
Invoice Controller to the currently selected item in the
Client Controller. This way we’ll only show the invoices for the selected
client. So, you can see we’ve bound to the
Client Controller with a
Controller Key of
selection and a
Model Key Path of
What this is doing is taking the current
selection from the
Controller and retrieving the array returned from the
invoices method. The
invoices method was created for us by Core Data based on the
relationship we created in our model.
This one is very similar to the
Invoice Controller binding. We just bind to
Invoice Controller instead of
Client Controller and we want the
payments key path in the
Sweet, our arrays are now hooked into our Core Data model and are ready to
populate our tables. We’ll bind the
Clients table first.
Nothing too complicated in there. We want to bind the
Value binding for the
Table Column (make sure you have the Table Column and not the Table View or
Text Field Cell or the Scroll View). We bind to the
Client Controller as
it’s the client information we want to display. We use the
Controller Key of
arrangedObjects which will return the objects in the given controller. I
setup the cells to display the name of the client so we use the
path into the
Client object. I’ve also checked the
Value field. I’m not sure what this does but one of the tutorials mentioned it
so I use it. If you know, please let me know.
We now move onto the
This is similar to our
Client bindings from above. We want to bind to the
Invoice Controller and I’m using a different model key. The model key is set
client.name. This allows me to retrieve the
name field of the
client associated with this invoice. (Remember we have a relationship from
Client in our model.) You can use the dot notation to chain
these methods together to pull out the exact information you require.
Due Date column is the same except I bind the model key to
Payment table is basically the same bindings. For the
column I bind the model key to
invoice.client.name to pull out the client
name for the invoice associated to this payment. The other fields just bind to
Cool. Ok, we can now display the data stored in our Core Data model. I guess it would help if we could actually add some data. If you haven’t noticed, pretty much everything we’ve done has been fairly easy (assuming you know the right syntax for the model keys and the controller keys.) Hooking up add and remove is no different.
If you option-click on the
+ symbol in the
Clients section you can drag
(and a blue line will appear) down to the
Client Controller. Releasing the
mouse you’ll see a popup. Select the
add: entry. Do the same option-click
and drag from the
- button and select the
Follow the same procedure for the buttons in the
Save the file in Interface Builder, switch back to Xcode and run the application. You should be able to add clients, invoices and payments and hitting apple-s will save the file. If you close Reckoning and re-open you should see your created data populating the tables.
Not bad for not actually writing a line of code, eh?
Next time we’ll be attempting to hookup the
Edit buttons for the client and