A JBoss Project
Red Hat

Getting Started with AeroGear and Xcode Template

As of version 1.0 of the AeroGear library, we are offering an Xcode template for our users to easily get started with the framework. This guide will help you setup the template in your existing Xcode installation. Once installed you can easily use the template as a basis for all your future AeroGear iOS applications. The guide assumes that you have Xcode IDE installed in your machine. If not, you can download it from the Apple developer web site here. Let’s get started.

Step 1: Install Xcode Template

Click here to download the latest version of the template from the project’s web site. Once the download completes, uncompress the file and inside you will find an AeroGear folder.

Template folder

The folder contains the system configuration files that comprise the installable Xcode template. To assist Xcode to recognize the template, we need to copy it to a specific location in order for it to pick it up. Because the location is a hidden folder in the system, we need to manual do the copy operation using a terminal.

Open a terminal and type:

$ ls ~/Library/Developer/Xcode/Templates/Application/Project\ Templates/

If you get back a response that the folder does not exist it means that it’s the first time you install a template and the location is not yet initialized. To fix that, simply create the folder otherwise skip to the next step.

$ mkdir -p ~/Library/Developer/Xcode/Templates/Application/Project\ Templates/

and finally copy the template:

$ cp -r AeroGear ~/Library/Developer/Xcode/Templates/Application/Project\ Templates/

Step 3: Verify installation

Fire up Xcode and when presented with the welcome screen, choose "Create a new Xcode Project".

Welcome screen

If the installation of the template was succesful, you will find the AeroGear template under the iOS section. If not, please verify that you correctly install the template from Step 1.

AeroGear screen

Select the template and click Next

Enter "AeroGearExample" in the Product Name field and your initials as the Class Prefix. In this example I used "AGP". The template also has options for specifying the Authentication type as well as an options to enable Push (check the UnifiedPush section later in the tutorial) and Crypto. Uncheck those options since we won’t need it in our example but if you are interested check our cookbook guide for more information. For the device family choose iPhone.

Details

Xcode creates our project and we are presented with the default project screen.

Main Project Screen

Now that our project is created, there is one final step left before we can start using the project.

Step 4: Install Dependencies through CocoaPods

The project created by the AeroGear template uses CocoaPods for managing any external library dependencies (including AeroGear). If you haven’t used it so far in your iOS projects, please take the time to familiarize yourself. It will make your life much easier when dealing with dependencies (and upgrades!). Have a look at the web site for installation details.

If you take a close look at the "Project Navigator" you will notice a file with name Podfile. This file is used by CocoaPods to install any library dependencies the project has. The template has already filled in "AeroGear" for us and you are free to add several other popular libraries.

So time to install the dependencies.

Close the project window and using the terminal navigate to the project directory. Assuming you have correctly installed CocoaPods, simple do a:

pod install

to install the dependencies. Further, this step will also create a workspace file (with the extension <project name>.xcworkspace) that contains the definitions for both the project and it’s required library dependencies. We will use that file for now on when opening the project.

Step 5: Verify the installation

Now that our depenendecies are set up, it’s time to verify our installation. For this, we are going to build a simple application that will fetch remote data from our testbed testbed server deployed on OpenShift. The data represent a list of developer information (image, name, twitter) that will be displayed on a standard UITableView component. Upon clicking a developer on the list, his twitter page will be opened using the system’s default browser. Let’s start.

AeroGearExampleAPIClient.m

At the Project Navigator click on "AeroGearExampleAPIClient.m". This singleton instance class is used through out our project and hold references to the Pipe objects allowing us to connect to remote RESTFul endpoints and perform CRUD operations on them.

The first thing we need to do is add the URL of the remote application. The template has already generated a convinient placeholder so go ahead and change the kAeroGearExampleAPIBaseURLString constant to point to the remote server that is http://myserver.rhcloud.com/rest

static NSString * const kAeroGearExampleAPIBaseURLString = @"http://myserver.rhcloud.com/rest";

...
- (id)init {
    if (self = [super init]) {
        NSURL *baseURL = [NSURL URLWithString:kAeroGearExampleAPIBaseURLString];

        // create the Pipeline object
        AGPipeline *pipeline = [AGPipeline pipelineWithBaseURL:baseURL];   // [1]

        _pipe = [pipeline pipe:^(id<AGPipeConfig> config) {   // [2]
            [config setName:@"/team/developers"];  // [3]
        }];
        // ..add your own pipes here
    }

    return (self);
}

Central to AeroGear is the concept of Pipeline and Pipe. The former represents a collection of server connections and the latter the connection itself. In [1] we initialize our Pipeline object with the base URL of the remote application and then we call it’s pipe method [2] passing a configuration object that sets the name of the remote endpoint. Change the placeholder generated by the template to /team/developers as shown in [3]

