Psellos
Life So Short, the Craft So Long to Learn

Portland: Which Way Is Up on iOS?

February 1, 2011

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.