Lesson 27

Consuming RESTful JSON Web Services

A web service is essentially a web application that runs on a web server and provides a list of methods that allow users to access server-side resources. These resources can be web pages, business data, images, or video files. You access the web service as you would any other website using a URL.

Web services themselves can be written using one of several technologies including Node.js, PHP, ASP.NET, and ColdFusion. Creating a web service is outside the scope of this book.

The examples in this lesson use a simple web service called MathService. Table 27.1 lists the operations supported by the web service, the web service end point, and a brief description of each.

Table 27.1 MathService Methods

Method Name Endpoint URL Supported HTTP Operations Description
CircleArea www.asmtechnology.com/MathService/CircleArea/?radius=X GET Input: radius
Output: Returns the area of a circle with specified radius.
RectangleArea www.asmtechnology.com/MathService/RectangleArea/?length=X&breadth=Y GET Input: length, breadth
Output: Returns the area of a rectangle with specified length and breadth.
SquareArea www.asmtechnology.com/MathService/SquareArea/?length=X GET Input: length
Output: Returns the area of a square whose sides are of specified length.
TriangleArea www.asmtechnology.com/MathService/TriangleArea?base=X&height=Y GET Input: base, height
Output: Returns the area of a triangle with specified base length and height.

Types of Web Services

There are two kinds of web services:

  • RESTful
  • SOAP

RESTful Web Services

REST is an acronym for Representational State Transfer. REST is an architecture style, primarily used to build lightweight, scalable web services. Each server-side resource that is exposed by a RESTful web service will have at least one URL. It is quite common for RESTful web services to return responses in either XML or JSON formats with the latter gaining popularity in recent years.

A RESTful service URL resembles a directory-like structure and identifies a resource or collection of resources as objects. A key differentiating point between web services that are RESTful and those that aren't is how resources are accessed.

When a resource is accessed through a RESTful web service, the actual operation that will be performed on the server is determined by the HTTP verb specified when making the request. The response from the server includes a status code that can be inspected to determine success or failure. A list of HTTP status codes can be found at http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html.

The most common HTTP verbs are GET, POST, and DELETE. The same URL can be called with different HTTP verbs to perform different operations. This is different from non-RESTful web services where one would have a different URL for each operation that is to be performed on the server.

For instance, if you had a database of employees on a server that you wished to expose publicly with a RESTful web service, the URL to identify an employee (with unique identifier of 1790716) would resemble the following:

http://www.example.com/Employee/1790716

If you were to send an HTTP GET request to this URL, the service would typically return some information about that particular employee. If, on the other hand, you were to send an HTTP DELETE request to the same URL, the service could potentially delete the employee record.

Needless to say, web service designers carefully decide which operations will be supported on a web service.

SOAP Web Services

SOAP is an acronym for Simple Object Access Protocol. It is an XML-based message format, which allows different applications to exchange objects with each other. SOAP web services often contain a machine-readable description of the functions exposed by the web service written in WSDL (Web Services Description Language). SOAP web services are generally used when communicating between enterprise applications. SOAP requests and responses are larger than equivalent RESTful versions and are therefore not suited to processing on mobile devices.

JSON and NSJSONSerialization

JSON is an acronym for JavaScript Object Notation and provides constructs that allow you to conveniently serialize objects to UTF-8 text. It is used primarily to communicate between servers and clients as an alternative to XML.

JSON is preferred over XML because JSON data takes fewer bytes to represent the same information. For example, the following snippet shows how a collection of Organization objects would be encoded in JSON and XML. As you can see, the JSON representation is more compact, which translates to fewer bytes being sent over the network.

{"organizations":[
  {«name»:"ACME Corportation", "address":"112, Fleming Drive, LE3 4F6, UK"},
  {«name»:"Bright Ideas LLC", "address":"26, Syon Lane, London TW3 3P2"},
  {«name»:"Chromatic Inks Ltd", "address":"178, Lexuar Drive, Langley, SL6 3U0"}
]}
<organizations>
  <organization>
    <name>ACME Corportation</name>
