2012-11-08

Barcodes in iOS

Recently I’ve been investigating methods for getting iOS apps to read one and two dimensional barcodes. There’s a huge variety of formats, but the three I’m mainly interested in are UPCA and EAN-13 (linear barcodes you’ll find on boxes in shops) and PDF417 (matrix barcodes you’ll find on some US driving licences).

There appear to be three main competitors for your attention if you’re trying to read two-dimensional barcodes in an iOS app:

ZXing is an open-source offering that supports a smorgasbord of formats for a variety of platforms and languages, including iOS, Android, Java, C#, ActionScript and others. Unfortunately, its PDF417 support is listed as “alpha quality” and the iOS port only supports QR codes (another matrix format). The iOS port hasn’t been updated in about 3 years, so ZXing is out.

Manatee Works is a closed-source library that claims to be smaller and more efficient than ZXing. It supports Windows, Android and iOS, and supports all of the bar codes I’m interested in. Looks good! How much does it cost? And can I see some code so I get an idea of how well the API was put together? No. No I can’t.

Manatee Works appears to be one of those companies that believes ardently that keeping its prices secret will encourage people to contact its sales team so they can presumably engage in the hard sell. Either that, or it’s like one of those restaurants that don’t put prices on the menus because, if you have to ask how much it costs, you probably can’t afford it. In any case, their product does indeed cost more than I’d pay.

Lastly, there’s SD-Toolkit. Again, they support multiple platforms: Windows, Android, iOS, Mac OSX and Linux, and again it supports all of the barcodes I need. Their relatively reasonable prices are published on their website and they even provide a trial SDK. My only issue with SD-Toolkit was that their trial SDK doesn’t yet support the armv7s architecture used in the iPhone 5.

If you’re trying to include a barcode reader in your iOS app, these are your options:

  • An extremely limited open-source effort that’s been abandoned;
  • An extremely expensive closed-source library that you can’t see until you talk to the sales team;
  • A more reasonably-priced closed-source library, with a trial SDK, that’s a little behind the times.

At this point I came up with an alternative solution: don’t try to read barcodes on an iOS device at all. Instead:

  • Grab a photo of the barcode using the phone;
  • Rotate, scale, crop and compress the image down to ~40K;
  • Post the image to a web server running a barcode reader SDK behind a RESTful web service;
  • Perform all of the barcode parsing on the web server;
  • Return the parsed data as JSON objects to the iOS device.

The downsides to this are obvious. Barcode parsing will only work on an iOS device that has an internet connection, and parsing times will include data transmission time. However, the advantages are compelling. The average web server is so much faster than an iPhone that the time taken to transmit in both directions and decode via a web service appears to be no longer than the time to parse directly on an iPhone. ZXing becomes an option again if you’re a Java shop. If you can’t use ZXing, you should be able to find an SDK for your prefered language for - at most - the cost of Manatee Works’ library, but it will work on all devices. Yep, a web service will work with Android, iOS, Windows, Linux, BSD or anything else with an internet connection.

That’s the option I’ve plumped for. It’s working nicely so far. The most troublesome part was rotating, scaling and cropping the images from the iOS camera correctly on all iOS devices.

One final note: Don’t bother trying to read barcodes on an iOS device that doesn’t have an autofocusing camera. It just doesn’t work reliably. Stick to the iPhone 3GS+, iPod Touch 5g+ or the iPad 3+.

2012-01-24

JSON, .NET and NSDate

If you’re trying to access a JSON web service created using .NET from an iOS device, you’ve probably discovered that dates are produced in a less than useful format:

/Date(1233423345345)/

The rationale for the date format can be found here, but it’s basically a call to the JavaScript Date object constructor. The numeric value represents the number of milliseconds since 1970-01-01.

Below I’ve added a handy Objective-C function that will extract the value and return an NSDate object. Note that we’re losing accuracy by magnitudes as we translate from .NET DateTime objects (which measure time in ticks) to JavaScript Date objects (which measure time in milliseconds) and finally to iOS NSDate objects (which measure time in seconds).


/**
 * Converts a .NET JSON date in the format "/Date(x)/" to an NSDate object.
 * @param string The NSString to convert.
 * @return the NSDate object.
 */
(NSDate*)jsonStringToNSDate(NSString* string) {

    // Extract the numeric part of the date.  Dates should be in the format
    // "/Date(x)/", where x is a number.  This format is supplied automatically
    // by JSON serialisers in .NET.
    NSRange range = NSMakeRange(6, [string length] - 8);
    NSString* substring = [string substringWithRange:range];

    // Have to use a number formatter to extract the value from the string into
    // a long long as the longLongValue method of the string doesn't seem to
    // return anything useful - it is always grossly incorrect.
    NSNumberFormatter* formatter = [[NSNumberFormatter alloc] init];
    NSNumber* milliseconds = [formatter numberFromString:substring];

    [formatter release];

    // NSTimeInterval is specified in seconds.  The value we get back from the
    // web service is specified in milliseconds.  Both values are since 1st Jan
    // 1970 (epoch).
    NSTimeInterval seconds = [milliseconds longLongValue] / 1000;

    return [NSDate dateWithTimeIntervalSince1970:seconds];
}

2011-11-29

Enterprise iOS Apps

Recently I’ve been looking into the potential of iOS devices in the enterprise, which is surprisingly zeitgeisty. Computer users in enterprises typically have to deal with laptops that take 10 minutes to boot because they’re old, running Windows XP and laden with crufty enterprise junk. Users can’t install anything because they don’t have admin rights, and the IT department doesn’t want to spend its time uninstalling the Bing toolbar and Bonzi Buddy from the computers of techno-illiterates who, like magpies, are attracted by anything that blinks, flashes, glows or moves. They have to deal with IE6 and a virus killer that saps their CPU time and their will to live in equal measure.

