Check Network Resources Before Use
Using Facebook-connect for iPhone SDK to post stories to Facebook is a great feature to add to your iPhone application. But what happens if the user has no access to the network? If you don’t check the network, the answer is nothing. This leads to user confusion, and it will prevent your app from being approved for the App Store.
So, how do you check? Apple provided a sample application called Reachability which provides the answer. I’ll demonstrate here. In several blog posts people felt that the Reachability sample was overkill for their needs. If you agree, here’s a link to a recipe from The iPhone Developer’s Cookbook that provides an alternate solution. Even if you don’t plan on using it, I recommend reading through the code in the Reachability example.
Project Setup
In an earlier post I used the Facebook-Connect for iPhone SDK to post stories to Facebook. I’ll use the same project to demonstrate how to check that Facebook is reachable. Here’s the project with the API Keys and Template Bundle IDs removed. fbconnect-iphone.zip I’ll include a link to the final project at the end.Download the Reachability project too. I’ll be importing a class from it to check the network connection status.
Adding the SystemConfiguration Framework
The reachability class uses the SystemConiguration Framework to check network reachability. Adding the framework to the project is easy.
- Open Connect.xcodeproj if you haven’t already.
- In the Groups & Files area, control click on the Frameworks folder and select Add => Existing Frameworks.
- Look in the Frameworks folder for the SystemConfiguration.framework folder and select it.
- Click Add to add the framework to your project.
Add the Reachability Class
Now add the Reachability Class from Apple’s sample.- Control click on the Source folder in the project and select Add => Existing Files.
- Navigate to the Reachability project, click Classes, and select the files: Reachability.h and Reachability.m.
- Click Add, then select Copy items into destination group’s folder (if needed)
- Click Add again.
Using the Reachability Class
Now that the framework and class have been added to the project, put them to work in the code. Create a variable to track the internet connection status, and two methods in the SessionViewController.h file.- Open the SessionViewController.h file and import the Reachability class:
#import "Reachability.h"
- Add a variable to track the status:
NetworkStatus internetConnectionStatus;
- Add a property to hold the network status:
@property NetworkStatus internetConnectionStatus;
- Add these two methods:
- (void)reachabilityChanged:(NSNotification *)note;
- (void)updateStatus;
#import "FBConnect/FBConnect.h"
#import "PermissionStatus.h"
#import "Reachability.h"
@class FBSession;
@interface SessionViewController : UIViewController
<FBDialogDelegate, FBSessionDelegate, FBRequestDelegate,
PermissionStatusDelegate> {
IBOutlet UILabel* _label;
IBOutlet UIButton* _permissionButton;
IBOutlet UIButton* _feedButton;
IBOutlet FBLoginButton* _loginButton;
FBSession* _session;
PermissionStatus *permissionStatusForUser;
NetworkStatus internetConnectionStatus;
}
@property(nonatomic,readonly) UILabel* label;
@property (nonatomic, retain) PermissionStatus *permissionStatusForUser;
@property NetworkStatus internetConnectionStatus;
- (void)askPermission:(id)target;
- (void)publishFeed:(id)target;
- (void)reachabilityChanged:(NSNotification *)note;
- (void)updateStatus;
@end
Finally, the implementation.- Open SessionViewController.m.
- Sysnthesize the internetConnectionStatus variable:
@synthesize internetConnectionStatus;
- Define a string for the host name of the resource:
#define kHostName @"www.facebook.com"
- Make the viewDidLoad method look like this:
- (void)viewDidLoad {
//Use the Reachability class to determine if the internet can be reached.
[[Reachability sharedReachability] setHostName:kHostName];
//Set Reachability class to notifiy app when the network status changes.
[[Reachability sharedReachability] setNetworkStatusNotificationsEnabled:YES];
//Set a method to be called when a notification is sent.
[[NSNotificationCenter defaultCenter] addObserver:selfselector:@selector(reachabilityChanged:)
I want to receive notifications if the network status changes, so I used the setNetworkStatusNotificationsEnabled:YES method. However, I found that the reachabilityChanged method wasn’t called when the network status changed. I’ll refactor later to fix the problem.name:@"kNetworkReachabilityChangedNotification" object:nil];
[self updateStatus];
[_session resume];
_loginButton.style = FBLoginButtonStyleWide;
} - Implement the new methods:
- (void)reachabilityChanged:(NSNotification *)note {
[self updateStatus];
}
- (void)updateStatus
{
// Query the SystemConfiguration framework for the state of thedevice's network connections.
self.internetConnectionStatus =[[Reachability sharedReachability] internetConnectionStatus];
if (self.internetConnectionStatus == NotReachable) {
//show an alert to let the user know that they can't connect...
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"Network Status"
message:@"Sorry, our network guro determined that the network is not available.
Please try again later." delegate:self cancelButtonTitle:nil
To update the user of the status, I display an alert with a message. When status notifications are sent later, either enable the login button or display the alert.otherButtonTitles:@"OK", nil];
[alert show];
} else {
// If the network is reachable, make sure the login button is enabled.
_loginButton.enabled = YES;
}
} - Add the alert delegate method:
#pragma mark AlertView delegate methods
- (void)alertView:(UIAlertView *)alertView
If the network isn’t available, disable the loginButton. In this app nothing can be done without a connection, so additional information would be in order.clickedButtonAtIndex:(NSInteger)buttonIndex {
_loginButton.enabled = NO;
[alertView release];
}
Refactoring to Receive Reachability Changed Notifications
To get the change notifications I had to make several small changes. My initial observations when checking the remoteHostStatus instead of the internetConnectionStatus:- The reachabilityChanged method was being called.
- I noticed that when first initialized the remoteHostStatus is always NotReachable.
- The internetConnectionStatus returns a positive result before the remoteHostStatus.
- Because I am using an alert, sometimes the alert would be displayed twice or not at all when using one status or the other.
- Open SessionViewController.h and add a variable and property to hold the remoteHostStatus.
NetworkStatus remoteHostStatus;
- Also add a method to initialize the variables.
- (void)initStatus;
#import "FBConnect/FBConnect.h"
#import "PermissionStatus.h"
#import "Reachability.h"
@class FBSession;
@interface SessionViewController : UIViewController
<FBDialogDelegate, FBSessionDelegate, FBRequestDelegate,
PermissionStatusDelegate> {
IBOutlet UILabel* _label;
IBOutlet UIButton* _permissionButton;
IBOutlet UIButton* _feedButton;
IBOutlet FBLoginButton* _loginButton;
FBSession* _session;
PermissionStatus *permissionStatusForUser;
NetworkStatus internetConnectionStatus;
NetworkStatus remoteHostStatus;
}
@property(nonatomic,readonly) UILabel* label;
@property (nonatomic, retain) PermissionStatus *permissionStatusForUser;
@property NetworkStatus internetConnectionStatus;
@property NetworkStatus remoteHostStatus;
- (void)askPermission:(id)target;
- (void)publishFeed:(id)target;
- (void)reachabilityChanged:(NSNotification *)note;
- (void)updateStatus;
- (void)initStatus;
@end
Edit the implementation.- Open SessionViewController.m and add the initStatus method
-(void)initStatus {
self.remoteHostStatus =[[Reachability sharedReachability] remoteHostStatus];
self.internetConnectionStatus =[[Reachability sharedReachability] internetConnectionStatus];
} - Change the viewDidLoad method to call the initStatus method instead of the updateStatus method.
- (void)viewDidLoad {
//Use the Reachability class to determine if the internet can be reached.
[[Reachability sharedReachability] setHostName:kHostName];
//Set Reachability class to notifiy app when the network status changes.
[[Reachability sharedReachability] setNetworkStatusNotificationsEnabled:YES];
//Set a method to be called when a notification is sent.
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(reachabilityChanged:)
name:@"kNetworkReachabilityChangedNotification" object:nil];
[self initStatus];
[_session resume];
_loginButton.style = FBLoginButtonStyleWide;
} - Change the updateStatus method to check both variables.
- (void)updateStatus
{
// Query the SystemConfiguration framework forthe state of the device's network connections.
self.remoteHostStatus =[[Reachability sharedReachability] remoteHostStatus];
self.internetConnectionStatus =[[Reachability sharedReachability] internetConnectionStatus];
NSLog(@"remote status = %d, internet status = %d", self.remoteHostStatus,self.internetConnectionStatus);
if (self.internetConnectionStatus ==NotReachable && self.remoteHostStatus == NotReachable) {
//show an alert to let the user know that they can't connect...
UIAlertView *alert =[[UIAlertView alloc] initWithTitle:@"Network Status"
message:@"Sorry, our network guro determined that the network is not available.
Please try again later." delegate:self cancelButtonTitle:nil
otherButtonTitles:@"OK", nil];
[alert show];
} else {
// If the network is reachable, make sure the login button is enabled.
_loginButton.enabled = YES;
}
}
Here’s the original sample project without the API Keys and Template Bundle IDs. fbconnect-iphone-reachable.zip
No comments:
Post a Comment