<address>112, Fleming Drive, LE3 4F6, UK</address>
  </organization>
  <organization>
     <name>Bright Ideas LLC</name>
<address>26, Syon Lane, London TW3 3P2</address>
  </organization>
  <organization>
    <name>Chromatic Inks Ltd</name>
<address>178, Lexuar Drive, Langley, SL6 3U0</address>
  </organization>
</organizations>

NSJSONSerialization is a class that is part of the Foundation framework and can be used to convert JSON objects into Foundation objects and vice versa. NSJSONSerialization requires that the top level object is an array or dictionary and that all objects in the JSON are either strings, numbers, arrays, or dictionaries. Table 27.2 lists the mapping between Foundation types and JSON types

Table 27.2 Mapping Foundation to JSON Types

JSON Type Foundation Type
Array NSArray
Dictionary NSDictionary
String NSString
Number NSNumber

Assuming you have an NSData instance that contains JSON objects, you can convert them to Foundation objects using the JSONObjectWithData class method:

class func JSONObjectWithData(_data: NSData, options opt: NSJSONReadingOptions)
           throws -> AnyObject

The first parameter to this method is an NSData instance that contains JSON objects, encoded using either UTF-8, or UTF-16. The second parameter is used to specify how Foundation objects are generated from JSON objects, and can be a combination of the following:

This method returns a non-optional result and will throw an error if it was unable to parse the JSON input. The following snippet shows how you would use this method:

do {
    let JSONObject = try NSJSONSerialization.JSONObjectWithData(data!,
                     options: .MutableContainers)
    // use JSONObject
}
catch {
    // handle exceptions.
}

NSURLSession and Application Transport Security

NSURLSession refers to a class from a set of related classes that allow you to download content via HTTP. It was introduced in iOS7 and supersedes the NSURLConnection API. The new NSURLSession API is asynchronous by design and provides several improvements over NSURLConnection including the following:

  • Support for HTTP2.0 out of the box
  • Support for per-session cache, cookies, and auth credentials
  • Support for background downloads

With the NSURLSession API, your app can create multiple sessions, with each session coordinating a group of data transfer tasks. A session is analogous to a tab in a browser window, and a data transfer task to a request to fetch a single resource such as an image. Within each session you could hit the server multiple times for different resources. Each session has its own cache and HTTP security credentials.

Some of the key classes in the NSURLSession API are:

  • NSURLSession: Represents a session object
  • NSURLSessionConfiguration: Represents a configuration object used while creating a session object
  • NSURLSessionDataTask: Represents a task for retrieving the contents of a URL as an NSData object
  • NSURLSessionDownloadTask: Represents a task for retrieving the contents of a URL as a temporary file on the disk
  • NSURLSessionUploadTask: A task for uploading an NSData object to a file
  • NSURLSessionStreamTask: A task that lets you communicate using raw TCP/IP sockets. This is useful if you want to use a protocol other than HTTP/HTTPS such as IRC.

The API also provides several protocols that define delegate methods you can implement in your code for better control over session and task behavior:

  • NSSessionDelegate: Contains delegate methods to handle session level events
  • NSURLSessionTaskDelegate: Contains delegate methods to handle events common to all task types
  • NSURLSessionDataDelegate: Contains delegate methods to handle events specific to data and upload tasks
  • NSURLSessionDownloadDelegate: Contains delegate methods to handle events specific to download tasks
  • NSURLSessionStreamDelegate: Contains delegate methods to handle events specific to stream tasks

Creating an NSURLSession

To create an NSURLSession instance, you must first create a session configuration; use the defaultSessionConfiguration() method of the NSURLSessionClass:

let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()

Once you have a session configuration, you can fine-tune it by editing some of its attributes, including the following:

  • allowsCellularAccess: A value that indicates if the request should proceed over cellular networks
  • timeoutIntervalForRequests: A value that will cause a timeout if no data is transmitted after this interval has elapsed
  • HTTPAdditionalHeaders: Additional headers for outgoing HTTP requests
  • HTTPMaximumConnectionsPerHost: Limits the maximum number of simultaneous connections to a server

