The first day of Reckoning
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.
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.
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
.
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
.
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
.
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
.
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.
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.
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.