You are on page 1of 41

All Topics Find tutorials, courses, and more...

Code Categories Learning Guides

IOS SDK

Create a Weather App


with Forecast API
Integration
by Bart Jacobs 20 May 2013
13 Comments

1 3

In the first article of this series, we laid the foundation of the project by setting up
the project and creating the application's structure. In this article, we leverage the
AFNetworking library to interact with the Forecast API.
open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
Introduction
In the first installment of this series, we laid the foundation of our weather
application. Users can add their current location and switch between locations. In
this tutorial, we will use the AFNetworking library to ask the Forecast API for the
weather data of the currently selected location.

If you want to follow along, you will need a Forecast API key. You can obtain an API
key by registering as a developer at Forecast. Registration is free, so I encourage
you to try out the Forecast weather service. You can find your API key at the bottom
of your dashboard (figure 1).

open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
Figure 1: Obtaining Your API Key

1. Subclassing AFHTTPClient

open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
As I wrote earlier in this article, we will be using theAFNetworking library for
communicating with the Forecast API. There are several options when working with
AFNetworking, but to make our application future proof, we will opt for the
AFHTTPClient class. This class is designed for consuming web services, such as
the Forecast API. Even though we will only access one API endpoint, it is still useful
to make use of the AFHTTPClient as you will learn in a few moments.

It is recommended to create an AFHTTPClient subclass for each web service.


Because we already added AFNetworking to our project in the previous tutorial, we
can immediately start subclassing AFHTTPClient.

Step 1: Create the Class


Create a new Objective-C class, name it MTForecastClient, and make it a
subclass of AFHTTPClient (figure 2).

open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
Figure 2: Subclassing AFHTTPClient

Step 2: Creating a Singleton Object


We will adopt the singleton pattern to make it easier to use the MTForecastClient

open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
class in our project. This means that only one instance of the class is alive at any
one time for the lifetime of the application. Chances are that you are already familiar
with singleton pattern as it is a common pattern in many object-oriented
programming languages. At first glance, the singleton pattern seems very
convenient, but there are a number of caveats to watch out for. You can learn more
about singletons by reading this excellent article by Matt Gallagher.

Creating a singleton object is pretty straightforward in Objective-C. Start by


declaring a class method in MTForecastClient.h to provide public access to the
singleton object (see below).

1 #import "AFHTTPClient.h"
2
3 @interface MTForecastClient : AFHTTPClient
4
5 #pragma mark -
6 #pragma mark Shared Client
7 + (MTForecastClient *)sharedClient;
8
9 @end

The implementation of sharedClient may look daunting at first, but it isn't that
difficult once you understand what's going on. We first declare two static variables,
(1) predicate of type dispatch_once_t and (2) _sharedClient of type
MTForecastClient. As its name implies, predicate is a predicate that we use in
open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
combination with the dispatch_once function. When working with a variable of type
dispatch_once_t, it is important that it is declared statically. The second variable,
_sharedClient, will store a reference to the singleton object.

The dispatch_once function takes a pointer to a dispatch_once_t structure, the


predicate, and a block. The beauty of dispatch_once is that it will execute the
block once for the lifetime of the application, which is exactly what we want. The
dispatch_once function doesn't have many uses, but this is definitely one of them.
In the block that we pass to dispatch_once, we create the singleton object and
store a reference in _sharedClient. It is safer to invoke alloc and init
separately to avoid a race condition that could potentially lead to a deadlock. Euh ...
what? You can read more about the nitty gritty details on Stack Overflow.

01 + (MTForecastClient *)sharedClient {
02 static dispatch_once_t predicate;
03 static MTForecastClient *_sharedClient = nil;
04
05 dispatch_once(&predicate, ^{
06 _sharedClient = [self alloc];
07 _sharedClient = [_sharedClientinitWithBaseURL:[self baseURL]];
08 });
09
10 return _sharedClient;
11 }

open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
The important thing to understand about the implementation of the sharedClient
class method is that the initializer, initWithBaseURL:, is invoked only once. The
singleton object is stored in the _sharedClient static variable, which is returned by
the sharedClient class method.

Step 3: Configuring the Client