The following code snippet shows how to set a custom HTTP request header to support HTTP basic authentication in your code:

let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
let userPasswordString = "username@yourcompany.com:password"
let userPasswordData = userPasswordString.dataUsingEncoding(NSUTF8StringEncoding)
let base64EncodedCredential =
userPasswordData!.base64EncodedStringWithOptions(
NSDataBase64EncodingOptions.Encoding64CharacterLineLength)
let authString = "Basic \(base64EncodedCredential)"
configuration.HTTPAdditionalHeaders = ["Authorization" : authString]

To learn more about how HTTP basic authentication works, refer to the section “Basic Authentication Scheme” in the HTTP reference documentation at http://www.w3.org/Protocols/HTTP/1.0/spec.html#BasicAA.

Once you have a session configuration, you can create a session using the following:

let session: NSURLSession! = NSURLSession(configuration: configuration,
                             delegate: nil, delegateQueue: nil)

The delegate is optional, and is an object that implements the NSURLSession protocol. The final parameter, delegateQueue, is a queue for scheduling the delegate calls and completion handlers. If nil, the session creates a serial operation queue. It is important to note that your delegate methods (or completion handler) will be called on this queue. If you intend to update the user interface within these callbacks, then you must make sure that the code that updates the UI is called on the main queue.

One way to do this is by using NSOperationQueue.mainQueue() as the final parameter while creating the NSURLSesssion:

let session: NSURLSession! = NSURLSession(configuration: configuration,
                             delegate: nil,
                             delegateQueue: NSOperationQueue.mainQueue())

Creating a Data Task

If you wanted to call a method on a web service, chances are that you will end up using a data task. To create a data task, you can use any of the following methods of the session object.

dataTaskWithRequest(urlRequest)
dataTaskWithRequest(urlRequest:completionHandler:)

There are two versions of this method. Use the first one if you want to supply a delegate object; the second one takes a block that is called when the request completes. The following code snippet shows how you could create a data task with a completion handler:

let task : NSURLSessionDataTask! = session.dataTaskWithRequest(request,
                         completionHandler: {(data, response, error) in
});

The first parameter to both these methods is an NSURLRequest instance. An NSURLRequest instance allows you to specify not only the URL but also additional information such as the request type (GET/POST). NSURLRequest defaults to creating a GET request, but Apple provides a mutable version called NSMutableURLRequest that allows you to set the content type. In general, it is common practice to use NSMutableURLRequest in your code as you always have the option of changing the request type when needed.

The following code snippet shows how you could create an NSMutableURLRequest from a URL and set the request type to POST:

let serviceURL:String =
"http://www.asmtechnology.com/MathService/CircleArea/?radius=\(radius)"
let url:NSURL! = NSURL(string: serviceURL)
let request: NSMutableURLRequest = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"

Once you have created an appropriate task, you need to call its resume() method to begin it:

task.resume()

Application Transport Security

Application Transport Security (ATS) is a new feature in iOS 9 that helps prevent accidental disclosure of data while making network requests by encouraging your application to make secure connections to web services.

By default, ATS prevents calls to HTTP URLs. In fact, even if you provided an http:// prefix to your URL, The NSURLSession API will end up making an HTTPS call. This is fine if your server supports HTTPS, but if it doesn't, then you will need to configure an exception for one or more URLs in your application's Info.plist file.

To configure ATS, you need to add a new key to your Info.plist file called NSAppTransportSecurity and set its type to be a Dictionary.

To disable ATS completely (not recommended), thereby allowing all http:// URL requests to pass through, add a new Boolean value to this dictionary called NSAllowsArbitraryLoads and set its value to YES (Figure 27.1).

Screenshot of Running RESTClient on iPhone6 with the Key NSAllowsArbitraryLoads set to Boolean in Type and YES in Value, encircled.

Figure 27.1

If, on the other hand, you want ATS to allow only specific insecure connections, you need to use a different key called NSExceptionDomains (which is also a dictionary). Within this dictionary, you can configure exceptions on a domain basis.

