How to handle reachability in iOS

Issue #209

Here are what I learn about reachability handling in iOS, aka checking for internet connection. Hope you will find it useful, too.

This post starts with techniques from Objective age, but many of the concepts still hold true

The naive way

Some API you already know in UIKit can be used for checking internet connection. Most of them are synchronous code, so you ‘d better call them in a background thread

1
2
3
4
5
6
7
8
- (BOOL)connectedToInternet
{
NSString *string = [NSString stringWithContentsOfURL:[NSURL URLWithString:@"http://www.google.com"]
encoding:NSUTF8StringEncoding
error:nil];

return string ? YES : NO;
}
1
2
3
4
5
6
7
8
9
10
- (BOOL)connectedToInternet
{
NSURL *url = [NSURL URLWithString:@"http://www.google.com"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:@"HEAD"];
NSHTTPURLResponse *response;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error: NULL];

return ([response statusCode] == 200) ? YES : NO;
}

Using SystemConfiguration framework

After importing the SystemConfiguration framework, you can use either SCNetworkReachabilityGetFlags to synchronously get the reachability status, or provide a callback to SCNetworkReachabilitySetCallback to be notified about reachability status change.

Note that SCNetworkReachabilityGetFlags is synchronous.

The System Configuration framework reachability API () operates synchronously by default. Thus, seemingly innocuous routines like SCNetworkReachabilityGetFlags can get you killed by the watchdog. If you’re using the reachability API, you should use it asynchronously. This involves using the SCNetworkReachabilityScheduleWithRunLoop routine to schedule your reachability queries on the run loop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (BOOL) isConnectionAvailable
{
SCNetworkReachabilityFlags flags;
BOOL receivedFlags;

SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(CFAllocatorGetDefault(), [@"dipinkrishna.com" UTF8String]);
receivedFlags = SCNetworkReachabilityGetFlags(reachability, &flags);
CFRelease(reachability);

if (!receivedFlags || (flags == 0) )
{
return FALSE;
} else {
return TRUE;
}
}

Note that SCNetworkReachabilitySetCallback notifies only when reachability status changes

Assigns a client to the specified target, which receives callbacks when the reachability of the target changes

Using some libraries

Libraries make our life easier, but to live well with them, you must surely understand them. There are many reachability libraries on Github, but here I want to mention the most popular: Reachability from tonymillion and AFNetworkReachabilityManager (a submodule of AFNetworking) from mattt. Both use SystemConfiguration under the hood.

Reachability

Some people use Reachability like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- (void)testInternetConnection
{
internetReachableFoo = [Reachability reachabilityWithHostname:@"www.google.com"];

// Internet is reachable
internetReachableFoo.reachableBlock = ^(Reachability*reach)
{
// Update the UI on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"Yayyy, we have the interwebs!");
});
};

// Internet is not reachable
internetReachableFoo.unreachableBlock = ^(Reachability*reach)
{
// Update the UI on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"Someone broke the internet :(");
});
};

[internetReachableFoo startNotifier];
}

Looking into the method “startNotifier”, you will see that it only uses SCNetworkReachabilitySetCallback and it means this callback will only be called if reachability status changes.

If you want to know the reachability status directly, for example, the reachability status at app launch, you must use the method “isReachable”. This method under the hood uses SCNetworkReachabilityGetFlags which is synchronous, and it locks the calling thread.

Reachability has reachabilityForLocalWiFi, which is interesting :)

1
2
3
4
5
6
7
8
9
10
11
+(Reachability*)reachabilityForLocalWiFi
{
struct sockaddr_in localWifiAddress;
bzero(&localWifiAddress, sizeof(localWifiAddress));
localWifiAddress.sin_len = sizeof(localWifiAddress);
localWifiAddress.sin_family = AF_INET;
// IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0
localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);

return [self reachabilityWithAddress:&localWifiAddress];
}

AFNetworkReachabilityManager

With AFNetworkReachabilityManager, all you have to do is

1
2
3
4
5
6
7
- (void)trackInternetConnection
{
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
// Handle the status
}];
}

What is nice about AFNetworkReachabilityManager is that in the “startMonitoring” method, it both uses SCNetworkReachabilitySetCallback and calls AFNetworkReachabilityStatusForFlags to get the initial reachability status in a background thread, and calls the AFNetworkReachabilityStatusBlock. So in the user ‘s point of view, all we care about is the AFNetworkReachabilityStatusBlock handler.

AFNetworking has all the features that Reachability has, and its code is well structured. Another cool thing about it is that it is already in your AFNetworking pod. It’s hard to find projects without AFNetworking these days

isReachableViaWWAN vs isReachableViaWiFi

