Profiles and certs and devices… oh hell!

Has it really been more than two years since I’ve written a post? It’s time to start writing again!

I thought that I would kick things off with something that’s cropped up again and again over those past 2+ years. And that is dealing with provisioning profiles when testing and deploying iOS apps. There may seasoned, full-time iOS engineers out there that have all of this down-pat. But for me, someone who doesn’t focus all of his attention on iOS anymore, this process can be perpetually confusing. I’ve tended to learn just enough to get things working, and when things would later mysteriously break, I’d flail around for awhile until things started working again.

If you’re like me, then read on. It’s time to figure this stuff out once and for all.

Provisioning Profiles

The first thing you learn is that everything centers around Provisioning Profiles. Provisioning profiles are really an aggregation of other elements that together determine whether an app can be built and run on a device. There are two types of provisioning profiles: Development and Distribution. As their names suggest, Development profiles are used to test ad hoc builds as you’re developing you app; Distribution profiles are used to distribute apps to the App Store.

We’ll focus on Development profiles here. Development profiles aggregate three things:

  1. Your App ID; for example, com.mycompany.mymobileapp. Each profile can contain only one app ID.
  2. A list of devices; specifically, device UUIDs. To install a build onto a device for ad hoc testing, that device’s UUID must be present in the development profile.
  3. A list of developers certificates that can be used to sign the profile.

App IDs

As mentioned above, each profile can contain only one app ID, so if you have multiple app IDs, you’ll need a separate profile for each. For example, my current company uses two app IDs: one for use when hitting our staging/test server, and one for our production server. So we need two separate development profiles.
Well, that’s not entirely true. You can get away with using wildcards if your app does not use certain features such as Apple Push Notifications. For example, if you have a simple app that uses the App ID com.mycompany.mymobileapp.testing when hitting a testing server, and com.mycompany.prod when hitting a production server, you can create a single Development profile with the App ID of
com.mycompany.*

Devices

To help enforce the “walled garden” App Store distribution model, development builds can only be installed on a limited number of devices. Apple allows each developer account up to 100 devices–identified by their UUIDs–to be registered. These UUIDs are stored in your development profile, ensuring that the profile can only be used on one of those devices.

Certificates

In going back over some earlier notes I had made, I saw that I had stated that profiles contain a list of developers who are allowed to sign the profile. This is not entirely true. Generally, each developer will have his or her own certificate–associated with his or her own private key–installed in his/her own OS X Keychain, for use in signing iOS apps. But this is not technically required. An organization could, for example, have a single certificate/private-key combination that every developer in the organization imports into his/her Keychain. This can make life easier, assuming of course that no developer ever leaves the organization.

The sum of the parts

Looking at development profiles as an aggregation of those three parts helps to make sense of why Apple established its requirements. A given iOS app (identified by the App ID), is allowed to be installed on a limited set of devices for testing purposes, and only certain developers identified by their certificates should be allowed to build that app.

What do you need to do?

So how does these concepts translate into practice? Well, let’s assume that you’ve set up your iOS developer account, but aside from that, you’re starting from scratch.
First, you’ll visit https://developer.apple.com and after logging in, make your way to the Identifiers section in your account. Create an App ID for you app. App IDs generally take the reverse-domain-name form, and incorporate the name of the product. For example, yours might look like com.yourcompany.yourproduct  As mentioned above, if you need to use more than one App ID for your product, you can either create multiple App IDs, or else create one using a wildcard (e.g. com.yourcompany.*)
Next, you’ll go to the Certificates section. Follow the instructions to create a new certificate, ensuring at the end that you download the certificate to you Mac. For starters, you’ll want to create a certificate of type iOS App Development. Once finished, double-click the certificate to install it, along with the associated private key, into your Keychain.
Then go to the Devices section, and enter the UUID of any devices on which you’ll want to test your app. There are plenty of resources online, such as this one, to help you find your devices’ UUIDs. Be sure to enter the UUIDs correctly; once entered, you won’t be able to remove any UUIDs for a year.
Finally, you’ll go to the Provisioning Profiles section. Create one of type Development / iOS App Development. When prompted, choose the App ID that you’d just created, select your new certificate, and the UUIDs of any devices on which you want to be able to test.
When finished, be sure to download the profile and double-click it to install it in your copy of Xcode. Alternatively, you can tell Xcode download and install the profile. How to do this seems to change from release to release. To do this in the current release of Xcode 5, you need to open the Preferences window, go to Accounts, enter your account info, then highlight your account name, click View Details, the finally click the refresh button at the bottom-left of the dialog.