For each domain you want to configure, you need to provide a child dictionary within the NSExceptionDomains dictionary. This domain-specific configuration dictionary can have any of the following keys:

  • NSIncludesSubdomains
  • NSExceptionAllowsInsecureHTTPLoads
  • NSExceptionRequiresForwardSecrecy
  • NSExceptionMinimumTLSVersion

For instance, if you wanted to allow insecure http requests for all URLs in the asmtechnology.com domain, your Info.plist file would resemble Figure 27.2.

Screenshot of Finished running RESTClient on iPhone 6 with items under the Key NSExceptionDomains encircled with the Type and Value.

Figure 27.2

In the next session you will see how to put all of this together to call a web service.

Try It

In this Try It, you create a simple iPhone application based on the Single View Application template called RESTClient, which uses a RESTful web service (described in Table 27.1) to calculate the area of a circle.

Lesson Requirements

  • Launch Xcode.
  • Create a new iPhone project based on the Single View Application template.
  • Add a UIButton to the default scene and an appropriate action method to the view controller class.
  • Add a UITextField to the default scene and an appropriate outlet to the view controller class.
  • Add a scrolling UITextView to the default scene and an appropriate outlet to the view controller class.
  • Dismiss the text field when the Return button is pressed on the keyboard by implementing a UITextFieldDelegate method.
  • Send a GET request to a RESTful web service when the UIButton is pressed.
  • Parse and display the response in the UITextView.

Hints