Take a look at the method AFNetworkReachabilityStatusForFlags and you will know the story

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) {
[...]
status = AFNetworkReachabilityStatusUnknown;
if (isNetworkReachable == NO) {
status = AFNetworkReachabilityStatusNotReachable;
}
#if TARGET_OS_IPHONE
else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) {
status = AFNetworkReachabilityStatusReachableViaWWAN;
}
#endif
else {
status = AFNetworkReachabilityStatusReachableViaWiFi;
}

return status;
}

isReachableViaWWAN is supposed to be for iOS Device

How to use AFNetworkReachabilityManager

I’ve asked a question here Issue 2262, you should take a look at it

The safe way is not to use the sharedManager, but use managerForDomain

1
2
3
4
5
6
7
8
AFNetworkReachabilityManager *afReachability = [AFNetworkReachabilityManager managerForDomain:@"www.google.com"];
[afReachability setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
if (status < AFNetworkReachabilityStatusReachableViaWWAN) {
[FTGAlertView showMessage:@"No internet connection"];
}
}];

[afReachability startMonitoring];

You should read the question 7 and 8 in the Reference below to know more about SCNetworkReachabilityCreateWithName vs SCNetworkReachabilityCreateWithAddress, and about the zero address

Reachability.swift

In Swift, there is this popular Reachability.swift to check for network reachability status

Connectivity

Sometimes, a more robust way is just to ping certain servers, that how’s Connectivy works

Also, read more Solving the Captive Portal Problem on iOS

In order to detect that it has connected to a Wi-Fi network with a captive portal, iOS contacts a number of endpoints hosted by Apple — an example being https://www.apple.com/library/test/success.html. Each endpoint hosts a small HTML page of the form:

Read more

  1. Reachability
  2. AFNetworkReachabilityManager
  3. How to check for internet connection synchronously?
  4. iOS: Check whether internet connection is available
  5. Check if Active Internet Connection Exists on iOS Device
  6. Technical Q&A QA1693 Synchronous Networking On The Main Thread
  7. How to check for network reachability on iOS in a non-blocking manner?
  8. understanding INADDR_ANY for socket programming - c

Understanding weak and strong in Objective C

Issue #164

From my own blog post https://github.com/Fantageek/fantageek.github.io/blob/source/source/_posts/2014-06-27-understanding-weak-self-and-strong-self.markdown


Blocks are wonderful. To avoid retain cycle you often see the weakSelf - strongSelf dance like this

1
2
3
4
5
6
__weak __typeof__(self) weakSelf = self;
self.block = ^{
__typeof__(self) strongSelf = weakSelf;
[strongSelf doSomething];
[strongSelf doSomethingElse];
};

When block is created

Block is object when it is copied

Blocks are created on the stack and will go away when their stack frame returns. While on the stack, a block has no effect on the storage or lifetime of anything it accesses.

If blocks need to exist after the stack frame returns, they can be copied to the heap and this action is an explicit operation. This way, a block will gain reference-counting as all objects in Cocoa. When they are copied, they take their captured scope with them, retaining any objects they refer

Blocks can capture values from the enclosing scope

As well as containing executable code, a block also has the ability to capture state from its enclosing scope. Note that block captures the variable along with its decorators (i.e. weak qualifier),

=> This explains why you need to declare self as __weak

When block is executed

When block is executed, it is possible for weakSelf to be non-nil for the first method (doSomething), but not for the second (doSomethingElse)

You may think, at first, this is a trick to use self inside the block avoiding the retain cycle warning. This is not the case. The strong reference to self is created at block execution time while using self in the block is evaluated at block declaration time, thus retaining the object.

For best practice, however, you should create a strong reference of your object using the weak one. This won’t create a retain cycle either as the strong pointer within the block will only exist until the block completes (it’s only scope is the block itself).

=> This explains why you need to declare another __strong self

More explanation

This is my answer to reader Nikita

  1. As many people point out, “Blocks are created on the stack and will go away when their stack frame returns. While on the stack, a block has no effect on the storage or lifetime of anything it accesses.”
    Even if block (declared on the stack) increase reference count to all the object it accesses, this would be useless, because this block will be discard when function returns

  2. When block are copied (You see that people usually declare property (copy) for block), it will increase reference count to all the objects it accesses.

Why? because block are mean to be executed at a later time, so it need to keep strong reference to all the object it access. Block can be executed MANY TIMES, so IT WON’T RELEASE self AFTER this ran.

When you nil out the block, it will be dealloc, hence it will decrease the reference count to all the objects it access.

AFNetworking nil out the block after it is called, so you don’t have to use weakself inside block http://www.fantageek.com/1376/afnetworking-gotcha-2/

  1. So there are cases when you don’t have to use weakself inside block
    a. Make sure the block is not copied, you simply declare and run it
    b. Make sure the block is nil out after it is called

Reference