Dec 29

On the eve of the decade I thought it might be fitting with a cautionary tale about dates and time.

An iPhone app that I was developing had to send data to a server. Among the data items was a timestamp.

As a best practice during development, the app was tested on several types of devices with different versions of the OS. It was working great. Except on one particular iPhone.

There was nothing obviously different about this device. Same hardware and same OS as on other devices that were working fine. We restarted the device and reinstalled the app several times. Still it refused to communicate properly with the server.

I’ll spare you the details of the hours of debugging that followed, and skip directly to the solution…

Sending timestamps between different systems is a common area of confusion and errors. For this app we had settled on expressing the time in the number of seconds since the Unix epoch (January 1, 1970). This value was then to be sent as a URL parameter in a HTTP GET request. This is a convenient way to transmit a timestamp between systems since most programming languages have ways to create a date + time object from this long value.

If you print out [[NSDate date] timeIntervalSince1970] you will see a 10 digit number. And that was what the server was expecting. However, if you look at the number (1262122135 as I’m writing this) you’ll notice that it wasn’t that long ago when the value went from 9 to 10 digits. In fact, this happened on the 9th of September 2001 at 01:46:40 GMT.

Upon further examination the obstinate iPhone that refused to run the app correctly, had its system clock set to early 2001. Thus the server call contained a 9 digit timestamp, instead of the expected 10 digits. This caused the failure when running the app.

The moral of the story: Never trust any data that you do not fully control. User input is a category that is so obvious that most developers always validate it. The system clock can also be set by the user and should therefore not be implicitly trusted.

written by Nick \\ tags: ,

Dec 18

Google Analytics is a powerful web analytics solution used by thousands of web sites. Like most Google products it’s free for most users.

Earlier I had toyed with the idea of adding a small UIWebView to each screen in an iPhone app to make use of Google Analytics. Since that would have been a kludge, I was happy to see that Google has introduced a native iPhone library that interfaces with Google Analytics.

Why would you choose Google Analytics over the many existing analytics solutions for the iPhone, Pinch Media, Medialets, Mobclix, et. al?

  • Google Analytics tracks user behavior, whereas most other services just track number of users with a particular attribute or who have done a particular action.
  • Google Analytics lets you slice and dice data in a seemingly infinite number of ways.
  • If you’re not using Location Services in your app to report the location of the user, then the analytics tool has to rely on IP address lookup for the location. Google has a much better database for this. (Note that determining a location by IP address is reasonably accurate when users are on WiFi, but all bets are off when users are on a mobile network.)

There are some drawbacks with the current version of Google Analytics:

  • The current version is 0.7 which means that there are some rough edges and bugs.
  • The simulator is identified as an iPhone device is the stats. This is not a big deal since the number of users on the simulator (i.e. you) should be dwarfed by the number of actual customers.
  • Another minor issue is that all devices are reported as supporting Java. 🙂
  • The OS version is not reported anywhere.
  • It’s not possible to tell Google Analytics the exact location of the user, should you know that from Location Services.
  • It’s not possible to create custom segmentations. This is a really powerful feature of GA, and it would be nice if that was exposed through the iPhone library.
  • If you’re not familiar with Google Analytics, it can be a daunting experience to explore and understand all the stats reported.

So which solution should you choose? My recommendation is both. I use both Google Analytics and Pinch Media, and I find that they complement each other well.

Adding Google Analytics to your iPhone app is easy.

  1. Download the library here. See the very bottom of the page for the download link. (There used to be a regular Google Code project for this library, but that mysteriously disappeared.)
  2. Add the libGoogleAnalytics.a static library and the GANTracker.h header file to your project.
  3. Add the framworks required by Google Analytics. If you already have another analytics library installed, then you probably already have the necessary frameworks added.
  4. Add a few lines of code to initialize the tracking and then a few lines where you want to track a page view or an event. See the included sample project for examples.

If you’re going to use more than one analytics service, you should consolidate the calls to each service in one place. Below are two methods that I use for Google Analytics and Pinch Media. Note that since Pinch Media doesn’t have any concept of page views, that method only calls Google Analytics, whereas both services track events.

- (void)trackPageview:(NSString *)pageName {
  // Google Analytics
  NSError *gaError;
  if (![[GANTracker sharedTracker] trackPageview:pageName withError:&gaError]) {
    NSLog(@"GA trackPageView Error: %@", [gaError localizedDescription]);
  }	
}

