iOS Wireless App Distribution [Updated]

With the new iPhone iOS 4, you can distribute apps wirelessly without iTunes intervention. You still need to collect the appropriate devices id’s and create the appropriate provisioning profiles but if you already have those sending out files is easy.

First, select “Build and Archive” from your XCode build menu. Your archived project will be stored in the “Archived Applications” section of the the XCode organizer (Window > Organizer).

Next, select the archive you want to distribute in the XCode organizer and select “Share Application…” at the bottom of the window. Pick the appropriate provisioning profile and then “Distribute for Enterprise”.

In the distribution window, enter the title and the full url to the ipa file (where you plan to host your app) for example, http://jeffreysambells.com/example.ipa.

Along with the generated .plist and the .ipa files, you’ll need the provisioning profile and a simple index file, for example:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>My Cool app</title>
</head>
<body>
<ul>
    <li><a href="http://jeffreysambells.com/example.mobileprovision">
                Install Example Provisioning File</a></li>
    <li><a href="itms-services://?action=download-manifest&url=http://jeffreysambells.com/example.plist">
                Install Example Application</a></li>
</ul>
</body>
</html>

With these all uploaded to your server all you need to do is point people at the index file and they can select the links to install the provisioning profile and app directly from mobile safari on their iOS devices. A much nicer experience compared to installing through the iTunes sync process.

UPDATE

After re-publishing updates to my apps several times, I was finding it a little tedious to re-type the full url into the XCode tool—plus I could never remember what I named the damn files—so I came up with this quick and dirty index.php script to do the grunt work for me:

<?php

$ipas = glob('*.ipa');
$provisioningProfiles = glob('*.mobileprovision');
$plists = glob('*.plist');

$sr = stristr( $_SERVER['SCRIPT_URI'], '.php' ) === false ? 
    $_SERVER['SCRIPT_URI'] : dirname($_SERVER['SCRIPT_URI']) . '/';
$provisioningProfile = $sr . $provisioningProfiles[0];
$ipa = $sr . $ipas[0];
$itmsUrl = urlencode( $sr . 'index.php?plist=' . str_replace( '.plist', '', $plists[0] ) );


if ($_GET['plist']) {
    $plist = file_get_contents( dirname(__FILE__) 
        . DIRECTORY_SEPARATOR 
        . preg_replace( '/![A-Za-z0-9-_]/i', '', $_GET['plist']) . '.plist' );
    $plist = str_replace('_URL_', $ipa, $plist);
    header('content-type: application/xml');
    echo $plist;
    die();
}


?><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Install iOS App</title>
<style type="text/css">

li {
    padding: 1em;
}

</style>
</head>
<body>
<ul>
    <li><a href="<? echo $provisioningProfile; ?>">Install Team Provisioning File</a></li>
    <li><a href="itms-services://?action=download-manifest&url=<? echo $itmsUrl; ?>">
         Install Application</a></li>
</ul>
</body>
</html>

It’s nothing fancy so you’ll probably want to spiff it up but to use it (you’ll need PHP), just create a folder and drop in the index.php along with the .mobileprovision file, the exported .ipa and the exported .plist. To make things easy just enter _URL_ (uppercase url with underscores) as the “url” in XCode and the script will automatically fill in the proper URL as necessary. It doesn’t matter what the file names are as long as there is only one .moblieprovision, .ipa and .plist.

Dissecting iPhone OS Touch Actions for Text

I’ve been working my way through a UiTextInput rich text editor implementation in iPhone OS 3.2 and thought I’d share some observations I’ve made regarding touch input related to text.

One of the biggest hurdles with UiTextInput is that you don’t get the benefit of the great UI work that Apple has put in their core iPhone text inputs. There’s no text selection, magnification loops, copy/paste or any of those other things we take for granted when we use the default inputs. If you want advanced editing features you have to implement them all yourself—and it’s a lot harder than it looks.

Text selection, for example, is something that you’re probably familiar with if you’ve used an iPhone or iPad but have you ever stopped to actually look what’s going on? The subtleties of the touch interaction are very intuitive but may not be what you expect when you think about it. For example, here are few things you may not have noticed when editing text in something like the default notepad.

  • When you quickly single tap, the carat is placed in the text where you tapped but it’s position is based on word boundaries. A mouse click in a word processor places the carat at the point where you clicked but a tap in iPhone OS places it in front of or after the word, depending on which end of the word was nearest the tap.

  • Holding on a word will reveal a magnifying loop that gives you fine grained control over the selection. This is the only way you can place the carat within a word.

  • The select/select all/paste menu will appear if you long-tap. As a result it will always appear after the loop is shown but could also appear if you touch and release just before the loop shows (of course you’ll be selecting and pasting on a word boundary in the latter case).

  • The “select” option in the menu always starts by selecting a full word. Which word depends on where the carat is placed. If the carat is in a word then that word will be selected. If the carat is at the beginning of a word, between the space and the first letter, then the word following the carat is selected. If the carat is at the end of a word, before the following space, then the word preceding the carat is selected.

  • Double tapping a word will immediately select it. If you hold and drag on the second tap you can select a larger range based on where you drag. Interestingly, the range selection also has a magnifying loop—but with a different shape—so you can have fine grained control at the end of the selection.

These are just a few of the subtle interactions that most iPhone users just do without thinking about. If you’re going to implement your own UITextInput be sure to closely look at the existing text inputs and how you typically interact with them as you’ll need to implement yours the way users will expect it to work.

Static Libraries with XCode and iPhone SDK

One of the easiest ways to overwhelm yourself when developing for iPhone OS is trying the all-in-one approach to code management. I’ve done it countless times and rooting through a maze of groups and files in a project can drive you insane. After frustration push me to the edge one too many times I finally snapped and sat down to figure out a better way.

When you have a large project, there’s often a lot of code that is very generic—an XML parser, network connection manager, location manager—that could be placed in their own shared libraries to use across different projects. This separation of code vastly increases the cleanliness of each individual project but also has other advantages including:

  • Clear boundaries for business logic, api requirements, and unit testing.
  • Less overlap in a single project when multiple developers are working together.

Moving the code to a library also removes it from the project with the “business logic”, allowing you to focus on the task at hand and prevent you from “tweaking” libraries just to accommodate the business logic of one particular project.

The iPhone SDK doesn’t support custom frameworks or dynamic libraries so you may not have thought it was possible but iPhone OS does does support static libraries and therefore allows you to write re-useable code—with a few caveats. This is how libraries such as Flurry or Google Mobile Analytics work.

The only disadvantage is that if you include other frameworks or libraries in your library you still need to include them ALL in your final project. Adding MapKit to a static library means you also need to add MapKit directly to the main project (which is why you need to add the SQL lib with Google analytics).

Creating your own library requires a few steps but it boils down to:

  1. Create an XCode project to contain all the code for a specific library.
  2. Create a static library target with the appropriate configuration.
  3. Create a project that will use the library.
  4. Import your static library project into your main project.
  5. Link in the static library.

To simplify steps one and two even further, I’ve created an iPhone OS Static Library Project template for XCode. Download it and feel free to suggest any changes you’d like to see.

Usage instructions and a quick “How To” are available on the project page.

Enjoy.