When things change

Typically you’ll get this far, and after a bit of stumbling and tweaking things, you’ll get stuff working. But things will change. Here are some common scenarios you’ll need to deal with:

You get a new tester in your organization

This means that you’ll need to add your tester’s UUID to your account. Visit your developer account online, go to the Devices section as you did above, and enter the new UUID(s). Next, you’ll need to revisit the Provisioning Profiles section and edit your profile(s). Add the new devices to the profile. Then either re-download the profile and double-click it to re-import it into Xcode, or just tell Xcode to grab it and re-import it for you (as described above, using the Preferences window).

You get a new developer in your organization

That developer will need to have a certificate/private-key pair installed on their system that can be used to sign your app. As mentioned above, you have two options. The new developer can either create his/her own certificate, and s/he can use an existing one.
For the latter, the developer will have to access your organization’s developer account (you did give them access, right?) and visit the Certificates section to create his/her certificate. S/he will need to download the certificate and install it into his/her Keychain. The provisioning profile(s) will then need to be edited in the Provisioning Profiles section so that the new certificate is added. The developer–and ideally all of the developers in your organization–should then install the certificate into their Xcode instances, either by downloading and double-clicking the certificate, or by refreshing it from within Xcode.

What about Distribution Provisioning Profiles?

Distribution profiles are similar to development profiles, except that they don’t define the devices that are allowed to install the app. This makes sense, since presumably more than 100 pre-defined people will be using your app! Other than that, the concept is the same; your distribution profile must be tied to an app ID, and it must define the certificates that can be used to sign it.

That’s all for now

This article was intended to help shed light on the concepts associated with building iOS apps, rather than providing practical steps. But hopefully it will provide that little bit of of extra insight that will help you keep control over your iOS development process.

jQuery Mobile, AJAX, and the DOM

I’ve been messing around with jQuery and jQuery Mobile lately, and ran into an issue that’s worth exploring. It might be a newbie issue–I’m sure seasoned JQM developers don’t fall into this trap much–but I figure that I might be able to save new developers some frustration.

Let me first describe the app I was working on. The app basically provides online utilities that I and other engineers might find useful from time to time in day to day work. The utilities are grouped into different categories (for example, Date Conversion tools, Data Encryption tools, etc). While I started out developing these tools for desktop browsers, I figured I also ought to port them to a mobile web app as well. While the desktop tools are all displayed on one single page, however, the mobile app would present each category of tools (e.g. Date Conversion) on its own page. The “homepage” would present a simple listview, allowing the user to select a category of tools to use.

The homepage is contained in its own HTML page (named, unsurprisingly, index.html). Each tool category also has its own page; for example, the Date Conversion tools are located on dc.html. It was with this page that I ran into problems. The primary date conversion tool is one that takes milliseconds and converts them to a formatted date, and vice-versa. Given this, I’d decided to allow users to choose the date format they want to work with. To implement this, I used a multi-page template in dc.html  The first page div consisted of the tool itself; the second div consisted of a group of radio buttons from which the user could select a date format.

Stripped of irrelevant code, dc.html looks like this:

<!DOCTYPE html>
<html>
<head>

function addDfChoices() {
        // This method populates the radio buttons. Its existence becomes important later
}
$(document).ready(function() {
        …
        addDfChoices();
        …
});

</head>
<body>


<!– Start of main page –>
<div data-role=”page” id=”main”>

  <div data-role=”content” id=”main”>
    <span class=”section_content”>
      <label style=”text-align:baseline”>Millis since 1970</label>
      <a href=”#page_dateformat” style=”width:100px;float:right”>Format</a>
      
    </span>
  </div><!– /main content –>
</div><!– /main page –>


<div data-role=”page” id=”page_dateformat”>
  …
  <div data-role=”content”>
    <div data-role=”fieldcontain” id=”df_option_container”>
      <fieldset data-role=”controlgroup” id=”df_options”>
      </fieldset>
    </div>
  </div><!– /dateformat content –>
</div><!– /dateformat page –>


</body>
</html>