In sharedClient, we invoke initWithBaseURL:, which in turn invokes baseURL ,
another class method. In initWithBaseURL:, we set a default header, which means
that the client adds this header to every request that it sends. This is one of the
advantages of working with the AFHTTPClient class. In initWithBaseURL:, we
also register an HTTP operation class by invoking registerHTTPOperationClass:.
The AFNetworking library provides a number of specialized operations classes. One
of these classes is the AFJSONRequestOperation class, which makes interacting
with a JSON API very easy. Because the Forecast API returns a JSON response,
the AFJSONRequestOperation class is a good choice. The
registerHTTPOperationClass:method works similar to how the
registerClass:forCellReuseIdentifier:of the UITableView class works. By
telling the client what operation class we want to use for interacting with the web
service, it will instantiate instances of that class for us under the hood. Why this is
useful will become clear in a few moments.

open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
01 - (id)initWithBaseURL:(NSURL *)url {
02 self = [super initWithBaseURL:url];
03
04 if (self) {
05 // Accept HTTP Header
06 [self setDefaultHeader:@"Accept" value:@"application/json"
];
07
08 // Register HTTP Operation Class
09 [self registerHTTPOperationClass
:[AFJSONRequestOperationclass
10 }
11
12 return self;
13 }

The implementation of baseURL is nothing more than a convenience method for


constructing the client's base URL. The base URL is the URL that the client uses to
reach the web service. It is the URL without any method names or parameters. The
base URL for the Forecast API is https://api.forecast.io/forecast/
/ . The API
key is part of the URL as you can see. This may seem insecure and it actually is. It
isn't difficult for someone to grab the API key so it is advisable to work with a proxy
to mask the API key. Because this approach is a bit more involved, I won't cover
this aspect in this series.

1 + (NSURL *)baseURL {
2 return [NSURL URLWithString:[NSString stringWithFormat:@"https://api.forecast.io/f
3 }

open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
You may have noticed in the implementation of baseURL that I have used another
string constant for storing the API key. This might seem unnecessary since we only
use the API key in one location. However, it is good practice to store application
data in one location or in a property list.

1 #pragma mark -
2 #pragma mark Forecast API
3 extern NSString * const MTForecastAPIKey;

1 #pragma mark -
2 #pragma mark Forecast API
3 NSString * const MTForecastAPIKey = @"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Step 4: Adding a Helper Method


Before we move on, I would like to extend the MTForecastClient class by adding a
helper or convenience method that will make it easier to query the Forecast API.
This convenience method will accept a location and a completion block. The
completion block is executed when the request finishes. To make working with
blocks easier, it is recommended to declare a custom block type as shown below. If
you still feel uncomfortable using blocks, then I recommend reading this great article
by Akiel Khan.

The block takes two arguments, (1) a boolean indicating whether the query was
open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
successful and (2) a dictionary with the response from the query. The convenience
method, requestWeatherForCoordinate:completion:
, takes the coordinates of a
location ( CLLocationCoordinate2D) and a completion block. By using a completion
block, we can avoid creating a custom delegate protocol or fall back to using
notifications. Blocks are a perfect fit for this type of scenario.

01 #import "AFHTTPClient.h"
02
03 typedef void (^MTForecastClientCompletionBlock)(
BOOL success, NSDictionary
04
05 @interface MTForecastClient : AFHTTPClient
06
07 #pragma mark -
08 #pragma mark Shared Client
09 + (MTForecastClient *)sharedClient;
10
11 #pragma mark -
12 #pragma mark Instance Methods
13 - (void)requestWeatherForCoordinate:(CLLocationCoordinate
2D)coordinate
14
15 @end

In requestWeatherForCoordinate:completion:
, we invoke
getPath:success:failure:, a method declared in AFHTTPClient. The first
argument is the path that is appended to the base URL that we created
earlier. The second and third arguments are blocks that are executed when

open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
the request succeeds and fails, respectively. The success and failure
blocks are pretty simple. If a completion block was passed to
requestWeatherForCoordinate:completion:
, we execute the block and pass a
boolean value and the response dictionary (ornil in the failure block).
In the failure block, we log the error from the failure block to the
console to facilitate debugging.

01 - (void)requestWeatherForCoordinate:(CLLocationCoordinate2D)coordinate
02 NSString *path = [NSString stringWithFormat:@"%f,%f", coordinate.latitude
03 [self getPath:path parameters:nil success:^(AFHTTPRequestOperation
04 if (completion) {
05 completion(YES, response);
06 }
07
08 } failure:^(AFHTTPRequestOperation*operation, NSError *error) {
09 if (completion) {
10 completion(NO, nil);
11
12 NSLog(@"Unable to fetch weather data due to error %@ with user info %@
13 }
14 }];
15 }

You may be wondering what the response object in the success blocks is or
references. Even though the Forecast API returns a JSON response, the response
object in the success block is an NSDictionary instance. The benefit of working
open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
with the AFJSONHTTPRequestOperationclass, which we registered in
initWithBaseURL:, is that it accepts the JSON response and automatically creates
an object from the response data, a dictionary in this example.

2. Querying the Forecast API

Step 1: Amend setLocation:

Armed with the MTForecastClient class, it is time to query the Forecast API and
fetch the weather data for the currently selected location. The most suitable place to
do this is in the setLocation: method of the MTWeatherViewControllerclass.
Amend the setLocation: method as shown below. As you can see, all we do is
invoke fetchWeatherData, another helper method.

01 - (void)setLocation:(NSDictionary *)location {
02 if (_location != location) {
03 _location = location;
04
05 // Update User Defaults
06 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults
];
07 [ud setObject:location forKey:MTRainUserDefaultsLocation];
08 [ud synchronize];
09
10 // Post Notification
open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
10 // Post Notification
11 NSNotification *notification1 = [NSNotification notificationWithName
12 [[NSNotificationCenterdefaultCenter] postNotification:notification
13
14 // Update View
15 [self updateView];
16
17 // Request Location
18 [self fetchWeatherData];
19 }
20 }

Have you ever wondered why I use so many helper methods in my code? The reason is
simple. By wrapping functionality in helper methods, it is very easy to reuse code in
various places of a project. The main benefit, however, is that it helps battle code
duplication. Code duplication is something you should always try to avoid as much as
possible. Another advantage of using helper methods is that it makes your code much
more readable. By creating methods that do one thing and providing a well chosen method
name, it is easier to quickly read and process your code.

Step 2: Sending the Request


It is time to put the SVProgressHUD library to use. I really like this library because it
is so simple to use without cluttering the project's code base. Take a look at the
implementation of fetchWeatherData below. We start by showing the progress
open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
HUD and then pass a structure ( CLLocationCoordinate2D) to the convenience
method we created earlier, requestWeatherForCoordinate:completion:
. In the
completion block, we hide the progress HUD and log the response to the console.

01 - (void)fetchWeatherData {
02 // Show Progress HUD
03 [SVProgressHUD showWithMaskType:SVProgressHUDMaskTypeGradient];
04
05 // Query Forecast API
06 double lat = [[_location objectForKey:MTLocationKeyLatitude]doubleValue
07 double lng = [[_location objectForKey:MTLocationKeyLongitude]doubleValue
08 [[MTForecastClient sharedClient] requestWeatherForCoordinate
:CLLocationCoordinate
09 // Dismiss Progress HUD
10 [SVProgressHUD dismiss];
11
12 NSLog(@"Response > %@", response);
13 }];
14 }

Before you build and run your application, import the header file of the
MTForecastClient class in MTWeatherViewController.m.

01 #import "MTWeatherViewController.h"
02
03 #import "MTForecastClient.h"
04
05 @interface MTWeatherViewController() <CLLocationManagerDelegate> {

open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
05 @interface MTWeatherViewController() <CLLocationManagerDelegate> {
06 BOOL _locationFound;
07 }
08
09 @property (strong, nonatomic) NSDictionary *location;
10
11 @property (strong, nonatomic) CLLocationManager *locationManager;
12
13 @end

What happens when the device is not connected to the web? Have you thought
about that scenario? In terms of user experience, it is good practice to notify the
user when the application is unable to request data from the Forecast API. Let me
show how to do this with the AFNetworking library.

3. Reachability
There are a number of libraries that provide this functionality, but we will stick with
AFNetworking. Apple also provides sample code, but it is a bit outdated and doesn't
support ARC.

AFNetworking has truly embraced blocks, which is definitely one of the reasons that
this library has become so popular. Monitoring for reachability changes is as simple
as passing a block to setReachabilityStatusChangeBlock:
, another method of the

open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
AFHTTPClient class. The block is executed every time the reachability status
changes and it accepts a single argument of type AFNetworkReachabilityStatus.
Take a look at the updated initWithBaseURL: method of the MTForecastClient
class.

01 - (id)initWithBaseURL:(NSURL *)url {
02 self = [super initWithBaseURL:url];
03
04 if (self) {
05 // Accept HTTP Header
06 [self setDefaultHeader:@"Accept" value:@"application/json"
];
07
08 // Register HTTP Operation Class
09 [self registerHTTPOperationClass
:[AFJSONRequestOperationclass
10
11 // Reachability
12 __weak typeof(self)weakSelf = self;
13 [self setReachabilityStatusChangeBlock
:^(AFNetworkReachabilityStatus
14 [[NSNotificationCenterdefaultCenter] postNotificationName
15 }];
16 }
17
18 return self;
19 }

To avoid a retain cycle, we pass a weak reference to the singleton object in the
block that we pass to setReachabilityStatusChangeBlock:
. Even if you use ARC
in your projects, you still need to be aware of subtle memory issues like this. The
open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
name of the notification that we post is another string constant declared in
MTConstants.h/.m.

1 extern NSString * const MTRainReachabilityStatusDidChangeNotification


;

1 NSString * const MTRainReachabilityStatusDidChangeNotification


= @"com.mobileTuts.MTRa

The reason for posting a notification in the reachability status change block is to
make it easier for other parts of the application to update when the device's
reachability changes. To make sure that the MTWeatherViewControllerclass is
notified of reachability changes, instances of the class are added as an observer for
the notifications sent by the Forecast client as shown below.

01 - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {


02 self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
03
04 if (self) {
05 // Initialize Location Manager
06 self.locationManager = [[CLLocationManager alloc] init];
07
08 // Configure Location Manager
09 [self.locationManager setDelegate:self];
10 [self.locationManager setDesiredAccuracy:kCLLocationAccuracyKilometer];
11
12 // Add Observer
13 NSNotificationCenter*nc = [NSNotificationCenterdefaultCenter
open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
13 NSNotificationCenter*nc = [NSNotificationCenterdefaultCenter
14 [nc addObserver:self selector:@selector(reachabilityStatusDidChange:)
15 }
16
17 return self;
18 }

This also means that we need to remove the instance as an observer in the
dealloc method. This is a detail that is often overlooked.

1 - (void)dealloc {
2 // Remove Observer
3 [[NSNotificationCenterdefaultCenter] removeObserver:self];
4 }

The implementation of reachabilityStatusDidChange:is pretty basic at the


moment. We will update its implementation once we create the application's user
interface.

1 - (void)reachabilityStatusDidChange:(
NSNotification *)notification {
2 MTForecastClient *forecastClient = [notificationobject];
3 NSLog(@"Reachability Status > %i"
, forecastClient.networkReachabilityStatus
4 }

open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
4. Refreshing Data
Before we wrap this post up, I want to add two additional features, (1) fetching
weather data whenever the application becomes active and (2) adding the ability to
manually refresh weather data. We could implement a timer that fetches fresh data
every hour or so, but this is not necessary for a weather application in my opinion.
Most users will launch the application, take a look at the weather and put the
application in the background. It is therefore only necessary to fetch fresh data
when the user launches the application. This means that we need to listen for
UIApplicationDidBecomeActiveNotificationnotifications in the
MTWeatherViewControllerclass. As we did for monitoring reachability changes, we
add instances of the class as observers of notifications of type
.
UIApplicationDidBecomeActiveNotification

01 - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {


02 self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
03
04 if (self) {
05 // Initialize Location Manager
06 self.locationManager = [[CLLocationManager alloc] init];
07
08 // Configure Location Manager
09 [self.locationManager setDelegate:self];
10 [self.locationManager setDesiredAccuracy:kCLLocationAccuracyKilometer];
11
open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
11
12 // Add Observer
13 NSNotificationCenter*nc = [NSNotificationCenterdefaultCenter
14 [nc addObserver:self selector:@selector(applicationDidBecomeActive:)
15 [nc addObserver:self selector:@selector(reachabilityStatusDidChange:)
16 }
17
18 return self;
19 }

In applicationDidBecomeActive:, we verify that location is set (not nil )


because this won't always be true. If the location is valid, we fetch the weather data.

1 - (void)applicationDidBecomeActive:(
NSNotification *)notification {
2 if (self.location) {
3 [self fetchWeatherData];
4 }
5 }

I have also amended fetchWeatherData to only query the Forecast API if the
device is connected to the web.

01 - (void)fetchWeatherData {
02 if ([[MTForecastClient sharedClient] networkReachabilityStatus
] == AFNetworkReach
03
04 // Show Progress HUD
05 [SVProgressHUD showWithMaskType:SVProgressHUDMaskTypeGradient];
06

open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
06
07 // Query Forecast API
08 double lat = [[_location objectForKey:MTLocationKeyLatitude]doubleValue
09 double lng = [[_location objectForKey:MTLocationKeyLongitude]doubleValue
10 [[MTForecastClient sharedClient] requestWeatherForCoordinate
:CLLocationCoordinate
11 // Dismiss Progress HUD
12 [SVProgressHUD dismiss];
13
14 // NSLog(@"Response > %@", response);
15 }];
16 }

Let's add a button to the weather view controller that the user can tap to manually
refresh the weather data. Create an outlet in MTWeatherViewController.h and
create a refresh: action in MTWeatherViewController.m.

01 #import <UIKit/UIKit.h>
02
03 #import "MTLocationsViewController.h"
04
05 @interface MTWeatherViewController: UIViewController <MTLocationsViewControllerDeleg
06
07 @property (weak, nonatomic) IBOutlet UILabel *labelLocation;
08 @property (weak, nonatomic) IBOutlet UIButton *buttonRefresh;
09
10 @end

1 - (IBAction)refresh:(id)sender {
2 if (self.location) {

open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
2 if (self.location) {
3 [self fetchWeatherData];
4 }
5 }

Open MTWeatherViewController.xib, add a button to the view controller's view


with a title of Refresh, and connect the outlet and action with the button (figure 3).
The reason for creating an outlet for the button is to be able to disable it when no
network connection is available. For this to work, we need to update the
reachabilityStatusDidChange:method as shown below.

open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
Figure 3: Adding a Refresh Button

1 - (void)reachabilityStatusDidChange:(
NSNotification *)notification {
2 MTForecastClient *forecastClient = [notificationobject];
3 NSLog(@"Reachability Status > %i"
, forecastClient.networkReachabilityStatus

open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
NSLog(@"Reachability Status > %i"
, forecastClient.networkReachabilityStatus
4
5 // Update Refresh Button
6 self.buttonRefresh.enabled = (forecastClient.networkReachabilityStatus
7 }

It isn't necessary to temporarily disable the refresh button when a request is being
processed in fetchWeatherData because the progress HUD adds a layer on top of
the view controller's view that prevents the user from tapping the button more than
once. Build and run the application to test everything out.

Advertisement

open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
Bonus: Removing Locations
A reader asked me how to delete locations from the list so I am including it here for
the sake of completeness. The first thing that we need to do is tell the table view
which rows are editable by implementing tableView:canEditRowAtIndexPath:of
the UITableViewDataSource protocol. This method returns YES if the row at
indexPath is editable and NO if it is not. The implementation is simple as you can
see below. Every row is editable except for the first row and the currently selected
location.

01 - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath


:(NSIndexPath
02 if (indexPath.row == 0) {
03 return NO;
04 }
05
06 // Fetch Location
07 NSDictionary *location = [self.locations objectAtIndex:(indexPath
08
09 return ![self isCurrentLocation:location];
10 }

To check whether location is the current location, we use another helper method,
isCurrentLocation:, in which we fetch the current location and compare the
locations coordinates. It would have been better (and easier) if we had assigned a

open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
unique identifier to each location stored in the user defaults database. Not only
would it make it easier to compare locations, but it would also allow us to store the
current location's unique identifier in the user defaults database and look it up in the
array of locations. The problem with the current implementation is that locations with
the exact same coordinates cannot be distinguished from one another.

01 - (BOOL)isCurrentLocation:(
NSDictionary *)location {
02 // Fetch Current Location
03 NSDictionary *currentLocation = [[
NSUserDefaults standardUserDefaults
04
05 if ([location[MTLocationKeyLatitude]doubleValue] == [currentLocation[MTLocation
06 [location[MTLocationKeyLongitude]doubleValue] == [currentLocation[MTLocatio
07 return YES;
08 }
09
10 return NO;
11 }

When the user taps the delete button of a table view row, the table view data source
is sent a tableView:commitEditingStyle:forRowAtIndexPath:
message. In this
method, we need to (1) update the data source, (2) save the changes to the user
defaults database, and (3) update the table view. If editingStyle is equal to
, we remove the location from the locations
UITableViewCellEditingStyleDelete
array and store the updated array in the user defaults database. We also delete the
row from the table view to reflect the change in the data source.

open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
01 - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditing
02 if (editingStyle == UITableViewCellEditingStyleDelete) {
03 // Update Locations
04 [self.locations removeObjectAtIndex:(indexPath.row - 1)];
05
06 // Update User Defaults
07 [[NSUserDefaults standardUserDefaults
] setObject:self.locations
08
09 // Update Table View
10 [tableView deleteRowsAtIndexPaths
:@[indexPath] withRowAnimation
11 }
12 }

To toggle the table view's editing style, we need to add an edit button to the user
interface. Create an outlet for the button in MTLocationsViewController.h and an
action named editLocations: in MTLocationsViewController.m. In
editLocations:, we toggle the table view's editing style.

01 #import <UIKit/UIKit.h>
02
03 @protocol MTLocationsViewControllerDelegate
;
04
05 @interface MTLocationsViewController: UIViewController <UITableViewDataSource
06
07 @property (weak, nonatomic) id<MTLocationsViewControllerDelegate> delegate;
08
09 @property (weak, nonatomic) IBOutlet UITableView *tableView;
10 @property (weak, nonatomic) IBOutlet UIBarButtonItem *editButton;
open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
@property (weak, nonatomic) IBOutlet UIBarButtonItem *editButton;
11
12 @end
13
14 @protocol MTLocationsViewControllerDelegate<NSObject>
15 - (void)controllerShouldAddCurrentLocation:(
MTLocationsViewController
16 - (void)controller:(MTLocationsViewController*)controller didSelectLocation
17 @end

1 - (IBAction)editLocations:(id)sender {
2 [self.tableView setEditing:![self.tableView isEditing] animated:YES
3 }

Open MTLocationsViewController.xib, add a navigation bar to the view


controller's view, and add an edit button to the navigation bar. Connect the edit
button with the outlet and action that we created a moment ago.

open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
Figure 4: Adding an Edit Button

You may be wondering why we created an outlet for the edit button. The reason is
that we need to be able to change the title of the edit button from Edit to Done, and
vice versa, whenever the editing style of the table view changes. In addition, when
the user deletes the last location (except for the current location) in the table view, it
open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
would be nice to automatically toggle the table view's editing style. These features
are not hard to implement which is why I leave them up to you as an exercise. If
you run into problems or have questions, feel free to leave a comment in the
comments below this article.

Conclusion
We have successfully integrated the Forecast API in our weather application. In the
next tutorial, we will implement focus on the user interface and the design of the
application.

open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
Advertisement

Difficulty: Suggested Tuts+ Course


Intermediate
Length:
Medium
Categories:

iOS SDK Mobile Development

Translations Available:

Tuts+ tutorials are translated by our community


members. If you'd like to translate this post into
another language, let us know! The Swift Programming Language Start
open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
Download Attachment
Related Tutorials

Working with NSURLSession:


AFNetworking 2.0
About Bart Jacobs
Code
Bart Jacobs runs Code Foundry, a mobile
and web development company based in
Belgium and writes about iOS iOS SDK: Detecting Network
development on his blog. Bart is also the mobile Changes with Reachability
editor of Tuts+. Code

Create a Weather App with Forecast


User Interface
Code

Jobs

Web Developer / Site Management


at Zanes Law in Phoenix, AZ, USA

Advertisement
PHP Coder with Magento Knowledge
at Yoginet Web Solutions in New Delhi,
open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
Delhi, India

Envato Market Item

13 Comments Mobiletuts+

Sort by Best

Join the discussion

Jahin 8 months ago

open in browser PRO version


Hey, can you please tell me in which file do I add the API key ?
Are you a developer? Try out the HTML to PDF API pdfcrowd.com
Hey, can you please tell me in which file do I add the API key ?
1 Reply Share

Lorenzo Musso a year ago


great tutorial Bart, thank you.

just a question: how can I force a refresh of the current location at the application startup, in order to have
location always updated?

thanks
Reply Share

Jonathan a year ago


Also, can you explain the choice of using NSUserDefaults instead of a data controller? I was under the im
that user defaults was primarily meant for persistent settings, not application object management (i.e. this
criticism but I'm truly curious if there are other ways that this could/should be done).
Reply Share

Jonathan > Jonathan a year ago


(scratch that - misunderstanding)
Reply Share

Jonathan a year ago


Great tutorial, Bart. Any interest in revisiting this with the new AFNetworking 2.0 update?
Reply Share

Meg a year ago


I was just going thru the tutorial... and upgrading the code to iOS7 - but ran into a bug getting thru the 'coc
open in browser PRO version installation... I downloaded
Are you a developer? but
Try out the HTML to PDFitAPI
seems to be written for people who don't understand english!pdfcrowd.com
I cant figure
installation... I downloaded but it seems to be written for people who don't understand english! I cant figure
anyone help?
Reply Share

kevin chen 2 years ago


Hi, I have studied you demo, but I meet a problem.

The exception is:


2013-05-23 23:35:03.508 Rain[70188:907] *** Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]:
insert nil object from objects[0]'

I know the exception code is here, the city always nil.

// Extract Data
NSString *city = [placemark locality];
NSString *country = [placemark country];
CLLocationDegrees lat = placemark.location.coordinate.latitude;
CLLocationDegrees lon = placemark.location.coordinate.longitude;

// Create Location Dictionary


NSDictionary *currentLocation = @{ MTLocationKeyCity : city,
MTLocationKeyCountry : country,
MTLocationKeyLatitude : @(lat),
MTLocationKeyLongitude : @(lon) };

I'm a chinese, why the city is always nil, could you help me, thanks!
Reply Share

open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
kevin chen > kevin chen 2 years ago
I'm in china, I have find a solution and I test in chinese and english of International setting. My devic
version is 6.1.4.

NSString *country = placemark.country;


NSString *city = placemark.administrativeArea;

thanks!
1 Reply Share

Bart Jacobs Mod > kevin chen 2 years ago


I am glad to see that you have found the bug yourself. Even though the documentation doe
this, it appears that the `locality` property can be `nil`, which results in an exception in this c
Reply Share

kevin chen > Bart Jacobs 2 years ago


Thanks! I hope to read more useful articles and learn more iOS knowledge from yo
Reply Share

Sivikk > kevin chen 2 years ago


i am trying to run your app on ios5 simulator but it gives an error .
"AFNEtwork.h"excpected"(".
how can i solve this problem.

thankss
1 Reply Share

open in browser PRO version Are you a developer? Try out the Bart Jacobs
HTML to PDF API
Mod > Sivikk a year ago pdfcrowd.com
Bart Jacobs Mod > Sivikk a year ago
The import statement imports the wrong header file. It should be #import
"AFNetworking.h".

Reply Share

James Anderson > Bart Jacobs a year ago


Hey it has no errors but when i try to run the app it gives me a SIGABRT error This
the error:

2013-12-08 20:33:53.666 BPA App[611:60b] -[BPAWeatherViewController


networkReachabilityStatus]: unrecognized selector sent to instance 0x1769a930

2013-12-08 20:33:53.669 BPA App[611:60b] *** Terminating app due to uncaught


exception 'NSInvalidArgumentException', reason: '-[BPAWeatherViewController
networkReachabilityStatus]: unrecognized selector sent to instance 0x1769a930'

*** First throw call stack:

(0x2df48e83 0x382a56c7 0x2df4c7b7 0x2df4b0af 0x2de99dc8 0xf100d 0x2df0ae71


0x2de7eab1 0xf0593 0xf06ef 0x306d195b 0x306d1719 0x134a1f 0x11975f 0x119a4
0x119c81 0xefc3f 0x3073daad 0x3073d4f3 0x30737b41 0x306d2a07 0x306d1cfd
0x30737321 0x32bb776d 0x32bb7357 0x2df13777 0x2df13713 0x2df11edf 0x2de7c
0x2de7c253 0x307365c3 0x30731845 0xefa8d 0x3879eab7)

libc++abi.dylib: terminating with uncaught exception of type NSException

see more

Reply Share

open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
Subscribe d Add Disqus to your site Privacy

Advertisement

18,889 Tutorials 461 Video Courses

open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
Teaching skills to millions worldwide.

Follow Us Email Newsletters

Get Tuts+ updates, news, surveys &



offers.

Help and Support Email Address

FAQ
Subscribe
Terms of Use
Contact Support Privacy Policy
About Tuts+
Advertise
Teach at Tuts+

Custom digital services like logo design, WordPress installation, video


production and more.
Check out Envato Studio

Build anything from social networks to file upload systems. Build faster
with pre-coded PHP scripts.

open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com
Browse PHP on CodeCanyon

2014 Envato Pty Ltd. Trademarks and brands are the property of their respective
owners.

open in browser PRO version Are you a developer? Try out the HTML to PDF API pdfcrowd.com

You might also like