Getting metadata for barcodes - Barcodes with iOS: Bringing together the digital and physical worlds (2015)

Barcodes with iOS: Bringing together the digital and physical worlds (2015)

Chapter 6. Getting metadata for barcodes

This chapter covers

· Modern networking with NSURLSession

· Updating Core Data databases asynchronously

· Presenting a barcode scanner modally, and using unwind segues

· Calling RESTful web services

· Unit testing web service wrappers

When you scan a barcode on a product, you end up with a GTIN (Global Trade Item Number). As an engineer, you might be marveling at the beauty of those digits, but your users will want more interesting benefits from having scanned the bars.

A traditional point-of-sale (POS) system has a local database mapping GTINs to products and their prices. But having a mobile barcode scanner in your pocket and the ability to retrieve product metadata over the internet gives you a leg up on POS. Having barcode scanning and internet connectivity together in a mobile device enables a new breed of apps that can be as niche-specific as they are product-centric.

When Apple introduced iOS 7, they gave networking a major overhaul by introducing NSURLSession. Prior to that, you had to create and configure requests individually, and you only had a global cache to work with. URL sessions take on most of the configuring work and have their own session-local caching. As an added bonus, you can perform downloads outside of your app (a.k.a. “out-of-process”) with an iOS background downloading daemon.

If you support barcode scanning in your app, you have to require at least iOS 7. And having iOS 7 as a minimum deployment target for your app means that you can also make full use of the goodness found in Apple’s new networking APIs.

Whether you’re a seasoned iOS developer or you only started developing apps after iOS 7 was introduced, you’re on the same footing as everybody else when it comes to modern networking APIs. The only difference might be that the seasoned pro has an appreciation of why the new APIs are so much more comfortable, comparing how difficult networking was in the past with how much simpler it is now.

6.1. Modern networking with NSURLSession

Strictly speaking, there’s more to the umbrella term “networking” than we’ll cover in this chapter. Here we’ll focus on performing HTTP-based downloads and uploads as opposed to communicating with other devices or dealing with iCloud. Those are also interesting topics, but they’re quite tangential to the purpose of this book, which is to enable you to build exciting niche-specific, product-centric apps that use barcodes to identify physical items.

The centerpiece of the new style of networking, introduced in iOS 7, is NSURLSession. This class is usually created based on one of multiple predefined NSURLSession-Configuration instances. Those determine the setting presets for the session. Think of the URL session object as the manager of multiple network tasks. It provides factory methods that create three kinds of tasks:

· NSURLSessionDownloadTask—Downloads a file from a web server

· NSURLSessionUploadTask—Uploads a file to a web server

· NSURLSessionDataTask—Implement a custom HTTP communication, such as for raw data transfers

You can mix and match those to your heart’s content (see figure 6.1). As long as the session lives, every task you create from its factory methods will get its settings from the session and be managed by the session. These settings determine whether requests should be performed over cellular radios, which HTTP cookie policy to use, timeout values, and whether or not responses should be cached. You configure those settings once on the URL session, and each task you create for it inherits them.

Figure 6.1. NSURLSession components

When we were young ...

Before iOS 7, you had to work with NSURLConnection objects for everything. You had to implement a class to be the delegate and then assemble multiple NSData objects into the total file. There were some synchronous and asynchronous convenience methods, but if you wanted to get the download progress or needed to deal with authentication, those tasks were far from convenient.

Many smart software engineers built networking wrappers to work around those shortcomings, such as ASIHTTPRequest by Ben Copsey, MKNetworkKit by Mugunth Kumar, AFNetworking by Matt Thompson, and DTDownload by yours truly.

NSURLSession and its related session tasks promise to make those libraries all but obsolete, much as barcode functionality since iOS 7 is making third-party scanning libraries obsolete.

6.1.1. File downloads with NSURLSessionDownloadTask

Let’s look at how simple it is to download a file from a web address with NSURLSession.

The following code snippet is part of the SimpleDownload sample app that you can find in the book’s source code. This app has a text field for a URL and a button to start the download. This is the action method linked to the button:

The basic process illustrated in this example is to create an NSURLRequest for the NSURL you want to download. Then you create an NSURLSession with one of the three predefined configurations:

· defaultSessionConfiguration—Uses default settings, for most common tasks allowing caching

· ephemeralSessionConfiguration—Specifies settings for tasks not needing caching or cookies

· backgroundSessionConfiguration—Specifies settings to allow out-of-process downloading

The biggest portion of the preceding code does various kinds of error checking. First, if there’s a transmission error due to an invalid URL or a timeout, you bail out of the completion handler. Next you check that you received an HTTP response. Conceivably—if you used a different protocol than HTTP—you might not be getting an NSHTTPURLResponse instance here, and trying to access the allHeaderFields property would cause an exception. By coding defensively you can verify that you have the correct class. Then, from the header fields, you get the content type (MIME type) of the response, and you check to make sure that you indeed received an image.

If the download occurred without issues, the downloaded file will be available at the location URL parameter, but only until the completion block returns. Because of this, you’ll want to either load the data into memory—as done here—or move the file to a more permanent location.

The last step inside the completion handler block is to switch to the main thread and set the image as the content of the image view. If there’s no valid image data, the imageWithData: would return nil and nothing would happen.

All tasks are created in a suspended state, so the last line of the method resumes the task to get the download rolling.

This is about all you need to know about basic networking as it should be done from iOS 7 onward. Next we’ll query a REST-based JSON web API using NSURLSessionDataTask.

6.1.2. Building a Music Collection app

Most people have some sort of music collection at home. If they started collecting before the advent of iTunes, there’s usually a sizable number of CDs sitting on a shelf. Hard-core audiophile enthusiasts (including my development editor) swear by the audio quality of their vinyl LP collections.

