24/7 twenty-four seven

iOS/OS X application programing topics.

iPhoneアプリケーションで圏外を通知する。

ネットを利用するiPhoneアプリケーションは、圏外の通知が必須に? - 24/7 twenty-four seven

上記の記事で書いたように、現在、ネットを使用するアプリケーションは、ネットワークに接続できないときには、何らかの形でユーザーに知らせないといけないので、僕が「はてな touch」「LDR touch」「テレビ番組表」でやっている方法を書きます。

Reachability

iOS Dev Center - Apple DeveloperにサンプルコードとしてアップルがReachabilityというものを公開しています。
こちらを使うと、ネットワークの状態を簡単に取得することができます。


また、圏外になったときに自動的に通知してくれる機能もついています。

使い方

ReachabilityのプロジェクトからReachability.hとReachability.mをコピーします。


SystemConfiguration.frameworkを追加します。


接続確認のために接続先のアドレスを設定します。
Reachabilityオブジェクトはシングルトンなので、アプリケーションで一度だけ行えばOKです。

    [[Reachability sharedReachability] setHostName:@"reader.livedoor.com"];


アップルのサンプルコードのように、applicationDidFinishLaunching:メソッドで初期設定をして、ネットワークの状態を確認するメソッドをコントローラに実装するのが分かりやすいと思います。

- (void)updateStatus {
    self.remoteHostStatus = [[Reachability sharedReachability] remoteHostStatus];
    self.internetConnectionStatus = [[Reachability sharedReachability] internetConnectionStatus];
    self.localWiFiConnectionStatus = [[Reachability sharedReachability] localWiFiConnectionStatus];
}

- (void)applicationDidFinishLaunching:(UIApplication *)application {
    [[Reachability sharedReachability] setHostName:@"reader.livedoor.com"];

    [self updateStatus];


ネットワークの状態は以下の3種類があります。

typedef enum {
    NotReachable = 0,
    ReachableViaCarrierDataNetwork,
    ReachableViaWiFiNetwork
} NetworkStatus;
  • NotReachable
    • ホスト(接続先)に繋がらない
  • ReachableViaCarrierDataNetwork
    • 3Gネットワークを使って接続できる
  • ReachableViaWiFiNetwork
    • WiFiで接続できる


Reachabilityオブジェクトの各プロパティは、それぞれ上記の3種類の状態をとります。
状況に応じて使い分けます。

  • remoteHostStatus
    • リモートホスト(設定した接続先)に繋がるかどうか
  • internetConnectionStatus
    • インターネットに繋がるかどうか
  • localWiFiConnectionStatus
    • ローカルなWi-Fiネットワークに繋がるかどうか(Bonjourの機器など)


例えば以下では、「インターネットに接続できない」「3Gで接続している」「Wi-Fiで接続している」の3種類を判定しています。

if ([[Reachability sharedReachability] internetConnectionStatus] == NotReachable) {
    NSLog(@"Access Not Available.");
} else if ([[Reachability sharedReachability] internetConnectionStatus] == ReachableViaCarrierDataNetwork) {
    NSLog(@"Available Via Carrier Data Network.");
} else if ([[Reachability sharedReachability] internetConnectionStatus] == ReachableViaWiFiNetwork) {
    NSLog(@"Available Via WiFi Network.");
}

圏外を通知する

たいていのアプリケーションは、接続先は決まっているでしょうから、「リモートホストに繋がるかどうか」だけ判定すればいいと思います。

if ([[Reachability sharedReachability] remoteHostStatus] == NotReachable) {

以下のコードは、データを取得する前に判定をし、接続できない場合はアラートで通知する例です。

- (void)refleshIfNeeded {
    if ([[Reachability sharedReachability] remoteHostStatus] == NotReachable) {
        NSBundle *bundle = [NSBundle mainBundle];
        NSDictionary *infoDictionary = [bundle localizedInfoDictionary];
        NSString *appName = [[infoDictionary count] ? infoDictionary : [bundle infoDictionary] objectForKey:@"CFBundleDisplayName"];
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:appName message:NSLocalizedString(@"NotReachable", nil)
                                                       delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [alert show];	
        [alert release];
        return;
    }
    [hotEntryView reloadData];
}

Tips

networkStatusNotificationsEnabledプロパティをYESに設定すると、ネットワークの状態が変化したときに、通知を受け取れます。

    [[Reachability sharedReachability] setNetworkStatusNotificationsEnabled:YES];


上記の設定をした上で、状態変化の通知を受け取るオブジェクトと、通知を受けたときに呼び出されるメソッドを登録します。
ここで登録したオブジェクトは、解放される前に、自分で登録解除する必要があります。

- (void)viewDidLoad {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityChanged:)
                                                 name:@"kNetworkReachabilityChangedNotification" object:nil];
}

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [super dealloc];
}


「LDR touch」ではオフラインでもダウンロード済みのデータを閲覧することができるので、アラートは出さずに画面の色を変えることで、オフライン状態が分かるようにしています。
どの画面を表示しているときでも同様に処理できるように、通知の仕組みを使っています。

- (void)reachabilityChanged:(NSNotification *)note {
    LoginManager *loginManager = [LDRTouchAppDelegate sharedLoginManager];
    if (loginManager.remoteHostStatus == NotReachable) {
        [refleshButton setEnabled:NO];
        [pinListButton setEnabled:NO];
        [toolBar setBarStyle:UIBarStyleBlackOpaque];
    } else {
        [refleshButton setEnabled:YES];
        [pinListButton setEnabled:YES];
        [toolBar setBarStyle:UIBarStyleDefault];
    }
}