Building the Item Data Model
Our code looks better and becomes easier to manage when we build objects that represent or model the data items that we receive from our web service. To this end, we'll create a file for our individual plumbing supply items using a Cocoa Touch Class template, and then fill it out with the necessary parameters supplied by the API. Open up your Xcode project if you haven't already. In the Xcode Project Navigator panel, right click on the top most group folder. From the menu options that appear, select new file.
You will be presented with a set of options similar to this:
Under iOS, select Source, followed by Cocoa Touch Class, then click Next. Under the secondary menu options: next to the 'Class' field type in PlumbingSupplyItem. For the next field, 'Subclass of', type NSObject, do not create a XIB file (uncheck box), and for the Language portion ensure that Swift is selected:
Click 'Next'. When prompted to select a destination, place the file alongside the other project files. If the file is not automatically loaded into Xcode, then open it from the Project Navigator panel. Now place the following code within the PlumbingSupplyItem class implementation:
As you can see, this code block models the data item passed from the RESTful API we built in the previous segment of this tutorial series, initializing an item id, an item name, an associated image and an item description. It doesn't get much simpler than this.
Segues to the Inventory
With our data model in place, we'll now create our controller class for our inventory, using a table view. This will allow us to mediate between the two inventory table views upon a touch event by the end user.
In the same manner that we created the PlumbingSupplyItem model file, create a new file named PlumbingSupplyInventoryTableViewController using a Cocoa Touch template, and have this file subclass a UITableViewController. From the Object Library, located in the Utilities panel, drag and drop two UITableViewControllers into the main storyboard as depicted below:
Select one of the two view controllers that were just dropped in. Now, in the Identity Inspector, located in the Utilities panel, find the Class field, and type in the name of the class we just created (i.e. PlumbingSupplyInventoryTableViewController) as shown below:
Do the same for the other controller as well, since we are building two separate views of the same kind of object, i.e. items in each of our two inventories.
Note that when you are zoomed out in a storyboard, you can only make selections on the view controllers themselves. To make selections on a controller's internal elements, you have to zoom in. Armed with that information, go ahead and select the cell with the label 'Plumbing Tools'. While holding down the Control key and the left mouse button, drag your mouse over to the top right most view controller and release. During this motion you should see a blue line stretch from our starting location, the table view cell, to our final destination, the view controller.
Upon releasing your mouse button over the controller, a menu will appear. Select the top option that reads: Selection Segue->Show.
Now repeat the same steps for the other cell as well. Your storyboard should look like the following:
What did we just accomplished? We have set up view controller view transition objects known as segues. These segues will respond to a user's touch of a cell. When a cell is pressed, the associated segue will transition to the view controller that it points to.
Preparing Our Segue
Since we will be using a single view controller to display the different inventories of plumbing supplies from the two possible options, namely, Plumbing Tools and Copper Pipes and Fittings, we need to inform the destination view controller which cell was selected. The tip of a segue arrow is said to point to the destination view controller and the tail of the arrow is said to originate from the source view controller. Create a file named PlumbingSupplyCategoriesTableViewController and have this file subclass a UITableViewController.
Now we'll hook it up on the story board. Select the source view controller, i.e. the controller with the two cells. In the Identity Inspector, located in the Utilities panel, where the field reads 'Class', type in the name of the file and class we just created: PlumbingSupplyCategoriesTableViewController. In the main storyboard, select the segue arrow that is associated with the cell labeled 'Plumbing Tools'. From the Attributes Inspector, located under the Utilities panel, locate the Identifier field and type in: plumbingTools.
Do the same for the other segue arrow, but this time provide the Identifier with the name from the title of the other inventory list: copperPipesAndFittings.
We're now going to jump back to our code files. In the PlumbingSupplyInventoryTableViewController.swift file, place the following code (in line 3 below) immediately after the class definition, like so:
Now, over in the PlumbingSupplyCategoriesTableViewController.swift file, replace all the code inside the class PlumbingSupplyCategoriesTableViewController: UITableViewController { . . . } with the following snippet:
This is a system call method that is responsible for notifying the source view controller that a segue is about to be performed. The title that we set in the code above is to the destination table view controller that we segue to. On this destination controller we also set the plumbing inventory url endpoint so that the controller can load the content that was chosen by its corresponding table view cell. Our destination has enough information to load the entire inventory of the selected plumbing category.
Holding Pattern: Activity Indicator
Before we request a list of inventory items, it is customary to run an activity indicator to show the user that our app is in the process of getting some information to display. In the PlumbingSupplyInventoryTableViewController.swift file, below the line of code that reads var inventoryUrlEndpoint : String = "", enter the following line of code:
This line of code creates an instance of UIActivityIndicatorView and makes it private. Now we're going to fill it out inside the viewDidLoad method. In the same file, but inside the method override func viewDidLoad(), add the following lines:
Save all your files and run the application. Once it loads, segue to the two inventory table view controllers. You should see an activity indicator animation as shown here:
Requesting an Inventory
Inside the PlumbingSupplyInventoryTableViewController.swift file, immediately below the webActivityIndicator property, we'll now define another variable that holds a reference to a URL session object that will asynchronously handle our web request. Enter the following line there:
var urlSession = NSURLSession.sharedSession()At the bottom of the same class, we now define a function that will perform the url session request, parse the request and send us back objects if the success was successful. Here's our first stab:
If you are new to Swift, this function might look rather cryptic. That's because it is! It was no easy task to get this right—a whole lot of Google engineering was required on our end. Lets break it down by parameter:
'endPointURL : String'
- 'endPointURL' is an arbitrarily chosen parameter name that we defined in order to identify this parameter, and the 'String' portion requires that the passed in object be of string type.
'responseHandler'
- 'responseHandler' is an arbitrarily chosen name for our closure, a.k.a block in Objective-C.
'error : NSError?, items : Array<PlumbingSuppleyItem>?) → ()'
- This portion defines a closure.
- The 'error : NSError?' segment is again an arbitrarily chosen parameter name, and the 'NSError' portion requires that the passed in item be of NSError type.
- The question mark (?) indicates that the passed in object is optional, meaning that a nil may be provided—in its absence we are required to pass in a none nil value NSError object.
- 'items : Array<PlumbingSupplyItems>' indicates that the passed in object is a container that holds objects of PlumbingSupplyItem type.
- The arrow followed by the parenthesis '→ ()' indicates that this closure returns no values. Instead, objects are passed through the closure's callback parameters.
- The second arrow followed by parenthesis '→ ()' indicates that the function returns no values.
- The arrow along with the parenthesis may be omitted for both the function and closure.
Okay, that was a mouthful, but what happens when the view appears on the screen? Let's define it. Right below the viewDidLoad method, we add the following code for our new viewDidAppear method:
This function will be called by the system when the view appears onto the screen. This is good spot to begin requesting inventory items. Notice that we are calling our previously defined requestInventory function. Let's revisit that function now. Replace all the existing code inside the requestInventory method implementation, with the following snippet:
Let's go through this line by line. Line 1 creates an NSURL object from the provided endPointURL string that is referenced by a constant named url; it is essentially a wrapper object for a string formatted URL. This NSURL object is required by our urlSession object that is on the next line of code.
On this line of code, i.e. line 2, we create what is known as a 'data task' for querying our web service. This call spawns an asynchronous operation that communicates with our PHP web service without blocking the main thread. Not blocking the main thread is highly desired if we want our user interface to continue to be responsive—this makes for a better end user experience. Once the communication with our web service completes, the completionHandler closure and all of our code that is contained within this closure is executed. The closure has three parameters: 1) the raw data that comes from our web service, 2) a response object containing the headers from our web service response, or 3) an error object if the request failed.
Line 4 prints the response object inside the Xcode console.
Line 6 is a call to a closure that we defined for our requestInventory function. The parameters provided are an error object if any, and a list of data objects for our inventory items. Currently, we only return nil values.
Run the application the select both Plumbing Tools and Copper Pipes and Fittings table view cells. You should see the response objects printed for both selections in your Xcode console. The responses in your Xcode console should look similar to this:
Success! Notice that the response object is of type NSHTTPURLResponse. This response object contains information about the requesting url, and the header fields from our web services. Notice the status code of 200; this means that request was completed without issue. You can also print the data object but in its raw form it will not make much sense—try it if you'd like. The communiqué between the host and client is comprised of two parts. The first part is known as the head or header and the second part is known as the body. A typical request or response will contain both of these parts (It's possible to just request the head of a web service response—Google to find out how). The real-world analogy that is often used to describe this is paper mail. The electricity bill, for example, is comprised of two parts as well. The first part is the envelope itself that contains information about its sender—the head. The second part is the contents of the envelope that contains information for the recipient—the body.
JSON Parsing
Now let's get our raw inventory. To do this, we take the response or body of the message and translate it into a container that we can readily read from. In the same function that we've been working on, replace the following line of code that prints out the response, (i.e. println(response), line 4 in the snippet above) with the following:
The first line of code creates a parse error pointer to an object of type NSError, or nil value. On line 2, we call the NSJSONSerialization class method JSONObjectWithData(. . .) and pass to it our raw data response from our web service along with the memory address of the pointer that we created on the first line.
This last method will convert our raw data into a mutable container of type NSDictionary. If the conversion process fails, then this method will assign an error object to the provided memory address with a description or nature of the failure.
The last line of code simply prints our newly created container to the console screen. Save the file then build and run the application. Navigate to one of the inventory endpoints from the table view. In the Xcode console, you should see a similar response to this:
This project can be found on GitHub.
Success! Our application is now querying our tools and supplies API, and returning the inventory response for each of the categories listed on our primary view controller. In the next article in the series, we'll create model objects and then display our inventory item list in our Swift client. Continue to part three on building the Swift client.
Index
Introduction
Introduction and Overview: From the Back End API to the End User Application
The Web API
Building a RESTful API in PHP, Part 1
Building a RESTful API in PHP, Part 2
The Swift Client App
Networking in Swift: Building the Swift Client, Part 1
Networking in Swift: Building the Swift Client, Part 2
Networking in Swift: Building the Swift Client, Part 3
Networking in Swift: Building the Swift Client, Part 4
Networking in Swift: Building the Swift Client, Part 5
This tutorial was authored by Stefan Agapie, a Senior iOS Software Engineer, and adapted for the present piece.
PlumbingSupplyCategoriesTableViewController should be a subclass of UITableViewController not UIViewController
ReplyDeleteThank you for catching that! The tutorial has been updated.
Delete