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 the 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:

  • Client
  • 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

  • Invoice
  • 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

  • Payment
  • amount :: float
  • date :: date (default: “today”)
  • invoice :: relationship to the Invoice table

You’ll end up with something similar to the figure below in the data modeler. Reckoning Data Model

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 ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$ as the contact_email regular expression.

Simple enough. Although, writing this I realized I forgot the default_rate 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.

Main Reckoning 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 Amount 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.

The Edit buttons are currently unused. The + and - 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 Payment Controller.

Information Window

In the inspector set the mode of all three to Entity and set the Entity Name to the corresponding entity name in our data model (Client, Invoice and Payment respectively).

Next up are the bindings for our array controllers. We’ll start with the Client Controller.

Client Controller

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 mangedObjectContext Model Key Path. The Reckoning_AppDelegate was created for us when we created the application.

Next up is the Invoice Controller.

Invoice Controller Bindings

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 Client 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 invoices. What this is doing is taking the current selection from the Client Controller and retrieving the array returned from the invoices method. The invoices method was created for us by Core Data based on the invoices relationship we created in our model.

Finally the Payment Controller.

Payment Controller Binding

This one is very similar to the Invoice Controller binding. We just bind to the Invoice Controller instead of Client Controller and we want the payments key path in the invoice model.

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.

Client Table Bindings

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 name key path into the Client object. I’ve also checked the Continuously Updates 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 Invoicing table.

Invoicing Client Column Bindings

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 to client.name. This allows me to retrieve the name field of the client associated with this invoice. (Remember we have a relationship from Invoice to Client in our model.) You can use the dot notation to chain these methods together to pull out the exact information you require.

The Due Date column is the same except I bind the model key to due_date.

Finally the Payment table is basically the same bindings. For the Client 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 date and amount respectively.

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 remove: entry.

Follow the same procedure for the buttons in the Invoicing and Payments sections.

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 invoicing information.