Introduction to MapKit in iOS 6 Tutorial

A tutorial that shows you how you can use MapKit in your iOS apps to show maps, drop pins, look up addresses, and more! By Matt Galloway.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Obtaining Arrests Data: The Plan

The next step is to plot some interesting arrests data around our current location. But where in the heck can we get such stuff??

Well, it depends on your current location. Here in Baltimore, we are quite lucky because the city is working quite hard to making all city data available online, through the OpenBaltimore initiative.

We will be using this dataset for the purposes of this tutorial. After you finish this tutorial, maybe look around to see if your city has an alternate dataset you can use?

Anyway, the Baltimore city data is made available through a company named Socrata, who has an API you can use to access the data. The Socrata API documentation is available online, so we aren’t going to go into the gory details here, except to explain the high level plan of attack:

  1. The specific dataset we’re interested in is the BPD Arrests. Using this link, you can take a peek at the raw data, and if you click Export\API, you can see the API access endpoint we’ll be using.
  2. To query the API, you basically issue a POST to the given Socrata URL, and pass in a query as JSON. The results will come back as JSON as well. You can learn more about the command and response formats in the Socrata API documentation, but you don’t really need to know the details for this tutorial.
  3. The query we need to use is on the largish end, so we’ll store it in a text file to make it a bit easier to read and edit, and do some subtitutions in the code.
  4. To save time, we’ll use ASIHTTPRequest to assist with sending/receiving data to the web service.

Ok – so we’ve got a plan, but before we can begin, we need to quickly add the ASIHTTPRequest library to our project.

Adding the Libraries

To add ASIHTTPRequest, first download it. Once you have it downloaded, right click your ArrestsPlotter project entry in groups and files, select New Group, and name the new group ASIHTTPRequest. Then drag all of the files (but not the folders) from within the ASIHTTPRequest\Classes directory (ASIAuthenticationDialog.h and several others) into the new ASIHTTPRequest group. Make sure “Copy items into destination group’s folder (if needed)” and “Add to targets -> ArrestsPlotter” are selected, and click Finish.

Also repeat this for the two files (Reachability.h and Reachability.m) in ASIHTTPRequest\External\Reachability, as these are dependencies of the project.

To add MBProgressHUD, first download it. Once you have it downloaded, right click your ArrestsPlotter project entry in groups and files, select New Group, and name the new group MBProgressHUD. Then drag MBProgressHUD.h and MBProgressHUD.m into the new MBProgressHUD group. Make sure “Copy items into destination group’s folder (if needed)” and “Add to targets -> ArrestsPlotter” are selected, and click Finish.

The last step is you need to link your project against a few required frameworks. To do this, click on your ArrestsPlotter project entry in Groups & Files, click the ArrestsPlotter target, and choose the Build Phases tab. Click the plus button, and choose CFNetwork.framework. Then repeat this for SystemConfiguration.framework, MobileCoreServices.framework, and libz.dylib.

Framework dependencies in Xcode 4.2

Note that, so far, if you compile the app you will run several error messages like – autorelease is unavailable, retain is unavailable and ARC forbids explicit message send of ‘release’ and many others around the ARC feature.

Actually, projects with Automatic Reference Counting (ARC) enabled can use ASIHTTPRequest. However, since ASIHTTPRequest’s codebase does not use ARC, you will need to add compiler flags to get everything working. This is pretty easy. In Xcode, go to your active target and select the “Build Phases” tab. In the “Compiler Flags” column, set -fno-objc-arc for each of the ASIHTTPRequest source files (including Reachability.m).

Compile your project just to make sure you’re good so far, and now we’re back to the fun stuff! You will notice a few warnings coming from ASIHTTPRequest and MBProgressHUD which you can simply ignore. They are just there because it hasn’t been fully updated yet to support the new iOS 6 SDK.

Obtaining Arrests Data: The Implementation

First, download this resource file which contains a template for the query string we need to send to the Socrata API web service to get the arrests near a particular location. When you get the file, unzip it and drag command.json into your ArrestsPlotter\Supporting Files group, make sure “Copy items into destination group’s folder (if needed)” and “Add to targets -> ArrestsPlotter” are selected, and click Finish.

Next, you need to set up the “Refresh” button on the toolbar to call a method, so you know when it’s tapped and can search for the arrests data around the current location. Again, you could do this the old way (make an IBAction outlet and connect with Interface Builder), but you might as well use the new super-duper, automagic way!

To do this, click MainStoryboard.storyboard, select the Refresh button, and control drag from the button to ViewController.h, to the line right after the mapView outlet. Change the Connection type to Action, the Name to refreshTapped, keep the Type as id, and click Connect. Xcode will automatically create the method for you in both the header and implementation, and connect it too!

Then switch over to ViewController.m and make the following changes:

// At top of file
#import "ASIHTTPRequest.h"

// Replace refreshTapped as follows
- (IBAction)refreshTapped:(id)sender {
     // 1
    MKCoordinateRegion mapRegion = [_mapView region];
    CLLocationCoordinate2D centerLocation = mapRegion.center;
    
    // 2
    NSString *jsonFile = [[NSBundle mainBundle] pathForResource:@"command" ofType:@"json"];
    NSString *formatString = [NSString stringWithContentsOfFile:jsonFile encoding:NSUTF8StringEncoding error:nil];
    NSString *json = [NSString stringWithFormat:formatString, 
                      centerLocation.latitude, centerLocation.longitude, 0.5*METERS_PER_MILE];
    
    // 3
    NSURL *url = [NSURL URLWithString:@"http://data.baltimorecity.gov/api/views/INLINE/rows.json?method=index"];

    // 4
    ASIHTTPRequest *_request = [ASIHTTPRequest requestWithURL:url];
    __weak ASIHTTPRequest *request = _request;

    request.requestMethod = @"POST";    
    [request addRequestHeader:@"Content-Type" value:@"application/json"];
    [request appendPostData:[json dataUsingEncoding:NSUTF8StringEncoding]];
    // 5
    [request setDelegate:self];
    [request setCompletionBlock:^{         
        NSString *responseString = [request responseString];
        NSLog(@"Response: %@", responseString);
    }];
    [request setFailedBlock:^{
        NSError *error = [request error];
        NSLog(@"Error: %@", error.localizedDescription);
    }];
    
    // 6
    [request startAsynchronous];
}

Let’s review this section by section.

  1. Gets the lat/long for the center of the map.
  2. Reads in the command file template that you downloaded from this site, which is the query string you need to send to the Socrata API to get the arrests within a radius of a particular location. It also has a hardcoded date restriction in there to keep the data set managable. The command file is set up to be a query string, so you can substitute the lat/long and radius in there as parameters. It has a hardcoded radius here (0.5 miles) to again keep the returned data managable.
  3. Creates a URL for the web service endpoint to query.
  4. Creates a ASIHTTPRequest request, and sets it up as a POST, passing in the JSON string as data.
  5. Sets up two blocks for the completion and failure. So far on this site we’ve been using callback methods (instead of blocks) with ASIHTTPRequest, but I wanted to show you the block method here because it’s kinda cool and convenient. Right now, these do nothing but log the results.
  6. Finally, starts the request going asynchronously. When it completes, either the completion or error block will be executed.

Compile and run your code, and if all works well you should see some data in your console when you click refresh, similar to the following:

Arrests Data Web Service Results