2

Writing an iOS app, I neet to give the user the option to block the network access for this app only. Is it possible doing that in code?

Meaning every call, made by whatever part of the code (and that's including static libraries) should be blocked for that particular moment.

Rizon
  • 1,516
  • 4
  • 25
  • 45

3 Answers3

7

You can use a custom NSURLProtocol that will intercept all your network calls.

That's exactly what I do in my OHHTTPStubs library to stub network requests (my library use then private API to simulate a network response, but in your case if you don't need to fake a response you can avoid these calls to the private API and use this technique in production code)

[EDIT] Since this answer, OHHTTPStubs has been updated and don't use any private API anymore, so you can use it even in production code. See my EDIT below at the end of this answer for some code example.


@interface BlockAllRequestsProtocol : NSURLProtocol
@end

@implementation BlockAllRequestsProtocol
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
    return YES; // Intercept all outgoing requests, whatever the URL scheme
    // (you can adapt this at your convenience of course if you need to block only specific requests)
}

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request { return request; }
- (NSCachedURLResponse *)cachedResponse { return nil; }

- (void)startLoading
{
    // For every request, emit "didFailWithError:" with an NSError to reflect the network blocking state
    id<NSURLProtocolClient> client = [self client];
    NSError* error = [NSError errorWithDomain:NSURLErrorDomain
                                         code:kCFURLErrorNotConnectedToInternet // = -1009 = error code when network is down
                                     userInfo:@{ NSLocalizedDescriptionKey:@"All network requests are blocked by the application"}];
    [client URLProtocol:self didFailWithError:error];
}
- (void)stopLoading { }

@end

And then to install this protocol and block all your network requests:

[NSURLProtocol registerClass:[BlockAllRequestsProtocol class]];

And to later uninstall it and let your network requests hit the real world:

[NSURLProtocol unregisterClass:[BlockAllRequestsProtocol class]];

[EDIT] Since my answer, I've updated my library which does not use any private API anymore. So anyone can use OHHTTPStubs directly, even for the usage you need, like this:

[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest* request) {
    return YES; // In your case, you want to prevent ALL requests to hit the real world
} withStubResponse:^OHHTTPStubsResponse*(NSURLRequest* request) {
    NSError* noNetworkError = [NSError errorWithDomain:NSURLErrorDomain
                    code:kCFURLErrorNotConnectedToInternet userInfo:nil];
    return [OHHTTPStubsResponse responseWithError:noNetworkError];
}];
Rhythmic Fistman
  • 34,352
  • 5
  • 87
  • 159
AliSoftware
  • 32,623
  • 6
  • 82
  • 77
  • How would I use it to block only certain calls (lets say, based on the base url) ? – Rizon Jun 15 '13 at 11:19
  • Simply implement the `canInitWithRequest:` method accordingly (instead of returning `YES`, return `[request.URL.absoluteString hasPrefix:...]` for example). _For more information, don't hesitate to read the `NSURLProtocol` documentation that explains it all._ – AliSoftware Jun 15 '13 at 11:24
  • This is VERY close to what I need, but still not exactly; as returning this error code doesn't 100% mimic a no-connectivity situation. I need that static lib to think there's no connection at all. Maybe we should go deeper than NSURLProtocol? – Rizon Jun 15 '13 at 12:35
  • When you don't have any internet connection, you will get an NSError with code kCFURLErrorNotConnectedToInternet = -1009 (I just tested it to confirm). So you can simply mimic that. (I should have used -1009 instead of 500, will edit my answer) – AliSoftware Jun 15 '13 at 12:50
  • The lib still reacts differently..something, somewhere is not identical.. :\ – Rizon Jun 15 '13 at 13:08
  • Maybe you should investigate on that "act differently". Do the requests of your lib still hits the real world? Does it goes thru your `NSURLProtocol` code when a lib's request is triggered? Are you sure the lib does not use low-level networking like sockets? (I can't investigate and debug all that for you…) – AliSoftware Jun 15 '13 at 13:31
  • You're absolutely right! And I did all those tests..the connection indeed goes through the , and I managed to return my 'fail' response. Perhaps there are other calls that don't, maybe there's a usual reachability test that responsible for the difference. I guess I need to snif it and come back with better understanding..stay tuned.. :) – Rizon Jun 15 '13 at 13:36
0

I dont think you can make the network connection blocked progamatically

Work Around

Keep a bool variable for blocking network.Check on every network call the value on variable.If block is set to yes Dont call any webservice.

Lithu T.V
  • 19,955
  • 12
  • 56
  • 101
  • Could take control of HTTP and HTTPS using NSURLProtocol and subclass the start/stop loading methods to your desire – Shams Ahmed Jun 15 '13 at 10:15
0

I'm using a crittercism library that does network analysis on every single network call within the app, no matter if it's your own code or a third party library.

It's closed source, but from what I've seen from the occasional stack track, they are method swizzling methods on CFNetwork classes. These are pretty low level network classes which will be used by the higher level apis such as NSURLConnection (this is just my assumption).

So I'd start by looking there.

bandejapaisa
  • 26,576
  • 13
  • 94
  • 112
  • do you use their basic or pro plan? – Or Arbel Jun 15 '13 at 11:45
  • If they are swizzling low level methods on CFNetwork, I would recommend not using the library. – bbum Jun 15 '13 at 15:52
  • I'm not positive they are doing this, but I remember seeing the word 'swizzle' when i was evaluating their forthcoming network product. This code isn't in their stable release. – bandejapaisa Jun 15 '13 at 19:14