Psellos
Contemporary Development With Functional Programming

Portland: Which Way Is Up on iOS?

May 8, 2012

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 iOS Simulator. See Gamut: Explore Colors in iOS Simulator for a simple example app packaged for the iOS Simulator.)

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.

The most recent version of Portland is 1.0.5. I built and tested it under OS X 10.7 (Lion) using Xcode 4.3.2, the latest available at the time of writing. I would expect it to work with minimal changes using future Xcode releases also. A previous version of Portland, for earlier versions of OS X and Xcode, can be found in the OCaml Programming Archives.

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 iOS apps in OCaml are:

  • Cross-compiling OCaml to ARM with iOS ABI

  • Linking to native iOS libraries

  • Using Interface Builder

  • Packaging, including code signing

For the first problem, you can use OCamlXARM, the OCaml cross compiler that we use at Psellos. 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 iOS 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 iOS application (like our Cassino and Master Schnapsen/66 apps), 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. These instructions are for Xcode 4.2 and later. You also need an OCaml-to-iOS cross compiler. As I mentioned, I use OCamlXARM, a modified version of OCaml 3.10.2 with patches from others and from us (Psellos). You can download a binary, or build it from source yourself. See Compile OCaml for iOS for details.

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.5 from Psellos:

$ curl -O -s http://psellos.com/pub/portland/portland-ios-1.0.5.tgz
$ ls -l portland-ios-1.0.5.tgz
-rw-r--r-- 1 psellos staff 19360 May 7 13:45 portland-ios-1.0.5.tgz

To save typing, you can download it directly from this link:

Unpack and Check Particulars

$ tar -xzf portland-ios-1.0.5.tgz
$ cd portland-ios-1.0.5

Most of the work of building Portland is done by Makefile.ios. You may need to change some of the specific settings in this file:

PLAT = /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform
SDK = /Developer/SDKs/iPhoneOS5.1.sdk
OCAMLDIR = /usr/local/ocamlxarm

Change PLAT to the location of your iOS platform directory, part of the Xcode iOS SDK. Change SDK to the iOS 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.

Make sure Xcode is building for an iOS device (not the simulator) and is building the Portland target (not Portlandbin).

  • Click at the left end of the Scheme selector at the left end of the toolbar. A menu drops down. Select Portland -> iOS Device.

The code for Portland is currently written to run in iOS 3.1 or later. If your device is running iOS 3.X, you’ll need to change the debugger used to launch the executable. The latest debugger, LLDB, apparently doesn’t work with old versions of iOS.

  • If you need to do this, again click at the left end of the Scheme selector. In the dropdown menu, select Edit Scheme. In the panel that appears, change LLDB to GDB.

Note: if you have the launch problem, the symptom is that Portland appears to hang immediately after starting. Exit the app and restart directly on the device. It will work fine; it’s only the debugging handshake at startup that fails.

Next make sure code signing is configured properly. This is hard to describe, but not so hard to do.

  • Under the Navigate menu, select Reveal in Project Navigator. This should show the project as a whole and its two targets near the top of the second pane.

  • Click on Portland, the target. It’s the last listed item in the second pane, with an A-shaped icon.

  • Click on Summary in the third pane. This brings up a few key settings for the target, including its bundle identifier.

  • Set the bundle identifier to an appropriate value for your development environment. In the supplied source, the initial value of the bundle identifier 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 and run an app with any identifier.

  • Now click on Build Settings in the third pane. This brings up a zillion settings for the Portland target, including code signing.

  • In the build settings, 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 ‘iOS Team Provisioning Profile: *’. The Team Provisioning Profile is the one created for Automatic Device Provisioning; it’s a wild-card profile that matches every bundle identifier.

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 Product -> Build menu item.

  • To build and run, click on the Product -> Run menu item.

If things go right, you’ll see an app running on your iOS device 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 iOS 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 nibfile, 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 click on Portland.xib in the leftmost pane, you’ll activate Interface Builder. You should see that the nibfile is also 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 doesn’t know anything about OCaml (at least not yet), it gets its information from the header files for your wrapper classes. Note that you need wrappers only for OCaml classes that are accessed from ObjC, which should be just a few of them. In Portland, there’s only one header file, wrap.h.

Discussion

This Portland app is stripped to the bare essentials, to show how to compile, link, and package an iOS app. For something with more of the feel of a real iOS app, see Slide24: Sliding Tile Puzzle for iOS.

As mentioned above, you can also run OCaml apps in the iOS Simulator. This is particularly easy, as it doesn’t require an iOS device or any code signing. You can download a compiler binary from Psellos, or build your own compiler from sources. See Compile OCaml for iOS Simulator for more information.

Many other resources, including more example apps for both iOS and the iOS Simulator, are listed on our OCaml page.

If you have comments, questions, or corrections, please leave a comment below or email me at jeffsco@psellos.com.