A few people within enterprises have bought iPads or iPhones and suddenly find themselves with a computer that doesn’t have IE, doesn’t have viruses, allows them to install whatever they want and is always ready to use. They’ve realised that they can do most of their work with nothing more than an iPad and a Bluetooth keyboard. If only there was “an enterprise app for that” they could dump their bottom-of-the-range Dells with their oversized bag and carry around their iPads instead; hence the need for enterprise iOS applications.

The average enterprise application consists of:

  • An enormous, badly-designed database written by guys who didn’t know how to make databases but who once met the brother of a friend of an acquaintance who’d seen a SQL query a few years ago, didn’t really understand it, but thought it was pretty neat;
  • A vile, enterprisey “business logic” layer, with interfaces that describe factory classes that produce factories that produce classes that aren’t used anywhere in the codebase, because all of that clever planning and abstraction was a counterproductive waste of time;
  • A dumb web UI that allows CRUD operations on the database, cunningly designed to be 100% compatible with Internet Explorer 5 and 6 and utterly unusable in anything else, possibly even implemented as an ActiveX control masquerading as a website for marketing purposes.

Enterprise iOS apps will typically replace this last tier of the application stack. At the very least, apps need to be able to interact with the business logic layer to perform CRUD operations.

If you’ve been smart when putting together your existing systems, you’ll have followed Steve Yegge’s advice and built everything as a service. Want to know which user has the network ID “elb”? Point your web browser at your RESTful HR web service and query /employees/elb. The user’s details will be output in easy-to-parse JSON format.

In that case, you can give yourself a congratulatory pat on the back and go grab copies of ASIHTTPRequest and JSONKit. All of your systems are already set up in such a way that you can interact with them from pretty much any device that supports HTTP. It’s even easier if they can perform asynchronous requests and parse JSON.

If you haven’t built a service-oriented architecture, or were misguided enough to use WCF and SOAP - perhaps because you wanted to add a piquant dash of vendor lock-in to an architecture designed for heterogeneity - you could do far worse than take a look at Nancy. This is a micro web framework for C# based on Ruby’s Sinatra that will allow you to create RESTful web services, and indeed full websites, with astonishing ease.

The next issue you’ll have is security. What you really don’t want to do is expose your databases to the internet, particularly if those databases contain sensitive data such as employee details, client data, etc. If your iPhone needs to interact with a web service behind a firewall, what can you do?

You have 3 options:

  • Ignore the security issues and expose your web services to the internet;
  • Only allow the app to work when the iPhone is connected to the corporate network;
  • Connect the iPhone to the corporate network via a VPN.

I like to think of the first option as “the Sony approach”. If you don’t particularly value the privacy of your employees or your clients, don’t mind months of downtime when you try to shoehorn security features into a live system and aren’t embarrassed by publically demonstrating your astounding ineptness, this is a very worthwhile choice. On the other hand, you might recognise it as a security disaster waiting to happen.

The second option introduces some complexities. The whole point of a mobile app is that it’s, well, mobile. A mobile app that can only be used at a single location is obviously not amazingly useful. One possible workaround would be to cache relevant chunks of the database’s data on the iPhone. All CRUD would be performed on the cache. Any changes would get replicated to the master database when the device next connected to the corporate network. Obvious downsides are the need to deal with conflicting edits, either by automatic/manual merging or by asking the user if he wants to overwrite the remotely-edited version with his own changes.

It’s a tidy solution, but it introduces another security risk - your sensitive data is now stored on a highly desirable and easily mislaid phone. You could consider encrypting data stored on the iPhone. More extreme possibilities for securing the data include requiring a username and password to start the app (which would be a massive detriment to usability) or setting up the device in Exchange so that it can be remotely wiped if lost.

The third option is the best tradeoff between usefulness and security. VPN traffic will be encrypted, there’s no need to punch holes in the corporate firewall, and the VPN can be accessed from any location with internet connectivity. The iPhone has an excellent built-in VPN client that appears to offer identical functionality to the client in OSX. This solution obviously relies on the company having an existing VPN infrastructure or being open to the idea of implementing one.

The downside is that a lost device now has access to not only any enterprise apps but the VPN too. The risk associated with this can be mitigated by using a username/password combo to authenticate with the VPN instead of using a certificate. Certificate-based authentication happens automatically, whereas username/password-based authentication requires the user to type in his password every time he tries to connect. This might be the most appropriate approach to adopt anyway, as corporations are fond of implementing expiry policies that would necessitate the creation of new VPN certificates every few months. However, the user would need to manually sign on to the VPN each time they used the app. Worse, the iPhone has a habit of dropping VPN connections whenever it feels like it. How often do you want your users to enter their passwords?

The best approach is probably a mixture of the above:

  • Encrypt all local data
  • Set up all iOS devices in Exchange so that they can be remotely wiped
  • Allow read-only access offline by caching data on the device
  • Require a VPN connection for editing data

The VPN can authenticate via certificate for ease of use or via username/password for heightened security/to appease the password policy patrol.

Our enterprise iOS app architecture now looks like this:

  • MSSQL database used as storage
  • .NET-based business logic layer
  • RESTful Nancy web service exposing CRUD operations of business logic layer
  • VPN connection for encrypted, authenticated communication with the web service
  • iOS UI app that caches data locally for offline browsing and allows the user to interact with the web service via ASIHTTPRequest and JSONKit

Vaguely related to this, I’ve added a simple website/web service for distributing enterprise iOS apps to my BitBucket page:

It presents a list of iOS apps in an iOS-friendly website and allows them to be downloaded straight to the device. It can also be used by apps to determine the latest version number should they want to update themselves automatically.