Reachability causes crash after no network situation -- how to properly use Reachability asynchronously
Asked Answered
R

2

7

I was encountering a very strange crash with my iPhone app in development. It seemed that every time I'd show my app to a friend it would crash, but it would never crash otherwise. After being generally mystified by the Murphy's Law aspect, I've determined the pattern of crashes -- the New York City subway. My app crashes consistently after I use the subway. I've traced the problem to my use of Reachability. The app crashes the next time after it is used in a no network situation (not including airplane mode). I'm following Apple's guidelines and checking for for a connection with Reachability before I do any other network operations, but I've found some conflicting documentation about how to call it.

Currently I'm doing something like this:

-(BOOL)reachable {
    Reachability *r = [Reachability reachabilityWithHostName:@"www.stackoverflow.com"];
    NetworkStatus internetStatus = [r currentReachabilityStatus];
    if(internetStatus == NotReachable) {
        return NO;
    }
    return YES;

}

which I'm calling synchronously with a method called from viewDidAppear.

    if ([self reachable]== YES) {
        ... do network stuff ...

which is based on the code from Reachability Guide for iOS 4

My question: is there proper use of Reachability that will take care of this error and handle the absence of a 3G or Wifi network? Do I need to be spawning another thread or do something to remove a synchronous call?

Here, by the way, is the crash log that I see when my app crashes, which leads me to think that it's a synchronous/asynchronous problem.


Application Specific Information:
(app name) failed to resume in time

Elapsed total CPU time (seconds): 3.280 (user 1.770, system 1.510), 33% CPU 
Elapsed application CPU time (seconds): 0.040, 0% CPU

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0:
0   libsystem_kernel.dylib          0x30747fbc kevent + 24
1   libsystem_info.dylib            0x30abec4e _mdns_search + 586
2   libsystem_info.dylib            0x30abfb72 mdns_addrinfo + 370
3   libsystem_info.dylib            0x30abfd68 search_addrinfo + 76
4   libsystem_info.dylib            0x30ac1bcc si_addrinfo + 1080
5   libsystem_info.dylib            0x30abd0b2 getaddrinfo + 78
6   SystemConfiguration             0x311b4256 __SCNetworkReachabilityGetFlags + 962
7   SystemConfiguration             0x311b4f1e SCNetworkReachabilityGetFlags + 98
Rizzi answered 5/5, 2011 at 15:18 Comment(0)
S
5

In the synchronous case, you're probably being killed by the iOS Application Watchdog. That's because to do the reachability check, the SCNetworkReachability functionality needs to do a DNS lookup which can take up to 30 seconds. If check reachability on the main thread (i.e., in viewDidAppear) you block the main thread for a potentially long time, iOS thinks your app is hung and the application watchdog kills it after 20 seconds.

Apple even warns about this in the Reacahbility sample code:

Apple Reachability Sample Code README

Just use notifications like in the Reachability sample app --- it works well and is pretty straightforward once you grok the NSNotificationCenter design pattern.

Good luck!

Sarah answered 23/9, 2011 at 20:11 Comment(1)
Thanks -- so if the main thread was killed, would that be consistent with what I was seeing when I was checking it synchronously? (the application crashes the NEXT time it is run after the synchronous check to reachabiity)Rizzi
R
-1

I solved my problem by setting it up to be asynchronous. I call a method like this

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSTimer *timer = [NSTimer timerWithTimeInterval:0 target:self selector:@selector(loadData) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[pool release];

and the method called looks something like this

- (void)loadData {
    // check for reachability first before starting data load
    if ([self reachable]== NO) {
        // display error message that there is no internet connection, e.g.
        UIAlertView *errorAlert = [[UIAlertView alloc] initWithTitle:@"Connection Error" message:@"Cannot load data.  There is no internet connection." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:@"Retry",nil];
        [errorAlert show];
        [errorAlert release];
    } else {
        // do something to load data from internet ...      
    }

}

using the same reachable code as above.

I would say always use Reachability like this -- the example that Apple gives is incomplete. I've had this code running on a completed app for a few months and it's been very stable.

EDIT: This code is no longer stable as of iOS 5 -- it now can sometimes crash due to "active assertions beyond permitted time". Apple has updated their documentation and example code since I wrote this question, so I suggest following the link in another answer.

Rizzi answered 15/7, 2011 at 14:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.