Let’s build an app for those “niche users” to help them catalog and organize their collections. Adding items to a collection by scanning them is much more convenient than having to enter all those details by hand. Our data will be provided by Discogs (www.discogs.com), a massive database of information about music, artists, and media collected by music enthusiasts. This app will teach you the best practices for creating a reusable wrapper around a RESTful web service.

Our Music Collection app will have the following features:

· The user can tap on a plus button to show the barcode scanner.

· When a barcode is detected, the barcode scanner is hidden and a new row appears in the app’s list of music media, initially showing the GTIN.

· Discogs is queried asynchronously, and if a result is found, the row is updated with the retrieved information.

· An Edit button toggles the table view into deletion mode to remove items.

· Music media is sorted into sections by genre. Inside the genre, they’re sorted by title.

· Tapping on an item will open the related Discogs page.

The finished app will look like figure 6.2 (CDs not included).

Figure 6.2. Music Collection app

Because of the size of the sample app, I won’t reproduce all source code in this chapter. Rather, I’ll highlight specific portions of interest. Please look at the book’s sample code to see it all working together.

Discogs

The main reason why I chose Discogs as the data source for this example is that all Discogs data is licensed under the Creative Commons CC0 license. This license places no restrictions on what you can do with the data, making it ideal for use in mobile applications even if you plan to make money with them.

Discogs is getting its crowd-sourced data from audiophile enthusiasts. When building and testing the sample app, you’ll inevitably encounter barcodes that can’t be found through a simple Discogs search.

The main reason for this is that some barcodes have been entered with spaces where the elongated marker bars are, and the Discogs API isn’t smart enough to ignore these spaces. For a professional app, you might want to also query for the GTIN variant including spaces, if the first query doesn’t yield a result.

Another reason—albeit much rarer—can be that the “release” embodied by the CD in your hand has not been added to Discogs yet. In this case, you have a chance to return the favor and give back to Discogs by adding the missing info.

6.1.3. Asynchronous Core Data updates

The main view of the Music Collection app consists of a table populated by a fetched results controller. This is powered by a Core Data stack consisting of these ingredients:

· DiscogsModel.xcdatamodeld—Core Data data model defining a Release entity

· Release class—Represents Release instances

· MediaListViewController—Contains the setup for the Core Data stack and fetched results controller

Please refer to the sample code’s MediaListViewController.m file to see the lazy instantiation of the objects involved in -fetchedResultController and -managedObject-Context. These lazy initialization methods store their results in two private instance variables, and on subsequent calls those are returned. The NSFetchedResultsController watches the Release entity and keeps the table view in sync with it. It’s configured to sort the entities by genre, title, and artist, with the genres also being the section titles.

The managed object context (MOC) uses the NSMainQueueConcurrencyType so that it’s usable from UIKit methods, which are usually only to be used on the main thread. For this example, you probably could get by using the MOC for all purposes, including updates. But if you had an API call resulting in a sizable number of updates, then doing this work on the main thread would be a performance bottleneck—the user would experience pauses and jerkiness while scrolling the table view if the updates were taking place at the time.

Being performance-conscious, you want to avoid doing work on the main MOC wherever possible. The following helper method creates a temporary child MOC, and once the updates are done, it lets them bubble up to the main MOC. This offloads the work to a background thread, leaving more CPU time on the main thread for UIKit:

With the preceding method in place, you can perform asynchronous updates with ease. The method returns right away, because the update occurs on the private background queue of the temporary worker context. When it’s done, the changes bubble up to the main queue MOC and are saved there asynchronously as well. Using this helper method is straightforward, as shown in the following code snippet:

[self _performDatabaseUpdatesAndSave: ^(NSManagedObjectContext *context) {

// do asynchronous work on the passed context

}];

Performance Tip

If you find yourself inserting many new entities at once, then the asynchronous pattern presented in this section might still cause noticeable pauses in the UI because Core Data locks all contexts while it’s writing to the persistent store. This means that the main MOC—used for updating the UI—has to wait to retrieve information. In this case, you should change the approach to save the worker context every couple of rows, and also save the main MOC more often. Saving smaller chunks will make these context locks less noticeable.

The temporary creation of child MOCs is a very lightweight process. Another interesting use for them is as a way to implement transactions in Core Data. Any modifications to the context are only “committed” if you call the save method. If the context is discarded without saving, the changes are “rolled back” (they never make it to the parent MOC). In a Core Data–driven app, you could implement the save/cancel functionality with that approach. If users tap the Save button, the child context is saved; if they tap Cancel, it’s discarded.

You’ll see this asynchronous update method put to good use in the next section, where you’ll use it to update scanned barcodes with metadata from Discogs.

6.1.4. Presenting the barcode scanner modally

When the user wants to add a new CD or LP to their collection, they tap the plus button in the upper-left corner of the app. There you present the barcode scanner view controller you created in chapters 2 and 3. Two things will lead to the scanner’s dismissal: the user either scans a barcode or taps the Cancel button.

One minimal change is required to DTCameraPreviewController for using it modally. You need to stop it from detecting barcodes while it’s being dismissed. You only want the first barcode detection to trigger the dismissal, whereas the detection delegate could be called multiple times during the dismissal animation, which would cause iOS to emit warnings about you trying to dismiss one view controller multiple times.

If you inspect the sample source, you’ll find the addition of an _isDisappearing instance variable that’s set to YES in viewWillDisappear:. This allows you to ignore barcode detection callbacks from AV Foundation during the dismissal.

The source code reused from earlier projects is grouped within the Copied Code group in the sample project. The main storyboard embeds the scanner view controller inside a navigation controller, as shown in figure 6.3. A modal presentation segue leads from the plus button on the root view controller to the barcode scanner. The simplest way to get this into a new project is to copy and paste it from an existing project’s storyboard editor to the storyboard editor of the new project.

Figure 6.3. Music Collection storyboard

Set the identifier of the modal segue to showScanner because you need to set the scan delegate when it’s shown. This is done in MediaListViewController, which governs the main app view with the list of music media in your collection:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