Step-by-Step

  • Create a Single View Application in Xcode called RESTClient.
    1. Launch Xcode and create a new application by selecting File arrow New arrow Project.
    2. Select the Single View Application template from the list of iOS project templates.
    3. In the project options screen, use the following values:
      • Product Name: RESTClient
      • Organization Name: your company
      • Organization Identifier: com.yourcompany
      • Language: Swift
      • Devices: iPhone
      • Use Core Data: Unchecked
      • Include UI Tests: Unchecked
      • Include Unit Tests: Unchecked
    4. Save the project onto your hard disk.
  • Add UI elements to the default scene.
    1. Open the Main.storyboard file in the Interface Editor
    2. From the Object library, drag and drop a button, text field, and text view objects onto the scene and place to resemble Figure 27.3.
      Screenshot of storyboard scene with text field, text view objects placed, with the words Compute Area of Circle in a gray box.

      Figure 27.3

    3. Select the Text field and use the Attribute inspector, to change the value of the Placeholder attribute to Enter Radius.
    4. Select the Button and use the Attribute inspector to change its background color to a shade of gray and its caption to “Compute Area of Circle”
    5. Select the Text view and use the Attribute inspector to change its background color to a lighter shade of gray than the button.
    6. With the Text view still selected, uncheck the Editable attribute (see Figure 27.4). This will ensure that the user cannot change the contents of the text view.
      Screenshot of storyboard with the Behavior option Editable unchecked and Selectable checked under Text View in the right, and encircled.

      Figure 27.4

    7. Create layout constraints for each of the elements on the storyboard scene using the information in Table 27.3. When creating layout constraints using the pin constraints popup, ensure the Constrain to margins option is unchecked and Update Frames is set to Items of New Constraints.

      Table 27.3 Layout Constraints

      Element Left Top Right Bottom Width Height
      Text Field 16 20 16 30
      Button 56 16 176 38
      Text View 16 8 16 20
    8. Use the assistant editor to create an outlet for the Text field in the view controller class. Name the outlet radiusField.
    9. Set up the delegate property for the Text field.
      • Ensure the Assistant Editor is visible and the ViewController.swift file is loaded in it.
      • Right-click the table view to bring up a context menu. Drag from the item labeled “delegate” in the context menu to the item labeled “View Controller” in the document outline.
    10. Create an action method in the ViewController class and associate it with the Did End On Exit event of the text field.
      • Right-click the UITextField object on the scene to display its context menu, and drag from the circle beside the Did End On Exit item to an empty line in the ViewController.swift file.
      • Name the new Action onDismissKeyboard.
    11. Use the assistant editor to create an outlet for the Text view in the view controller class. Name the outlet serverResponseView.
    12. Create an action in the view controller class and connect it with the Touch Up Inside event of the button.
      • Ensure the Assistant editor is visible and the ViewController.swift file is loaded in it.
      • Right-click the button in the scene to display its context menu, and drag from the circle beside the Touch Up Inside item to an empty line in the ViewController.swift file.
      • Name the new action onCalculateArea.
  • Ensure the ViewController class implements the UITextFieldDelegate protocol.

    Modify the declaration of the ViewController class from:

    class ViewController: UIViewController 

    to

    class ViewController: UIViewController, UITextFieldDelegate 
  • Modify code in the view controller class.
    1. Open the ViewController.swift file in the project explorer.
    2. Add the following line to the implementation of the onDismissKeyboard method:
      radiusField.resignFirstResponder()
  • Add a tap gesture recognizer and use it to dismiss the keyboard when the background area of the view is tapped.
    1. Add the following method to the ViewController.swift file:
      func handleBackgroundTap(sender: UITapGestureRecognizer) {
              radiusField.resignFirstResponder()
      }
    2. Add the following code to the viewDidLoad method of the view controller class, after the super.viewDidLoad() line:
      let tapRecognizer = UITapGestureRecognizer(target:self ,
      action: Selector("handleBackgroundTap:"))
      tapRecognizer.cancelsTouchesInView = false
      self.view.addGestureRecognizer(tapRecognizer)
    3. Add code to clear the initial contents of the text view when the view is loaded.
      • Add the following line to the end of the viewDidLoad method:
        serverResponseView.text = ""
      • The viewDidLoad method in your ViewController.swift file should now resemble the following:
        override func viewDidLoad() {
                super.viewDidLoad()
                let tapRecognizer = UITapGestureRecognizer(target:self ,
                    action: Selector("handleBackgroundTap:"))
                tapRecognizer.cancelsTouchesInView = false
                self.view.addGestureRecognizer(tapRecognizer)
                serverResponseView.text = ""
        }
    4. Replace the implementation of the onCalculateArea method with the following:
      @IBAction func onCalculateArea(sender: AnyObject) {
          let radius:String = radiusField.text!
          if radius.isEmpty
          {
              return;
          }
          let serviceURL:String =
      "http://www.asmtechnology.com/MathService/CircleArea/?radius=\(radius)"
          let url:NSURL! = NSURL(string: serviceURL)
          let request: NSMutableURLRequest = NSMutableURLRequest(URL: url)
          let configuration =
      NSURLSessionConfiguration.defaultSessionConfiguration()
          configuration.timeoutIntervalForRequest = 15.0
          let session: NSURLSession! =
               NSURLSession(configuration: configuration,
                            delegate: nil,
                            delegateQueue: NSOperationQueue.mainQueue())
          let task : NSURLSessionDataTask! =
                            session.dataTaskWithRequest(request,
                            completionHandler: {(data, response, error) in
              if data != nil
              {
                  let decodedString = NSString(data: data!,
                                      encoding: NSUTF8StringEncoding)
                  self.serverResponseView.text = decodedString as! String
              }
          });
          task.resume()
      }
  • Configure Application Transport Security to allow an insecure HTTP connection for the asmtechnology.com domain.
    1. Expand the RESTClient group in the project navigator and click on the Info.plist file to open it in the property list editor.
    2. Add a new key to the Info.plist file called NSAppTransportSecurity and set its type as Dictionary.
    3. Expand the NSAppTransportSecurity key and add a new child key called NSExceptionDomains, also of type Dictionary.
    4. Expand the NSExceptionDomains key and add a child key called asmtechnology.com, also of type Dictionary.
    5. Expand the asmtechnology.com key and add two Boolean keys, both set to YES, called NSIncludesSubdomains and NSExceptionAllowsInsecureHTTPLoads.
  • Test your app in the iOS Simulator.
    1. Click the Run button in the Xcode toolbar. Alternatively, you can select Project arrow Run.
    2. Ensure your computer has an active Internet connection.
    3. Enter a numeric value for the radius field and tap the Compute Area of Circle button.