I loaded dc.html into my browser (I did a lot of testing in Chrome, but also of course on mobile browsers such as mobile Safari on my iPhone). The page worked as expected. On page load, the main page div was displayed, while a small Javascript instantiated different date formats and inserted them into the df_options fieldset located within the hidden page_dateformat page div. Tapping the Format link invoked a slide animation which loaded the radio buttons. Additional Javascript code (not shown above) registered any radio button selections and accordingly modified the date format used in the main page.
Except… sometimes things didn’t work. Specifically, sometimes tapping on the Format link took me to the homepage instead.
Playing around a bit, I learned that if I manually loaded dc.html into my browser, things worked fine. If I started on index.html and then linked to dc.html, and then tapped the Format link, that’s when things went wrong.
This is the relevant code within index.html:
<ul data-role=”listview” data-inset=”true” data-theme=”d”>
  <li><a href=”./dc.html”>Date Conversion</a></li>
  <li><a href=”./de.html”>Data Encoding</a></li>
</ul>
Nothing too special there.
Well, I decided to use Chrome’s DOM inspector to get a look at what was happening. I loaded dc.html and looked in the DOM inspector, and indeed I saw the two major page divs, main and page_dateformat. Then I loaded index.html and tapped on the Date Conversion link, and then took a look again at the DOM inspector after the date conversion tools page loaded. There was the main page div… but where was the page_dateformat div?
Here’s what jQuery was doing. When dc.html itself was loaded, the entire contents of the HTML file were loaded into the DOM. Since I was using a multi-page template, any div declaring data-role=”page” besides the first one (in my case, the page_dateformat div) was hidden… but it was still there in the DOM. The DOM looked roughly like this:
html
  head/
  body
    div data-role=”page” id=”main”/
    div data-role=”page” id=”page_dateformat” display=”hidden”/
  /body
/html
Now, when I started by loading index.html and then tapping the Date Conversion link, dc.html was retrieved but only the first data-role=”page” div was loaded into the DOM. Here, roughly, was the DOM at this point:
html
  head/
  body

    div data-role=”page”/  

            ^– this was the content div for index.html; it’s hidden at this point

    div data-role=”page” id=”main”/  
            ^– this is the first data-role=”page” div in dc.html, loaded via AJAX then inserted into the DOM
  /body
/html
Thus, when I tapped on the Format link (which, if you recall is marked up as <a href=”#page_dateformat”>Format</a>), the browser searched the DOM for a block with an ID of page_dateformat, couldn’t find one, and effectively gave up and reloaded the current page… which at this point was still index.html
At this point I reviewed the jQuery Mobile docs. The following little blurb provides a bit of a clue:

It’s important to note if you are linking from a mobile page that was loaded via Ajax to a page that contains multiple internal pages, you need to add a rel="external" or data-ajax="false" to the link. This tells the framework to do a full page reload to clear out the Ajax hash in the URL. This is critical because Ajax pages use the hash (#) to track the Ajax history, while multiple internal pages use the hash to indicate internal pages so there will be conflicts in the hash between these two modes.

I added data-ajax=”false” to the links on index.html  This solved the problem; dc.html was subsequently loaded completely into the browser’s DOM, allowing the page_dateformat div to be found. However, declaring data-ajax=”false” causes links to lose their animation effects. The pages are simply loaded the old-fashioned way.
I wasn’t too keen about that, so I removed the data-ajax=”false” declarations. I then changed the approach I took to the date format selection. Instead of a new “page” with radio buttons, I simply presented the user with a popup select list. For various reasons, I kept the dfChoices() Javascript function depicted above; the method was now invoked on page load to populate the select with date format options.
And immediately, I encountered the same success rate: when I started on dc.html, things worked great. When I started on index.html and linked to dc.html, the dfChoices() function was never invoked. And of course, the reason was the same as well. Much as the page_dateformat div hadn’t been loaded into the DOM, the dfChoices() function similarly was nowhere to be found.
At this point, I added data-ajax=”false”  back into the homepage’s links; for that matter, I also went back to using the multi-page template with radio buttons. There are certainly other solutions (separating out the radio buttons into their own html page and using some form of local storage to share state? putting Javascript functions into data-role=”page” divs instead of in the page header?) worth exploring, which I’ll do at some point. But just understanding the general issue is a good starting point.