if ([segue.identifier isEqualToString:@"showScanner"]) {

UINavigationController *nav = (UINavigationController *)

segue.destinationViewController;

DTCameraPreviewController *preview = (DTCameraPreviewController *)

nav.viewControllers[0];

preview.delegate = self;

}

}

The destinationViewController of this segue is a navigation controller that has the scanner view controller at the first position of its viewControllers. This lets you retrieve a reference to DTCameraPreviewController and set the delegate to self.

Dismissing a presented view controller is also called unwinding. You can add a dummy unwind action method to the same view controller to enable the creation of an unwind segue in the storyboard editor:

- (IBAction)unwindFromScannerViewController:(UIStoryboardSegue *)segue {

// intentionally left black

}

Having a method with such a signature enables the creation of unwind segues. Back in the storyboard editor you can now Ctrl-drag from the Cancel Bar Button Item onto the Exit option, as shown in figure 6.4. A popup menu lets you select the dummy method.

Figure 6.4. Connecting the unwind segue

Making this connection creates a new unwind segue that appears underneath the green Exit symbol. Click on it and set the identifier to unwind, as shown in figure 6.5. This allows you to trigger it from code as well.

Figure 6.5. Setting the unwind segue identifier

At this point you can already cancel the scanner by tapping the Cancel button, with no extra code required.

In the next section we’ll create a wrapper class for the Discogs search API to fill in some more interesting information on the scanned item.

6.1.5. Using NSURLSessionDataTask to call RESTful web APIs

Representational State Transfer (REST) is the name-child of Roy Fielding, who coined the term in 2000 for his doctoral dissertation. There’s a lot of boring theory behind it, but for our purposes, you only need to know that web services are said to be RESTful if they adhere to these conventions:

· Methods share a common base URI (API endpoint).

· Entities and methods have distinct paths relative to the API endpoint.

· Standard HTTP methods are used for interacting with entities (GET, PUT, POST, DELETE).

· Data is represented in a common internet media type, such as JSON.

RESTful web APIs are ideally suited for mobile applications because they make use of the same mechanisms used for retrieving web pages in your mobile browser or uploading a file to a website. You don’t need to encode/decode between specific transport formats; you just need to perform simple HTTP requests. When data is returned to you—for example, as a result of doing a search—it’s most commonly in JavaScript Object Notation (JSON). As you’ll see in this section, all the building blocks for calling RESTful web APIs are provided for you since iOS 7.

