Portland: Which Way Is Up on iOS?
Posted by Jeffrey
Note: This is an archived version of the Portland page, for those interested in earlier versions. The most recent version is at Portland: Which Way Is Up on iOS?.
I’ve put together an example that shows how to build a full iOS (iPhone, iPad) app in OCaml. If you’re registered as an Apple developer, you can compile the app and run it on your iPhone. (If you’re not, you can still run OCaml in the iPhone Simulator. A simple example app packaged for the iPhone Simulator is described here.)
As shown in these screenshots, what the app does is tell you how you are holding your phone: Portrait or Landscape orientation. So I call it Portland.
I built and tested the app using Xcode 3.2.5, the latest available at the time of writing. I would expect it to work with any recent (or future) Xcode release.
Please note that a few of the command lines of the following discussion are too long to fit on a single line of a typical browser window. In a lot of cases there is no good place to split them into smaller lines, usually because of a long filename or URL. Take care that you enter them as a single line if you are typing them in by hand.
Overview
The main problems of writing iPhone apps in OCaml are:
Cross-compiling OCaml to ARM with iPhone ABI
Linking to native iPhone libraries
Using Interface Builder
Packaging, including code signing
For the first problem, you can use the OCaml cross compiler that we use at Psellos, described here. The Portland example shows how to solve the other three problems.
To summarize, there are Objective C wrappers for linking from OCaml to the ObjC functions and classes of the native iPhone libraries (Cocoa Touch). There are also (inverse) Objective C wrappers for OCaml classes, which pass along incoming events from the ObjC world to the OCaml world. The inverse wrappers also provide Interface Builder with the information it needs to lay out the GUI and create an initial application state. Finally, code signing and packaging are handled by Xcode.
Keep in mind that the Portland app doesn’t really do all that much, so the amount of OCaml code is only about half the amount of Objective C wrapper code. In a more realistic sized iPhone application (like our Cassino app), almost all the code is in OCaml. The amount of Objective C wrapper code is relatively small and fixed.
Preliminaries
Before starting, make sure you have installed Apple’s Xcode and iOS SDK. I recommend using the most recent version of Xcode, though this probably isn’t critical. You also need an OCaml-to-iOS cross compiler. I use a modified version of OCaml 3.10.2, with patches from others and from us (Psellos). Information and instructions for building it are here.
Get Portland Sources
Choose a place to work (an empty directory would be good).
$ cd <good place to work>
Download the sources for Portland 1.0.3 from Psellos:
$ curl -O -s http://psellos.com/pub/portland-1.0.3/portland-1.0.3.tgz
$ ls -l portland-1.0.3.tgz
-rw-r--r-- 1 psellos staff 11702 Jan 27 13:45 portland-1.0.3.tgz
To save typing, you can download it directly from this link:
Unpack and Check Particulars
$ tar -xzf portland-1.0.3.tgz
Most of the work of building Portland is done by a Makefile
. You may
need to change some of the specific settings in this file:
PLAT = /Developer/Platforms/iPhoneOS.platform
SDK = /Developer/SDKs/iPhoneOS4.2.sdk
OCAMLDIR = /usr/local/ocamlxarm
Change PLAT
to the location of your iPhoneOS platform directory, part
of the Xcode iPhone SDK. Change SDK
to the iPhone SDK you wish to
use. Probably you want to set it to the most recent SDK that you have
installed. Change OCAMLDIR
to the location of your OCaml cross
compiler.
Open Xcode and Change Project Parameters
To open Xcode, you can use the following command:
$ open Portland.xcodeproj
This starts Xcode and opens Portland as an Xcode project. You can also open the project file (actually a directory) from Xcode’s File -> Open menu.
To simplify the discussion, I assume Portland is always your active project. This will be the case if you have only one project open in Xcode. (You might want to close any other projects temporarily—it will make things simpler.) Otherwise, begin each step by clicking the Portland project window to make Portland your active project.
Make sure Xcode is building for iPhone (not the simulator) and is building the Portland target (not Portlandbin).
Go to the Project -> Set Active SDK menu item, and select Device.
Now go to the Project -> Set Active Target menu item, and select Portland.
Next make sure code signing is configured properly. This is hard to describe, but not so hard to do.
Click on the Project -> Edit Active Target “Portland” menu item. This brings up a multi-paned window for manipulating the target.
In the Properties pane, set the app Identifier to an appropriate value for your development environment. In the downloaded example, the initial value of the app id is
com.psellos.Portland
. If you’re using Automatic Device Provisioning, you probably don’t need to change the identifier. The wild-card profile will allow you to compile an app with any identifier.In the Build pane, look at the Code Signing section. Make sure the Code Signing Identity is set to something reasonable for your development environment. For me it says: currently matches ‘iPhone Developer: Jeffrey Scofield (XXX)’ in ‘Team Provisioning Profile: *’. The Team Provisioning Profile is the one created for Automatic Device Provisioning; it’s a wild-card profile that matches every app id.
Build and Run
You can now build the app. If you have an iOS device attached, you can build and run it.
To build, click on the Build -> Build menu item.
To build and run, click on the Build -> Build and Run menu item.
If things go right, you’ll see an app running on your iPhone that looks like the screenshots above. Try holding the phone in different orientations; the display will change to show the orientation name.
Theory of Operation
Portland is a very simple iPhone app that has just one class that really does anything, named Portlandappdeleg. There is a singleton instance of this class that participates in the UIApplicationDelegate protocol to receive notifications of state changes. Let’s call this instance Pad for short. In particular, Pad notices when the app is activated and deactivated.
The startup of an iOS app is controlled by a nib file, generated by
Interface Builder. For Portland, the file is Portland.xib
. This file
says to create
Pad,
and to make it the delegate for the containing Portland app.
At startup,
Pad
establishes a timer that fires periodically, calling its own
timerTick'
method. This method determines the orientation of the
device, and uses the information to update the display.
Interface Builder
If you open Portland.xib
with Interface Builder, you’ll see that it too
is very simple. There are only three interesting objects: the main
window, a label that covers the whole window, and
Pad.
There are two connections: first,
Pad
is connected to the delegate
outlet of the File’s Owner (the
application itself). Second, the label is connected to the label
outlet of
Pad,
so
Pad
can update the display periodically.
Since Interface Builder (IB) doesn’t know anything about OCaml (at least not yet), it gets its information from the header files for your wrapper classes. If you’re running IB at the same time as Xcode, they form a kind of unholy alliance that automatically figures out which header files to read for information on your classes.
If you’re running Interface Builder on its own, you need to manually tell IB to load the wrapper header files. In the downloaded example, this has already been done for you. If you want to create your own project from scratch, you may need to do this yourself.
- To load header files into IB, click on the File -> Read Class Files menu item, and navigate to your ObjC header files, i.e., to the wrapper files for your OCaml classes. Note that you need wrappers only for OCaml classes that are accessed from ObjC, which should be just a few of them.
Discussion
This Portland app is stripped to the bare essentials, to show how to compile, link, and package an iPhone app. The Slide24 app, described here, has more of the feel of a real iPhone app. As mentioned above, you can also run OCaml apps on the iPhone Simulator. This is particularly easy, as it doesn’t require an iOS device or any special packaging. Instructions for compiling OCaml for the iPhone (and iPad) Simulator are given here, and a simple iPhone Simulator app, Gamut, is described here.
If you have comments, questions, or corrections, please email me at jeffsco@psellos.com.