- (void)trackEvent:(NSString *)category
            action:(NSString *)action
             label:(NSString *)label
             value:(NSInteger)value {
	
  // Google Analytics
  NSError *gaError;
  if (![[GANTracker sharedTracker] trackEvent:category
                                       action:action
                                        label:label
                                        value:value
                                    withError:&gaError]) {
    NSLog(@"GA trackEvent Error: %@", [gaError localizedDescription]);
  }
	
  // PinchMedia
  NSString *subBeaconName = [NSString stringWithFormat:@"%@ - %@ - %@", category, action, (label ? label : @"")];
  @try {
    [[Beacon shared] startSubBeaconWithName:subBeaconName timeSession:NO];
  } @catch (NSException * e) {
    NSLog(@"Error starting SubBeacon %@ : %@", subBeaconName, [e description]);
  }
}

written by Nick \\ tags:

Dec 15

An iPhone application that we’re developing for a client handles credit card information on the device. Therefore we need to have the app tested and certified against the Payment Application Data Security Standard (PA-DSS).

Unfortunately the consultant we had lined up for the testing self-destructed.

Do you know anyone in this business with experience testing iPhone apps? Are you a PCI consultant looking for a gig? Please let me know in the comments below. Thanks!

written by Nick

Dec 08

Unfortunately I was not able to attend any of Apple’s iPhone Tech Talk World Tour presentations this year. Fortunately several developers have posted their notes from the events.

On this blog I talk a lot about UIWebView. It turns out that Apple is pretty bullish on using web views in your app as well (from the iPhone Blog):

  • One dev who was new to Apple technologies found WebKit and their specific CSS (-webkit-gradient, -webkit-mask, webkit-box-reflect) to be “astoundingly powerful”. If you run WebKit or Safari, check out the westciv.tools.gradients demo.
  • Apple stressed the advantages of using WebKit and embedded WebView. The AppStore app is an example of a native app with a WebKit UI made by Apple.
  • A button made in CSS is much lighter than an image file and also scales elegantly (resolution independent).
  • Even a JPG that’s only 50k in size will take up 10 times more memory when it’s decompressed and rendered in a UI.
  • WebKit interfaces can be updated outside of the App Store approval process, so no resubmission just to change UI elements.
  • Client-side database storage API in HTML 5 saves state locally and reloads the next time you view the page. See the webkit.org/demos/sticky-notes demo.

written by Nick

Dec 04

In a previous post I outlined some of the difficult choices developers face in deciding to embrace In App Purchase in free apps to allow users to upgrade to a full version.

The issues include:

  • No upgrade path provided by Apple for current owners of a paid version.
  • Difficulty in breaking into the free top lists on the App Store.
  • In App Purchase requires OS 3.x and wireless access to the App Store.

Some of my clients have solved these issues by offering two versions of their apps on the App Store:

  1. A free app with In App Purchase to upgrade to the full set of features.
  2. A paid app with all content or functions already enabled.

If you have already been offering your app as a paid version and a free lite version, the only difference with this approach is the addition of In App Purchase to the lite version.

While this resolves most of the developers’ issues, it doesn’t help reduce the clutter on the App Store.

Update: I just noticed that Riptide Games have come to a similar conclusion after their experiments with In App Purchase. They also detail some of their statistics on sales and conversions. Recommended reading.

written by Nick \\ tags:

Nov 19

I just noticed something in iTunes 9 which I’m sure has been there in previous versions. But since it was new to me, it might be new to a few of you as well.

At the bottom of the top-level pages in the iTunes Store there is a collection of text links. The last link in the last column is called Change Country. When you click on that link you get to select a country from a large list. After you click on a country you will now be viewing the iTunes Store for that country.

Why is this useful for iPhone developers?

  • You can see the ranking of your apps in other countries.
  • You can see which apps are most popular in other countries.
  • You can read reviews of your apps from other countries.
  • You can see which apps have localized app descriptions.

At the bottom right corner of the top-level iTunes Store pages there is a round flag icon that indicates which country store you are currently in. Clicking on this icon has the same function as clicking on the Change Country link. This can be useful to know since the Change Country link is often localized to the language of country you’re viewing. So it’s not always easy to know which text link to click to get back to a language you can read…