This completes our walkthrough for the "AeroGearExampleAPIClient.m" class.

AGPViewController.m

At the Project Navigator click on "AGPViewController.m". This View Controller class holds the table component that will display the objects when they are retrieved from the server.

First we need to declare an instance variable that will hold the objects:

@implementation AGPViewController {
    NSArray *_data;
}

The connection and fetching of data is performed on the viewDidLoad lifecycle method, called by the system after the view is loaded. Modify the method to look like the following:

- (void)viewDidLoad {
    [super viewDidLoad];

    self.title = @"Developers";

    // access the singleton instance that holds our pipes
    AgProjectAPIClient *apiClient = [AgProjectAPIClient sharedInstance];  // [1]

    // time to retrieve remote data
    [[apiClient pipe] read:^(id responseObject) {   // [2]
       // do something with the response
       // e.g. updating the model

       _data = responseObject;  // [3]

       // instruct table to refresh view
       [self.tableView reloadData]; // [4]

    } failure:^(NSError *error) {
        NSLog(@"An error has occured during read! \n%@", error);
    }];
}

First we access the singleton instance [1] that holds the Pipe references. We then issue a read request on the Pipe object to fetch the data from from the remote application. If the fetch was successful, we update our local model [3] and we instruct the table view component to refresh itself [4] to show the latest data.

Now we need fill the table delegate data source methods that will be called when its time to display the data in the table view. The template has already generated the methods for us but with warning that are incomplete. Change the implementations with the following:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [_data count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    }

    // extract the developer
    NSDictionary *developer = [_data objectAtIndex:indexPath.row];  //  [1]

    // fill cell data   // [2]
    cell.textLabel.text = [developer objectForKey:@"name"];
    cell.detailTextLabel.text = [developer objectForKey:@"twitter"];
    cell.tag = indexPath.row;

    // fetch the twitter image asynchronous not to block UI
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul), ^{    // [3]
        NSData *imageData = [NSData dataWithContentsOfURL:
                             [NSURL URLWithString:[developer objectForKey:@"photoURL"]]];

        dispatch_async(dispatch_get_main_queue(), ^{
            if (cell.tag == indexPath.row) {   // [4]
                cell.imageView.image = [UIImage imageWithData:imageData];   // [5]
                [cell setNeedsLayout];
            }
        });
    });

    return cell;
}

In [1] we extract the developer object that is about to be rendered, from the list retrieved earlier during the initial Pipe read. We then use it to fill the cell data with developer information [2]. Since we don’t want to block the UI when the twitter image is fetched, we dispatch it asynchronously [3] with the power of GCD. When the image is finally fetched, we set it on the cell [5].

NOTE Since iOS table view component recycles cells in order to save memory (e.g. for large data sets), we need to ensure not to overwrite a recycled cell with an old image. The trick we use here is to assign a tag on the cell with the row index and we verify in [4] when we are about to display it.

What is left now is to add functionality where when a cell is clicked the application open’s the system browser and redirects to the developer’s twitter page. Change the implementation of the didSelectRowAtIndexPath that is called when the user clicks a cell on the table with the following:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // extract the developer
    NSDictionary *developer = [_data objectAtIndex:indexPath.row];  // [1]

    // format twitter url
    NSURL *url = [NSURL URLWithString:
                  [NSString stringWithFormat:@"http://twitter.com/%@", [developer objectForKey:@"twitter"]]];   // [2]

    // open twitter page
    [[UIApplication sharedApplication] openURL:url];  // [3]

    [tableView deselectRowAtIndexPath:indexPath animated:YES];
}

In [1] we extract the developer object as we did previously from the cell the user clicked. We then setup an NSURL object [2] that points to the developers twitter page and we ask the system to open it the browser [3].

Step 6: Run

We are ready now to run the sample project. From the menu select "Product→Run" and if all goes well, you will be presented with the following screen:

Emulator

Success! Your first iOS application built with AeroGear!

This completes our walkthrough. For more complete example applications that utilize different parts of the AeroGear library, have a look at our available cookbooks examples on github.

You can also browse AeroGear iOS API reference to familiarize yourself with the wealth of options.

UnifiedPush Support

By clicking the Enable Push option in the wizard, the template will also generate the code for you to connect to the AeroGear UnifiedPush Server, a new effort from JBoss to unify notification messaging across different mobile operating systems. By using the UnifiedPush Server at your backend it will allow you to send and receive notification across different mobile devices, do broadcasts and selective sends and much much more. If that sounds interesting, please have a look at this tutorial for more information. It will show you how to setup the UnifiedPush server, arrange the provisioning profiles with Apple and send notification messages.