Since Xcode 4 has, at least for the moment, removed the wonderful Core Data UI prototyping tool here is a newer version that goes through setting up the same app as before but using Xcode 4.1 running on Lion. While the screen shots will all be from 4.1 on Lion almost everything will be the same if you are running 4.0.x on Snow Leopard.
To save you from the need to first read the original tutorial before this one I will cover everything from the last one here just adding in the bits required for Xcode 4.x. If you have already completed the previous version of this tutorial (or for whatever other reason just need the portion related to bindings) feel free to scroll down until just past the image of a Commit sheet (at that point the project has been created and the data model built). If you find you are having issues using Xcode 4 then I suggest you take a look at these guides to start with:
For a complete list of guides related to Xcode check out Tools & Languages (you don’t even need a login to get to these).
With that out of the way let’s begin:
This tutorial assumes you have the Apple Developer Tools installed and are familiar with basic operation of Xcode and Interface Builder. It also assumes that you have a very basic knowledge of Core Data. If you are completely new to Core Data check out either Getting Started with Core Data or read chapters 11 & 30 of Aaron Hillegass’s book Cocoa Programming for Mac OS X 3rd Edition. Really if you are new to Cocoa you should have a copy of Aaron’s book no matter what, it is the one book I always go to when I’m not sure about something. We will also be doing a little with bindings, if you need a starter check out either Cocoa Bindings Programming Topics or Chapter 11 of Aaron’s book.
This is the first tutorial in the series so we should first specify our goal. The purpose of the app will be to track daily expenses. Every feature we think about adding should be assessed based on that one sentence, this will hopefully help keep down the feature creep. Since the purpose of the app is to track expenses we will just call it Expenses. Now we should determine the major features of the app. It will of course need to store expenses, perhaps when, what, & how much. To make life easier it would be a good idea to be able to assign each expense a category so the user can figure out things like how much they spend on food versus movies. Of course along the way we will come up with a few smaller features (such as copy and paste of expenses) that we will add in, but this should be the bulk of it. We will use Core Data to build Expenses because it is extremely well suited to this kind of work and will reduce the amount of code we have to write dramatically.
Note: The original version of this tutorial had the idea of also covering things like crash reporting, updates, etc.; however now that the Mac App Store has been released those things are handled by the store for you. I may cover a very basic validation method for App Store apps but if you look on GitHub you will find the code I would use (I would recommend making some changes to what you find).
Today we are going to start by just building the basic app without writing any code at all!
Create The Project
To start with open Xcode and create a new project (either from the File menu or from the startup window).
Choose to create a Cocoa app
Name it ‘Expenses’, set its App Store category to Finance, select to create a Document-Based Application, select to use Core Data, set the document extension to ‘expen’, and if you want change the Company Identifier to something more appropriate (since it may very well be MyCompany to start with), but not mine.
Pick a place to save the project and make sure the check box to create Git repository is checked (every project should have some sort of SCM after all).
Create The Data Model
Once the project window appears click on the disclosure triangle beside the root of the project in the Project Navigator.
Add two entities: one named ‘Expense’, the other named ‘Category’
Add one attribute to Category called ‘name’
If it is not currently visible show Utilities (you can do this from View -> Utilities -> Show Data Model Inspector, or by selecting the toolbar icon to show the utilities and then selecting the icon on the right for the Data Model Inspector).
In the inspector:
- Set the Attribute Type to be String
- Uncheck Optional (a category is pretty useless without a name)
- Set the Default Value to “Category” (since we are requiring a name we need to make sure one gets set right away)
Then add one relationship to Category named “expenses” (since it will be a to many relationship the title should be plural)
In the inspector:
- Set the Destination to ‘Expense’
- Leave the Inverse for now since the other side of the relationship doesn’t exist yet.
- Leave ‘Optional’ checked
- Check the box marked “To-Many Relationship”
- Do NOT check the “Ordered” box, it isn’t needed and will just slow down the app.
- I have left the Delete Rule as Nullify which means that if you delete a Category any associated Expense entities will just end up without a category until a new one is assigned. If you would prefer that when a Category is deleted any related Expense entities are also deleted change the rule to ‘Cascade’.
Next select the ‘Expense’ Entity and add a single relationship to it called “category”. Set the destination to be ‘Category’ and the inverse to be ‘expenses’ (you shouldn’t have to make any other changes beyond that and Xcode should have taken care of setting the inverse relationship for Category.expenses for you).
Then add three attributes to ‘Expense’:
- “desc”, type String, default value “Expense”, non-optional
- “date”, type Date, default value “TODAY” (this will add today’s date automatically for us), non-optional
- “amount”, type Float, default value 0, non-optional
If everything is right so far you should have no errors or warnings listed and if you select the graphic view style for the model it should look like this:
If you do have any errors they are likely telling you that an attribute is currently undefined so just check the errors to see which ones you missed and fix them.
Once the model is complete go a head and commit the project as it stands:
- Select File -> Source Control -> Commit…
- When the Commit sheet comes up add a message along the lines of “Added data model.”
- Click ‘Commit’
Create The User Interface
Layout The UI Elements
Now that we have our model we need to work on our view. Before we start let me just say that this UI is in no danger of winning any ADAs. Mostly this is to keep from distracting from our primary topic.
To start with navigate to MyDocument.xib. If you closed the Utility area you should open it back up again. (I tend to keep the Utility area open when I am working with Core Data models and xib files but closed the rest of the time. I also tend to close the Navigator area when working with xib files to save space.)
Delete the label that says “Your document contents here” from the window.
Change the window size 600 wide by 475 high.
At this point you can choose to either continue to use the old springs and struts style UI or you can use the new Auto Layout. For this tutorial I will primarily using Auto Layout since it is newer and better. For those of you who would prefer to stick with springs and struts (perhaps you are still using, or targeting, 10.6) I will still give you some guidance but I won’t be supplying any screen shots.
Using Auto Layout
In the Utility area select the File Inspector
Check the box marked “Use Auto Layout”
At this point you will likely get a sheet that looks like this:
Go ahead and confirm.
Springs And Struts
If you are using Springs and Struts(S&S) you really just need to set the window’s minimum size to the current size to prevent bad things from happening if the user makes the window really small. For the rest of the UI directions I will indicate the differences is brackets ‘[‘ & ‘]’.
Both UI Methods
No matter what UI method you are using drag an NSTabView from the Object Library (It is at the bottom of the Utility area and you may need to select it) into the window.
Stretch the tab view until if nearly fills the window (use the blue guides that appear at 20 pixels in from each edge). With Auto Layout you should see what look a lot like blue I-beams connecting the tab view to the window. [For S&S you need to go to the sizing portion of the Utility area to set both springs and all four struts (springs are the dotted lines inside the object and struts are the solid lines outside the object).]
Rename the two existing tabs to be “Expenses” and “Categories”.
Switch to the Expenses tab.
Drag out; four text labels, two text fields, one date picker, and one pop up button; make them look something like the image below: (The constraints should drop into pretty good positions though the spacing between lines is only constrained by the amount field and the date picker) [With S&S make sure you anchor the labels and fields for Amount and Date to the right hand side on the view. All of the elements should be anchored to the bottom, and only the text field for Description and the pop up button should have active springs.]
Add a number formatter to the text field for Amount, set it to currency and Lenient (with Lenient on you can type “25” and the system will reformat it to “$25.00”, off you have to type “$25.00” yourself). Note: If you lose hold of the formatter you can grab it again with the path control just above the main editing area as see below. The formatter symbol that used to appear when a field was selected that contained one is no longer there.
Next, add two buttons just above the amount field. Delete the text from both of them. Set the image for one of them to be NSAddTemplate and the other to be NSRemoveTemplate. Make sure you leave some space between them so that if the user misses by a little when they try to add an expense they don’t end up deleting one instead.
The last thing to add to the Expenses tab is a table view. Make it so that the table fills the remaining space in the tab view.
[For S&S set the scroll view containing the table to have all springs and struts turned on.]
Click through the scroll view to the actual table view (you can look at either the path control or the top of the Utilities area to see what you have selected currently).
Once you have the table view selected give it 4 columns and alternating row color. Leave Content Mode as Cell Based.
Adjust the column widths so that they all fit on the screen.
Edit the column titles from left to right as follows: “Date”, “Description”, “Amount” “Category”.
Drag a date formatter to the first column (you need to drag it all the way to the top row in the first column). Set the formatter to have the short date format and no time format.
Drag a number formatter to the third column and set it to currency like before remembering to set it to Lenient.
Drag a Pop Up Button Cell to the last column.
In the end the Expenses tab should look something like this:
Now let’s move onto the Category tab so select it now.
For the Category tab you will need two tables, three labels, one text field, and two buttons. (To make things a little faster I selected the table and buttons in the Expenses tab and pasted them into the Categories tab)
The two buttons should go in the lower left corner of the tab view, one should be for add and one for remove as with the Expenses tab. [S&S: anchor to bottom and left]
Just above the buttons you will need one of the tables. It should be single column, labeled “Categories” and doesn’t need alternating row colors. It should be about 180 pixels wide. [S&S: anchor top, bottom, and left; springs in both directions]
The top right corner gets one label, titled “Name:” and the text field which should consume the space left between “Name:” and the margin of the tab view. [S&S: anchor these to the right and top of the view with no springs]
The bottom right corner gets the last two labels. Both should be on the bottom margin and next to each other with the second one against the right margin. The first one should get the title “Total:” the second one will need a number formatter set to currency. (You can see a glimpse of Auto Layout’s coolness if you first position the two labels next to each other and then set the text for the first one and then give the second one a longer place holder string, I used “$100,000”, you should then see “Total:” move to the left on its own while with S&S you have to reposition things) [S&S: anchor both to bottom and right with no springs]
The second table (the one I copied from the previous tab) should fill the hole that is left. It should only get three columns not four (I just reduced the count by one in the inspector which removed the last column, exactly what we want). The three columns in this table should be the same as the first three columns of the table in the previous tab. Since the table has more than one column it is useful to give it alternating rows.
You should now have something that looks roughly like this:
At this point we have the UI laid out so now it is time to drop in our controllers
Add The Array Controllers
You will need a total of four array controllers so go ahead and drag them over (you can drop them anywhere on the canvas that the window is not, you will see a ‘+’).
Next we will name the array controllers to keep track of which one is which. The easiest way to do this is to look at the bottom of the bar of the left of the canvas that has all the controllers in it, there you will see a small button to expand the bar. You will then see the title of each array controller next to it if you select one of the controllers and press Return it will let you edit the name.
Give the array controllers the following names; “ExpenseController”, “CategoryController”, “CatPopUpController”, and “ExpenseByCatController”.
In the Attributes Inspector set the Mode for all four controllers to Entity Name. ExpenseController and ExpenseByCatController should both have their Entity Name set to ‘Expense’ while the other two should be set to ‘Category’. You should also check the box for Prepares Content directly below the Entity Name as well as the Auto Rearrange Content box in the top section.
Next select the Bindings Inspector tab of the Utility area and set the Content Set for the ExpenseByCatController (not the content array as Core Data uses sets not arrays) to be the CategoryController and the Model Key Path to “expenses”. This will make it so that this controller always has all the expenses associated with the selected category.
Now bind all the rest of the array controllers’ Managed Object Context to File’s Owner and the Model Key Path to “managedObjectContext”.
That takes care of the array controllers, now it is time to start connecting the UI to the array controllers so we can actually get some data on screen.
Bind The UI Elements To The Array Controllers
First we need to be able to add and remove entities so the add and remove buttons needs to be connected to the array controllers. In the Expense tab connect the + button to the add: action of the ExpensesController, and connect the – button to the remove: action.
Do the same thing in the Categories tab only connect to the CategoriesController this time.
While we are in the Categories tab lets take care of the rest of the bindings.
Drill down to the Categories column on the left hand table and bring up the bindings inspector. You should set the Value binding to be CategoryController.arrangedObjects.name.
Next is the name text field, bind its Value to CategoryController.selection.name. (You will be using a very similar binding for the detail items in the Expenses tab.)
For the label all the way in the bottom right of the view (the one I assigned a placeholder of “$100,000.00”) bind the Value to ExpenseByCatController.arrangedObjects.@sum.amount (@sum.amount goes in the Model Key Path field). This will automatically keep the field updated with the total of whatever category is currently selected.
The table needs to have each column bound to the proper key. All three columns will have the same value for Bind To: and Controller Key:. Bind To: will be ExpenseByCatController and the Controller Key: will be arrangedObjects.
For the Date column the Model key path will be “date”, for the Description column it will be “desc”, and for the Amount column it will be “amount”.
That takes care of the Categories tab, now back to the Expenses tab.
We will start with the table view, the first three columns are nearly identical to the bindings in the Categories tab for the expense table except you will be binding to the ExpensesController instead of the ExpenseByCatController. This of course means that the Value binding for the first three columns will all be bound to ExpensesController with a Controller Key of arrangedObjects. The Model Key Paths will be as follows: Date column = “date”, Description column = “desc”, and Amount column = “amount”.
The final column is a bit different since it has Pop Up Button Cells.
First the Content binding needs to be set to CatPopUpController and the Controller Key needs to be arrangedObjects while the Model Key Path needs to be blank.
The Content Objects binding must not be bound to anything.
Content Values will look just like the Content binding except it will have a Model Key Path of “name”.
Selected Index must also not be bound to anything.
The final binding we have to worry about is the Selected Object binding, it needs to be bound to ExpenseController.arrangedObjects.category.
I stepped through each of the bindings listed in the beginning of the Bindings Inspector since they have such similar names and are easy to confuse if you aren’t sure. The first binding we connected is much like the Content Set binding for an array controller, just telling it where to look for the info. The Content Values binding tells it which value to use from each item in the content set, while the Selected Object binding tells it which item to select from the set for each row. This happens by matching the category of a particular expense entity to a category in the CatPopUpController, then the Content Values tells the system which key to display (without that last binding you won’t see the name of the category just its pointer address).
The detail fields will mostly be the same as the table column bindings only the Controller Key will change from ‘arrangedObjects’ to ‘selection’. The only exception is the category pop up for which the two Content related bindings will stay exactly the same.
- Description – Value: ExpenseController.selection.desc
- Amount – Value: ExpenseController.selection.amount
- Date – Value: ExpenseController.selection.date
- Content: CatPopUpController.arrangedObjects
- Content Values: CatPopUpController.arrangedObjects.name
- Selected Object: ExpenseController.selection.category
Run (formerly Build and Run)
You should be able to add and remove Categories and Expenses. Undo should already work. Saving and reverting should work. So should the total for each category, that’s a lot of stuff without writing any code yet!
If you have any problems just check back through the bindings and make sure the add and remove buttons are connected to the right controllers, there are lots of bindings so it is easy to miss one.
The rest of the tutorials in this series should be easy enough follow under Xcode 4 so I will likely not re-write them all. I do have plans to write another article that covers taking a version of Expenses written pre-Lion and upgrade it to make use of things such as iCloud, auto saving, and Sandbox (after all it won’t be long now before all apps sold on the Mac App Store are required to use Sandbox).
I also plan to move this project to GitHub to make the source easier to acquire. When this happens I will post on this blog and I will create either tags or branches for each article.
As always I look forward to your comments and suggestions, I hope I can help a few people new to development as others have helped me.