Some interesting observations:

  • The iTunes Store is available in 76 countries. The iPhone is available in 86 countries. Does that mean people in 10 countries do not have access to the App Store? I’m guessing that countries like French West Indies, Reunion Island and U.S. Virgin Islands where the iPhone is available, use their “parent” country’s iTunes Store. Can any of my international readers confirm this?
  • The App Store top lists, New & Noteworthy, What’s Hot and Staff Favorites are all unique to each country store.
  • The top lists in music are much more homogeneous than the App Store lists. I guess the music labels still have some global marketing clout. Who is going to take on this role for apps?
  • The iTunes Store only has 7 official languages: Dutch, English, French, German, Italian, Japanese, Spanish. So you will only see app descriptions in these languages. (The iTunes UI is localized to many more languages than these.)
  • There is an exception to the rule of app descriptions in only 7 languages. If you have an app that is only available in your own language (not English) you can write the app description in that language in the place of English in iTunes Connect, and then only make the app available in your country’s App Store.
  • You cannot login to a foreign iTunes Store with your existing iTunes account. Therefore you cannot purchase apps or write reviews. (It is possible to create an iTunes account without a credit card. This also works after you select a foreign country. But in order to purchase anything you need to have a valid billing address in that country. So the utility of this technique is limited.)

Did you know about this iTunes feature already? What have you used it for?

written by Nick \\ tags: ,

Nov 13

This week has seen the departure of two high profile iPhone developers: Joe Hewitt and Rouge Amoeba. This is a big loss to the iPhone development community.

Rouge Amoeba details the story of the rejection of their Airfoil Speakers Touch app and presumably that was the straw that broke the camels back. I think this case has some interesting aspects that are worth discussing. I will not take sides, since I don’t know all the details of what has transpired between Apple and Rouge Amoeba.

There has been an interesting exchange between Jeff LaMarche and John Gruber about this case, which to me highlights the difficult position that Apple’s App Store reviewers are in. The iPhone is a computer that is constantly connected to a network, but it happens to be disguised as a phone. The complexity of the applications that you can write for the iPhone is astounding. The Airfoil Speakers Touch app is a great example of a complex application that involves streaming audio over a network from another computer. When a very experienced iPhone developer like Jeff LaMarche is not clear on the details of where images that show up in the UI are coming from, how is an App Store reviewer (presumably without a developer background) supposed to understand how an application like this is architected?

Since Apple is unlikely to completely do away with the App Store review process, what can be done to make it better?

In Rouge Amoeba’s case maybe getting an engineer at Apple involved earlier could quickly have resolved any technical questions Apple might have, and then they can make an informed decision on merits instead of possible misunderstandings. To not overburden an already bogged down process, there should be a limit to how often you can call in engineers to review your case. Maybe you have to use one of your golden support tickets. (And they could borrow the process from American football where a team who challenges the ruling on the field does not get charged a timeout if the ruling is overturned.)

I think the bigger issue is transparency. (Regular readers know that I’ve been on this soap box several times in the past.) In the beginning Apple could be excused from being overwhelmed and making up rules as they go. But now, 15+ months later, it’s time to publish the rules. All the rules.

It’s the uncertainty and apparent randomness that is making life hazardous for iPhone developers and companies who depend on the App Store for a living.

Maybe Apple still doesn’t know all the rules and all the details. That’s ok. The American legal system works in a similar way with case law. There is a limited number of laws that have been legislated and then courts interpret those laws and apply them to real-world cases. The key here is that courts’ decisions are published for all to see and learn from. I know this is a gross simplification and a system that is not without controversy, and bad decisions. But I think there’s enough similarity for Apple to adopt a similar system.

If every App Store rejection was published by Apple, including a detailed explanation of the reasons, all developers could learn and avoid mistakes made by others. Developers would win in such a system. I think Apple would win even more with a less clogged review system and much happier developers.

written by Nick \\ tags:

Nov 09

UIWebView has very few instance methods. One of them is stringByEvaluatingJavaScriptFromString, which very powerful and unfortunately poorly documented. (This is literally the extent of Apple’s explanation: “Returns the result of running a script.”)

Let’s explore this mysterious method with a couple of examples.

A trivial example of how to use stringByEvaluatingJavaScriptFromString is to get the title of the HTML document:

NSString *title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];

You would typically place this line of code in webViewDidFinishLoad.

This technique is not limited to one-liners, or accessing simple properties. Here’s an example of two lines of JavaScript code executed in order, as you would expect:

[webView stringByEvaluatingJavaScriptFromString:@"var field = document.getElementById('field_2');"
						 "field.value='Multiple statements - OK';"];

You can also call JavaScript functions this way. And if you want to call a JavaScript function that does not already exist in the web page that you’re downloading, you can “inject” it yourself with this technique:

[webView stringByEvaluatingJavaScriptFromString:@"var script = document.createElement('script');"
						 "script.type = 'text/javascript';"
						 "script.text = \"function myFunction() { "
							"var field = document.getElementById('field_3');"
							"field.value='Calling function - OK';"
						 "}\";"
						 "document.getElementsByTagName('head')[0].appendChild(script);"];

[webView stringByEvaluatingJavaScriptFromString:@"myFunction();"];

In essence I’m using Objective C to create a string which represents JavaScript which which when executed adds a JavaScript function to the HTML DOM. Apologies for the multiple meta levels… Let me try to untangle this line by line.

Line 1 : First we create a <script> element using JavaScript.
Line 2 : Set the type of the <script> element to text/javascript.
Line 3-6 : Set the content of the <script> element to the JavaScript function that you want to inject.
Line 7 : Add the new <script> element as a child to the <head> element of the HTML DOM.
Line 9 : Call the new JavaScript function.

Bonus tip: You can break up NSString constants over multiple lines in Xcode for increased readability. Just end the line with a double-quote character and begin the next line with a double-quote character. At compile time these lines will be joined into one string. So the string that begins with “var script” on Line 1 is one continuous string ending with “appendChild(script);” on Line 7.

Although it’s not critical for the discussion above, here’s the HTML that the example JavaScript refers to:

<html>
  <head>
    <meta name="viewport" content="width=320; initial-scale=1.0; maximum-scale=1.0"/>
  </head>
  <body>		
    <p>This is the UIWebView</p>
    <form>
      <input id="field_1" type="text" name="value" /><br/>
      <input id="field_2" type="text" name="value" /><br/>
      <input id="field_3" type="text" name="value" /><br/>
    </form>
  </body>
</html>	

You can use this generic technique to add JavaScript to any web page that you’re downloading and displaying in a UIWebView. The technique and the possibilities are similar to what you can do with the Greasemonkey plugin for Firefox.

written by Nick \\ tags: , ,

Nov 02

Thank you oDesk!

To all new visitors from oDesk: Welcome! If you like what you see here, click on the orange button in the top right corner of each page to subscribe to the RSS feed.

You should also check out the other blogs on the list. They’re all on my daily reading list.

written by Nick

Oct 30

For a recent project I encountered the following requirements:

  1. Download and store HTML documents that are rendered using the UIWebView class.
  2. Download and store the external documents referenced in the HTML, like images, so that they, along with the HTML documents, are available in non-networked situations.
  3. Use the same HTML documents and external documents as the body of in-app generated Emails.

The challenge was to devise a scheme whereby the same HTML documents can be used for both rendering content within the app under non-networked situations and to provide the exact same user experience within the Email body; a clear Catch-22!

There are a number of possible approaches (see page 10 of Nick’s presentation, but the one we choose was to use complete URL’s for all externally referenced documents within the HTML (for example, all images are referenced on a CDN). We choose this route as it provides the simplest way to support the Email requirement.

Next we had to figure out a way to modify these URL’s dynamically to reference the same external documents located locally on the device. To do this we use some features of UIWebView and Javascript.

First we set up the UIWebView and implement the webViewDidFinishLoad delegate method to kick off the Javascript. Below you can see we take advantage of the webview’s stringByEvaluatingJavaScriptFromString() method to call our Javascript event listener useLocalPaths().

- (void)webViewDidFinishLoad:(UIWebView *)webView {
  DLog(@"Local URLs used = %@",[webView stringByEvaluatingJavaScriptFromString: @"window.useLocalPaths()"]);
}

The Javascript for the event listener is the following:

<script type="text/javascript">
  window.addEventListener('load',function() {
    window.useLocalPaths = function() {
      var images = document.getElementsByTagName('img');
      for (var i=0; i<images.length; i++) {
        var url = images[i].src;
        if (url.length>0) {
          var bits = url.split('/');
          images[i].src = bits[bits.length-1];
        }
      }
      return true;
    };
  },false);
</script>

In this case, this event listener queries for all image tags and modifies the src attribute by stripping all of the URL except for the image file name. For example if our image tag looked like this,

<img src="http://www.pervasentcdn.com/pervasent.jpg" class="logo" alt="Pervasent" />

it would be come this

<img src="pervasent.jpg" class="logo" alt="Pervasent" />

and thus achieves our goal.

It’s worth noting that the general approach of using UIWebView + Javascript is quite handy and obviously has many possible applications.

Update: If you’re having timing issues with this approach, you may want to take a look at this technique: How To Inject JavaScript Functions Into A UIWebView

written by Jess \\ tags: ,