For the purposes of the Music Collection app, you’ll create a DTDiscogs wrapper around the Discogs search method (http://www.discogs.com/developers/#page: database,header:database-search). The wrapper’s method will abstract the calling of the web API, verify the response, and call a completion handler. The DTDiscogs class (whose interface definition is shown in the following code snippet) will represent Discogs, and it will have a search method that searches by GTIN. (There are other search options offered by Discogs, but we’re only interested in finding items by barcode.)

The DTDiscogs wrapper class abstracts away the network request that performs the actual calling of the web API, as illustrated in figure 6.6—you don’t want to have to create data tasks and the associated checking code for every individual API method. The paradigm communicated by the method signature is that you pass a GTIN, and in a short while your completion block will be called. Then you’ll get either a nil error and an object passed as result, or if something went wrong there will be an NSError with details about what went wrong.

Figure 6.6. Discogs wrapper flow

Let’s dive into the implementation of the DTDiscogs wrapper class, which you’ll find in DTDiscogs.m in the Music Collection app.

At the top, there are a couple of definitions:

All operations on the Discogs web API are reachable through URLs relative to the Discogs API endpoint. Parameters need to be added to the URL as a query. The following helper method constructs the full method URL, adding and URL-encoding parameters as needed:

In case of errors, you’ll want to return an NSError with a message describing what went wrong. The following helper method creates such an error with the suitable error domain, code, and message:

- (NSError *)_errorWithCode:(NSUInteger)code

message:(NSString *)message {

NSDictionary *userInfo;

if (message) {

userInfo = @{NSLocalizedDescriptionKey : message};

}

return [NSError errorWithDomain:DTDiscogsErrorDomain

code:code

userInfo:userInfo];

}

URL encoding

You can only have certain characters inside a website address, because characters like the equal sign, question mark, and ampersand are imbued with specific meanings. You have to escape such characters if you want to pass them via the query portion of the URL.

Prior to iOS 7, you had to go down to the Core Foundation level and call CFURLCreateStringByAddingPercentEscapes to make strings URL-safe. Apple listened to developers who wished for a more elegant solution and added the URLQueryAllowedCharacterSet class method toNSCharacterSet, as well as a suitable category method to NSString for escaping all characters in a string that are not part of the passed character set.

These “percent escapes” consist of a percent sign followed by two hex digits. For example, a space (ASCII code 32) is represented as %20, with hex 20 being the same as decimal 32. This process of substituting characters with escape sequences—to make them URL-safe—is also commonly referred to as URL encoding.

The implementation for searchForGTIN:completion: is relatively short because the grunt work is performed in another internal method. This division allows you to easily add additional wrapper methods to the DTDiscogs class. All you have to do is specify the method path and suitable parameters:

For the purposes of the Music Collection app, you’re searching releases by GTIN. Discogs defines a “release” as “a particular physical or digital object released by one or more Artists.” This is why the type parameter is release, and the barcode search parameter is the GTIN you’re looking for.

Barcode guru tip

Many web services—including Discogs—might not find items if you’re searching for GTIN-13 barcodes. In particular, if they’re U.S.-centric or have data from a time before the grand unification, you’ll have to search for UPCs instead.

A UPC (12 digits) is represented as GTIN-13 by appending a leading 0. So if you see a 13-digit barcode with a leading zero, you can trim it off and search for the UPC instead. This approach is compatible with barcodes from outside the U.S. because there numbers other than zero are used as leading digits.

The NSURLSession for the data task comes from a property accessor. The session object is instantiated “lazily” as soon as the accessor method is accessed the first time. The designated initializer, -init, calls the secondary initializer, taking a configuration parameter passing the standard ephemeral configuration. This is the ideal session configuration for API calls, which usually have no use for local caching:

Having a secondary initializer will allow you to customize the URL session configuration for unit testing later in this chapter. But for now you can use the default initializer with the default ephemeral configuration.

The method doing the actual work is too long to display in a single listing, so we’ll first look at the overall structure and then zoom in on the processing code. The method first gathers the necessary ingredients to make the network request, and then it processes the response inside the request’s completion block:

NSURLRequest embodies the method, headers, timeout, and other values relevant to the HTTP request. To specify a different HTTP method than the default GET, you would instead create an NSMutableURLRequest, which offers setHTTPMethod: for changing it.

The first error checked for is the one reported in the data task’s completion-Handler. Those errors always relate to networking problems. For example, the internet connection might be down or the API endpoint URL might not be resolving.

After various processing and error-checking steps, the completion block is called, passing the result or error. Because all URL session tasks are created in a suspended state, you need to resume the task to have it perform the network request.

The following listings are all inserted into the marked location in the previous listing. Note that all these code snippets use the previously defined helper method for creating an NSError before calling the completion block, passing it, and then bailing out of the block via return.

First you check if the response came from the correct host. If you use OpenDNS for your network, you’ll get a status 200 response coming from www.website-unavailable.com, which is in lieu of a DNS resolution error. Granted that’s a rather unlikely occurrence, but it doesn’t hurt to have this check in place:

NSString *calledHost = [methodURL host];

NSString *responseHost = [response.URL host];

if (![responseHost isEqualToString:calledHost]) {

NSString *msg = [NSString stringWithFormat:

@"Expected result host to be '%@' but was '%@'",

calledHost, responseHost];

retError = [self _errorWithCode:999 message:msg];

completion(nil, retError);

return;

}

The URL-loading system also supports dealing with protocols other than HTTP. Because of this, the response parameter is of class NSURLResponse. To make sure that you got an HTTP response—including HTTP status code and headers—you perform the following check:

if (![response isKindOfClass:[NSHTTPURLResponse class]]) {

NSString *msg = @"Response is not an NSHTTPURLResponse";

retError = [self _errorWithCode:999 message:msg];

completion(nil, retError);

return;

}

NSHTTPURLResponse is a subclass of NSURLResponse, providing these HTTP-specific properties for your inspection. You want to be certain that the response has the correct class before you typecast the response object to NSHTTPURLResponse and access properties.

You always expect the response to be in JSON format, and Discogs will always return a JSON dictionary containing the results or an error message. If the content type isn’t “application/json” or can’t be parsed, then something must have gone wrong:

NSHTTPURLResponse *httpResp = (NSHTTPURLResponse *)response;

NSDictionary *headers = [httpResp allHeaderFields];

NSString *contentType = headers[@"Content-Type"];

if ([contentType isEqualToString:@"application/json"]) {

result = [NSJSONSerialization JSONObjectWithData:data

options:0

error:&retError];

}

You parse the JSON response in any case. When you inspect the HTTP headers, you have to consider the HTTP status code. Only codes below 400 mean that you got a successful method result. Otherwise the parsed JSON will contain the error message:

The final statement calling the external completion block concludes this extensive error checking and processing. Note that this code just forwards the parsed JSON response as opposed to creating custom model objects from it. For a larger app, you might want to do this as part of your API wrapper, but for this small example I prefer this form, as it simplifies filling in the Core Data fields in the next section.

6.1.6. Authenticating API requests with OAuth

Initially Discogs didn’t require authentication on their search endpoint. But by far the largest group of applications using the Discogs API were MP3-tagging applications that would hammer the Discogs search API with mostly nonsensical search terms found in pirated music filenames and metadata, resulting in an extraordinary waste of resources. So the decision was made to require user authentication on calls to the search function. Discogs chose OAuth 1.0a for their authentication.

OAuth has these main goals:

· To know which application is making API requests

· To know on behalf of which user these requests are made

· To know if the user has authorized the application to make those requests

· To ensure that the requests have not been spoofed or otherwise tampered with

· To facilitate an authentication flow where the application doesn’t have to save the username and password

There are two common versions of OAuth that you may encounter. Version 1.0a is the original standard, with the appended a denoting that it was slightly modified to work around a security problem.

What is OAuth 2.0?

OAuth 1.0 requires elaborate construction of an authorization header that contains a cryptographic signature. The community found that this requirement was a big hassle and difficult to get right. The HTTPS protocol already provides end-to-end encryption, so it was felt that there was no longer a need for a signature.

OAuth 2.0 is different from OAuth 1 in many ways, and these versions are not compatible with each other. This is why versions 1 and 2 continue to coexist. That’s all you need to know about OAuth 2.0 at this point.

The “classic” OAuth 1.0 authentication flow has three steps, or legs, which is why it’s also referred to as three-legged OAuth (see figure 6.7).

Figure 6.7. Three-legged OAuth flow

Of these three steps, the first and last are HTTP POST requests that you can perform without user interaction. The second leg presents a web view by loading a page from the website. There the user has to log into the service and authorize your app. The web service will then redirect to a callback URL that you can catch so you know when the web view is no longer needed.

How to get rid of the dreaded web view

OAuth was designed to work in the scenario where website A wanted to access user data on website B, so the designers saw nothing wrong with redirecting the user to a web page on B for the authorization step, and having the successful authorization redirect back to A. But this requirement to present the authorization page in a web view feels anachronistic when modern apps have great native user interfaces.

One attempt at a solution was the invention of xAuth by Twitter, which sticks with OAuth 1.0a for requests made on behalf of users. In xAuth, the username and password are simply added to the first leg’s authorization header. But this presents a trust problem, because any app that gets hold of the user’s credentials could impersonate the user. This is why Twitter now only grants xAuth privileges to a very few and select app vendors, like Apple.

So the general answer is that you can’t get rid of the web view in the second leg of OAuth 1 unless somebody at the service you’re trying to access owes you a favor.

6.1.7. Adding DTOAuth to your project

Because Discogs uses OAuth 1.0a for their authentication, you’ll need to implement this in your DTDiscogs wrapper. DTOAuth is an open source project (available on GitHub: https://github.com/Cocoanetics/DTOAuth) that contains all the code necessary to add OAuth 1.0a support to your app.

Due to the complexity of the cryptographic signature, I’m providing the DTOAuth component with the Music Collection sample code in the Externals folder. You’ll include it as a subproject and link the static library target from it into your app binary to access its functionality.

Create an Externals subfolder in your project structure, and copy the contents of the DTOAuth project there. Then add a reference to DTOAuth.xcodeproj in the app project. Figure 6.8 shows the referenced Xcode subproject inside the Music Collection project.

Figure 6.8. DTOAuth subproject

Having an Xcode project referenced as a subproject gives you access to all its targets. Add the static library target both as a dependency and in the linker build phase, as shown in figure 6.9. This tells Xcode that your app needs to be rebuilt if there’s a change in the DTOAuth project. The linker build phase merges the compiled object code from inside the libDTOAuth_iOS.a static library with your app binary.

Figure 6.9. DTOAuth added as dependency

To enable Xcode to find the DTOAuth headers inside the subproject, you need to add the folder where you put the DTOAuth source code to your User Header Search Paths build setting: ${SOURCE_ROOT}/Externals/DTOAuth. Choose the recursive option so that the indexer will also look in subfolders of this location.

6.1.8. Configuring the OAuth consumer

To participate in OAuth, you need to create an application on the Discogs developer website (https://www.discogs.com/settings/developers). Your app is referred to as the consumer; take note of the consumer key and consumer secret values. In terms of nomenclature, the key is generally public and will be passed around, whereas the secret is kept private and is used for signing stuff. You should take care to never post your pair to a public source code repository, because this would enable somebody with malicious intent to impersonate your app when talking to the service.

The authentication flow state information is encapsulated in DTOAuthClient; DTDiscogs requires an instance of it for signing web API HTTP requests. This instance will be created in a lazy property accessor method.

First up, you need a property definition in the DTDiscogs header. Here are the two additions you have to make:

Moving on to the DTDiscogs implementation, you need an import for DTOAuthClient.h, and then you can create a lazy property initializer (shown in the following code) that takes care of setting up the consumer the first time the property is accessed. When initializing the OAuth client object, you have to pass the consumer key and secret to it. It also needs to know the base URLs for the three legs, as these can be different from service to service:

Now you need to add an Authorization header to all your Discogs API requests. At the beginning of the _performMethodCallWithPath:parameters:completion: method, add the following code, which adds the Authorization header if the OAuth client possesses a valid access token (that is, if isAuthenticated is true):

This is all you need to do within the DTDiscogs wrapper class to add OAuth. Next you need to hook up the search function to the barcode scanner and, if necessary, carry out the OAuth flow.

6.1.9. Implementing the UI for OAuth authorization

You want to show the OAuth authorization web view (second leg) at the latest possible moment—after the user has successfully scanned a barcode and before you execute the search on Discogs. The flowchart in figure 6.10 illustrates the decision about whether or not carrying out the OAuth flow is required.

Figure 6.10. To OAuth or not to OAuth

When the scanner view controller finds a valid GTIN, it calls the delegate method and is dismissed with a slide-out animation. During this animation, you can’t present any new view controller, such as the DTOAuthWebViewController provided for the second leg by DTOAuth. You have to delay handling the scanned code until the dismissal animation has ended.

Right after triggering the presentation or dismissal of a view controller, you can get a reference to the responsible transition coordinator. This coordinator offers a method to animate something in parallel with the animation and also provides a handy completion block that you can use.

The following code is called by the DTCameraPreviewController when a new barcode is scanned, showing how the handling of the scanned code is delayed until the transition animation has completed:

You create a helper method that carries out the OAuth flow on the right side of the flowchart in figure 6.10. Each step has a completion handler, and if the step was successful, the next step is carried out. Interspersed are abort conditions that cancel the flow as soon as one step fails:

If you make it to the very bottom of the code, then all three legs were successful and the passed block can be executed. This is where you can execute the Discogs search following the OAuth authorization flow.

6.1.10. Connecting barcode scanning and metadata retrieval

You can now fill in the _handleScannedCode: method. This method first creates a new blank Release object in the database via the following helper method:

- (Release *)_insertNewReleaseWithGTIN:(NSString *)GTIN {

// create a new Release object and fill in barcode

Release *release = [NSEntityDescription

insertNewObjectForEntityForName:@"Release"

inManagedObjectContext:_managedObjectContext];

release.barcode = GTIN;

release.genre = @"Unknown";

NSError *error = nil;

if (![_managedObjectContext save:&error]) {

NSLog(@"Unresolved error %@, %@", error, [error userInfo]);

abort();

}

return release;

}

The error handling in this example is intentionally crude. Aborting the entire application is frowned on in real-life apps.

If the user is already authorized, then you can search Discogs and update the new Release object right away. If not, this will have to wait until after successful completion of the authorization flow:

The helper method _performSearchAndUpdateRelease: queries Discogs and—if there was a hit—updates the freshly inserted Release object:

The update code is grouped in _updateRelease:fromDictionary:, and it uses the background-context update paradigm introduced earlier:

NSManagedObjects—like the Release object passed as a parameter here—can only be used with the managed object context they came from. But you can get a copy of the object suitable for another context via the MOC’s objectWithID: method. The managed object’s objectIDproperty is the only one you can safely access from another thread. Inside the block of the update method, you’re on the private queue of the worker context.

Discogs returns album title and artist behind the “title” key of the result dictionary. You then need to separate the values at the dash character and trim off any remaining whitespace. The genre, style, and format values are arrays from which you always take the first value.

_performDatabaseUpdatesAndSave takes care of saving the changes. It first saves the worker context and then also saves the main MOC. This, in turn, triggers an update on the fetched results controller, which then configures the table view row for this entity with the new values.

MediaListViewController configures cells with incomplete information to display the GTIN and “No info found.” As soon as the title property of the shown Release object becomes non-nil, the labels are populated accordingly. Figure 6.11 shows this progression from prototype cell to complete entry.

Figure 6.11. Filling in the blanks from Discogs

We haven’t gone into how the table view rows are configured for every Release—that’s typical boilerplate code that you’re likely very familiar with. Please refer to the MediaListViewController.m sample code to see how this is set up.

6.2. Unit-testing network operations

In the previous section, you created a class that wraps the Discogs web service API. In pro parlance, such a class is also referred to as a unit, because it has a minimum of external dependencies. Really, the only things it depends on, outside of the networking code, are classes and types defined in Apple’s frameworks. In other words, it’s a perfect candidate for unit testing.

Generally speaking, you want to create a sufficient number of tests to make sure that assumptions made about a unit hold true even if code inside the unit is changed. The header of a class is like a contract you make with people who use the class, so I like to keep the headers as simple as possible and also to add lots of commentary about what parameters are valid. Unit tests are a form of such comments that can be automatically tested by a machine.

Imagine that your API wrapper class is used by a larger team of developers and that you refactor some crucial bits, accidentally changing some behavior that another team member is relying on. A unit test that tests such an assumption would flag this change and save you from the embarrassment of checking faulty code into the shared source repository.

Manually testing all areas of an app under development grows tedious even for the most patient human being. Unit tests are usually performed by a machine—a build server or your own Mac—with unfaltering precision and endurance.

Now let’s get down to brass tacks and create some unit tests for DTDiscogs.

6.2.1. Introducing NSURLProtocol

The difficult part of testing anything to do with networking is that network connectivity might change from one run of unit tests to the next. This presents a bit of a problem. If your unit tests work one time because the remote server is reachable, but they fail subsequently because the internet connection is down, this can be a source of stress for you as a developer. You don’t want unit tests to fail just because the environment changed.

Note

Testing how a unit interacts with the environment is a different kind of test: an integration test. This might also be an interesting thing to test, but in this section we’re exclusively looking at ensuring that a unit is and will keep working flawlessly.

The actual network operation happens deep under the hood of Apple’s URL-loading system. Even with modern networking via NSURLSession, Apple is building on the tried and true networking foundations that remain the same. In the flowchart in figure 6.12, the DTDiscogs piece is the system under test, but triggering a search will cause an execution cascade through several system classes. The part where there’s communication via the internet is where it becomes fragile.

Figure 6.12. The internet as a variable

An NSURLRequest represents a request to be made via any TCP/IP-based network connection. It can be for any kind of protocol as expressed by the request’s URL scheme. When the connection is to be established, the operating system will query all installed NSURLProtocol subclasses to find one that’s willing and able to accommodate the request. The system provides only two: NSAboutURLProtocol, which handles requests for the about: URL scheme, and NSCFURLProtocol, which handles all http: and https: requests. The default behavior is that the system-provided NSCFURL-Protocol will win the fight and be the one to perform the actual communication with the internet.

The great thing is that this system can be adapted and extended by developers. You can install a protocol for your own foobar: scheme or replace the system’s handling of HTTP requests.

6.2.2. Implementing a custom URL scheme with NSURLProtocol

Let’s create a custom NSURLProtocol. In this section we’ll implement one to handle the foobar: URL scheme. Whenever there’s an image referenced by a URL like foobar://oliver.jpg, our app should load this from the app bundle instead of the internet. This LocalImageProtocolexample project will implement the foobar: custom URL scheme for use with a UIWebView.

First you need to create an NSURLProtocol subclass and name it LocalImage-Protocol. You need to implement the class method by which the system will ask your class if it’s interested in handling requests:

The second class method you need to implement is one that makes requests canonical. The point of it is to transform multiple variations of a URL, which all refer to the same resource, into the same canonical URL. This is only really used if you allow caching, because you’d want to cache each retrieved resource only once, with the canonical URL being the cache key. In our example, we won’t worry about this cache deduplication, so we can simply return the original request:

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {

return request;

}

If your class responded to the +canInitWithRequest: affirmatively, the system will create an instance of your class to handle the specific request. On this instance, you have a property referencing the request, as well as a client property that more or less represents theNSURLConnectionDelegate. You implement the -startLoading method to carry out the actual work, and communicate with this delegate, informing it about the various stages as you carry them out.

Note the use of NSURL’s resourceSpecifier in the following code for retrieving the filename. Because you don’t have any host and path components in the foobar URL, the entire string after the scheme becomes the resource specifier:

If there’s no image in the app bundle with this resource name, you’ll send an error to the object that has been registered as being interested in progress and status updates from the loading process, also called the URL-loading client. In this case, you also have to implement the -stopLoadingmethod, even if it does nothing. Without this dummy implementation, there will be an exception stating that this method is only implemented in the abstract superclass:

- (void)stopLoading {

// nothing to do, but still needs to be implemented

}

The method to handle the error case is just as simple:

Finally, you implement the _sendImageAtPath: helper method that sends the found image to the URL-loading client and messages the appropriate methods of the NSURLProtocolClient protocol to inform the client about the start, received data, and finish of the loading process:

If you wanted to, you could also add HTTP headers, such as to specify a content type, but this example works fine without them.

The protocol implementation is now finished. To install it for use by the URL-loading system, add this line to your app delegate:

[NSURLProtocol registerClass:[LocalImageProtocol class]];

If you run the LocalImageProtocol sample app, you’ll see a web view loading an HTML string with an embedded image (see figure 6.13). This image is loaded from the app bundle via the foobar:Oliver.jpg URL because now there’s a protocol to handle it.

Figure 6.13. LocalImageProtocol sample app

This technique can be used to implement any kind of URL protocol. In the following section, we’ll build a mechanism for stubbing network requests with it.

6.2.3. Stubbing NSURLRequest responses with DTURLProtocolStub

In the Music Collection app, you’ll find DTURLProtocolStub, which works much like LocalImageProtocol but with a few more bells and whistles. Like the app in the previous section, it’s a simple subclass of NSURLProtocol. Instead of repeating the explanation of how it works, let’s do something more exciting: eliminate the internet as a variable by simulating network requests.

Test stubs are programs that simulate the behavior of components that a module undergoing tests depends on. In this case the tested module is the DTDiscogs wrapper class, and the component it depends on is the Discogs web service. By specifying a variety of canned answers, you can create unit tests covering all sorts of scenarios, including those that are extremely rare in real life.

The DTURLProtocolStub class, which is provided as part of the Music Collection sample app, lets you stub HTTP network requests. To use this class in your own unit tests, copy the headers and implementation files for DTURLProtocolStub and DTURLProtocolResponse to your project, and make sure that the .m files are compiled for the unit test target.

Finding the Unit Test Source Code

All the code mentioned in this section—along with a few more tests—can be found in DiscogsQueryTests.m, which contains the Music Collection app unit tests.

Let’s assume you have a component that depends on a GET request to www.apple.com. You want to test one scenario where the response works with an HTTP status code of 200 (OK). Then you want to see that your code works correctly if—however unlikely—you get a code of 404 (Not Found).

The stubbing class is designed to provide canned responses based on criteria you define. Those criteria are specified in a test block that receives a reference to the NSURLRequest object for which the loading should occur. The test block can inspect properties of the URL request, like the URL, and if it returns YES the response should be returned. Specify a NULL test block to have the response always be delivered.

Having imported the stub header, you can add code to simulate the successful request. Response tests are executed for each request in the same order they were registered with the DTURLProtocolStub class. The following example installs a stub for requests to the Apple domain:

This unit test uses a convenience method of DTURLProtocolStub that provides a plain text response for all requests to the www.apple.com URL. A complete test case method incorporates several unit test assertions to determine if the result is indeed what was expected.

All methods on DTURLProtocolStub are class methods, because you have no reference to the individual protocol object instance that the system will create for the NSURLRequest. You need to install the stubbing protocol class in the -setUp method of your test case class:

This setup method is called before each individual test case method. Because of this, you need to remove the responses left over from the previous test. The +setMissingResponseBlock: method warns you if you forgot to provide a response for a specific scenario. Figure 6.14 shows thetest-FakeApple unit test passing because all assertions on the URL response object are passing.

Figure 6.14. Fake Apple unit test passing

For the scenario where the call to Apple needs to return a 404 instead, you specify the appropriate response:

NSString *string = @"404 Not Found";

[DTURLProtocolStub addPlainTextResponse:string statusCode:404

forRequestPassingTest:NULL];

If you pass NULL for the test block, this 404 response is sent for all requests. This is more convenient if you only have a single stubbed response. You have both tools at your disposal: the fine chisel (fine-grained test blocks) to target a variety of requests all getting different responses, or the heavy hammer (a NULL test block) that responds with the same content regardless of the request. Use the one that best suits the task at hand.

Another good test to have is one that checks how the URL connection (and your error handling) would deal with an offline internet connection:

NSError *offlineError = [NSError errorWithDomain:@"NSURLErrorDomain"

code:-1009

userInfo:nil];

[DTURLProtocolStub addErrorResponse:offlineError

forRequestPassingTest:NULL];

In real life—outside of unit testing—iOS messages the connection:didFailWithError: delegate method for a variety of transport errors, including -1009 for an offline connection. The preceding convenience method lets you specify the NSError that will be returned to the delegate. Which error this is doesn’t usually matter unless your error-checking code relies on a specific error code or message.

Note

Don’t confuse HTTP errors with connection/transport errors. Even an HTTP status code 404—for a web page that can’t be found—is considered to be a successful connection. Connection or transport errors, on the other hand, occur if there’s no internet connection, if DNS can’t resolve the host name, or if the connection times out. Successful connections result in didFinishLoading and unsuccessful ones in didFailWithError:.

DTURLProtocolStub has one more trick up its sleeve: you can record the individual NSURLRequest before it’s treated with a response. This is useful if you want to make sure that the URL has been constructed in a certain way by your wrapper class.

Place the following code into your test case setUp to have subsequent requests added to an array. This enables you to inspect the requests after the simulated network operations to assert that only those that you expected were added:

You’ve seen DTURLProtocolStub in action for “classic” NSURLConnection-based networking. Next we’ll look at how to use it together with NSURLSession.

6.2.4. Stubbing NSURLSession requests with DTURLProtocolStub

Before iOS 7, installing a protocol handler would always affect all network connections. In iOS 7 and later, NSURLSession doesn’t use the global protocol registry. Instead, the list of active NSURLProtocol classes is kept by NSURLSessionConfiguration instances.

In section 6.1.5 you used the default ephemeral session configuration to perform the Discogs query. You might also remember creating a secondary initializer for DTDiscogs that takes a session configuration as a parameter. This was done deliberately to allow you to modify the list of protocol handlers.

The MusicCollection unit tests implementation has the following helper method that modifies the session configuration to install your protocol stubbing class:

- (NSURLSessionConfiguration *)_testSessionConfiguration {

NSURLSessionConfiguration *config =

[NSURLSessionConfiguration ephemeralSessionConfiguration];

config.protocolClasses = @[[DTURLProtocolStub class]];

return config;

}

The test case -setUp creates a fresh instance of DTDiscogs in an instance variable that uses this configuration:

NSURLSessionConfiguration *config = [self _testSessionConfiguration];

_discogs = [[DTDiscogs alloc] initWithSessionConfiguration:config];

This causes all subsequent searchForGTIN:completion: calls to be handled by the protocol stubber, allowing you to specify a variety of responses for all the test cases you can dream up. Because the responses coming from Discogs are JSON, there’s a convenience method to have the response data come from a file path. Having the JSON come from files is much more convenient than having to specify the JSON as long strings in your unit test code.

Instead of configuring a single cover-all response like in the previous section, we’ll now set up a cascade of tests and responses where each subsequent test is broader than the previous one. The following three code snippets form the _setupProtocol-StubForSearch setup method, which can be reused for several test cases.

The first code snippet configures a successful response to a search for a specific Queen album by GTIN. The response data comes from a JSON file inside the unit test bundle:

If the test for the first response doesn’t match, then a second test returns a “not found” response to a search request:

If the API function URL points to an incorrect path on the Discogs server, it will respond with a “resource not found” error, with status code 404. We’ll use this as the final response for all requests not handled by the previous two tests. This way you can also test whether the code can deal with this occurrence:

path = [self _pathForResource:@"resource_not_found"

ofType:@"json"];

[DTURLProtocolStub addResponseWithFile:path statusCode:404

forRequestPassingTest:NULL];

All three JSON files can be found in the Music Collection project. They were created by saving the responses Discogs sent for these scenarios. Having them as files is much more convenient than constructing NSDictionary instances and then encoding these as JSON.

6.2.5. How to test asynchronous completion handlers

The DTDiscogs wrapper is constructed so that when you call the search method, you pass a completion block. This block will be called after the networking operations and subsequent validation are finished. That means it will be executed on a separate background queue and might also take a short while to complete.

Unit tests are executed on the main queue, so you need a way to pause execution until the completion block is called. Otherwise the test case would continue on and finish while the completion block is still executing. This pause mechanism is provided by Grand Central Dispatch (GCD) in the form of semaphores.

You create a semaphore in -setUp amongst the other initialization code you want to execute for each test:

_requestSemaphore = dispatch_semaphore_create(0);

Two helper methods call two semaphore functions. Their only purpose is to provide a name describing what they’re used for:

In practice, you wait on the semaphore right after the call to the search function. Inside the completion block, you unlock the semaphore as a last action.

Here’s the unit test for the scenario where a Queen album is found:

6.2.6. Shifting to test-driven development

In this chapter we first developed the DTDiscogs unit, and then we implemented unit tests for it. I can’t give you more than a few thoughts on the broad subject of unit testing, but I wanted to show you how to deal with the tricky bits relevant to our subject matter: eliminating the internet as a variable in your test setup, and testing asynchronous code.

The basic approach to unit testing—which I demonstrated in this chapter—is to create the implementation first and then create unit tests for it. The unit tests nail down the assumptions you make about your code’s output. Should you ever change your implementation to give different output, you’d notice this through failing unit tests. Unit tests serve as coal mine canaries in this regard.

Another approach I often take is to implement new unit tests for bugs I fix. This makes sure that I’ll never undo a fix for an already-fixed bug while fixing a new one. As apps grow more complex, you’re bound to lose the ability to foresee all the side effects that new code or changes will have on all other areas.

The reverse approach is to write the test first, knowing full well that it’s failing. Then you implement the minimum behavior that will allow the test to pass. Then write another failing test, rinse, and repeat. This approach is referred to as test-driven because the creation of tests drives the implementation of features.

The main advantage of test-driven development (TDD) is that you don’t have to think up appropriate unit tests for all branches in your implementation code after the fact. Once you have a working program, you’ll most likely also have a reasonable set of unit tests.

Whichever of these styles better suits your taste is up to you. But however you decide, unit tests will help you create better code—you can never have too many of them.

6.3. Summary

You now know that your users can scan barcodes on mobile devices that are connected to the internet by means of 3G cellular data or WiFi networking. Now you can start thinking about interesting usage scenarios. Can you think of a situation where it would be orders of magnitude more convenient to scan a product barcode than to enter multiple bits of information?

This chapter’s sample app demonstrated how you can use barcode scanning to let a user add music media to a collection. As a Core Data–based app, it displayed the music collection in a table list view, governed by a fetched-results controller. It introduced modern networking embodied byNSURLSession and demonstrated how to create a convenient wrapper class around the Discogs web service API.

You’ve learned a method for offloading Core Data updates to a temporary worker context, and you’ve also seen how unwinding segues are created and used. These are two skills that are tangential to the main goal of this chapter—retrieving metadata for scanned barcodes—but they’re nevertheless quite useful to know.

In the section on unit testing, we explored a technique for stubbing server responses for NSURLConnection and NSURLSession. For both of them, you can install a custom NSURLProtocol to dish out responses. Then you saw how to synchronize test execution and asynchronous completion handlers by using a dispatch semaphore.

These are the key takeaways for this chapter:

· NSURLSession-based networking and barcode scanning were both introduced in iOS 7.

· You can create a URL session with the most suitable of three default configurations: default, ephemeral, or background.

· Use NSURLSessionDownloadTask for downloading files and NSURLSession-UploadTask for uploading.

· Avoid performing update operations on a main-queue-based managed object context. This can lead to UI stuttering.

· NSURLSessionDataTask, in combination with the ephemeral configuration, is ideal for web API calls because you typically don’t need any result caching there.

· You should abstract the network operations for interacting with a RESTful service into a wrapper class.

· You should check for all possible kinds of errors when dealing with the result of a web API call.

· You should unit test any networking wrapper code you write. Whether you prefer to code first and then formulate tests or follow TDD is up to you. But you can never have too many unit tests.

· Simulating network responses eliminates the internet as a variable and also lets you test how your code can deal with a greater variety of responses.

This chapter elaborated on using the always-on internet of modern smartphones to retrieve metadata based on scanned barcodes. In the next and final chapter of this book, you’ll see how you can use the user’s current geographic and semantic context to further enhance your barcode-scanning apps.