checking network reachability in real time - ios

When the user presses a button, I need to know whether the device is connected to the internet at that very instant--not whether he was connected 3 seconds ago. The reachability (tonymillion) notifier takes about that long to update after there is a change in network reachability.
I thought that I would be able to check for actual access in real time using the following methods:
if (!([[Reachability reachabilityWithHostname:#"www.google.com"] currentReachabilityStatus] == NotReachable)) NSLog(#"reachable");
if ([[Reachability reachabilityWithHostname:#"www.google.com"] currentReachabilityStatus] == NotReachable) NSLog(#"not reachable");
But results indicated that in fact currentReachabilityStatus does not check for internet access; it only checks the same flag that is updated with ~3 seconds' delay.
What's an efficient way of actually checking for network access on the spot?

As you wished in the comments above here is a solution using a "HEAD" request.
Make your class conforming to the
NSURLConnectionDelegate.
Implement the connection:didReceiveResponse: delegate method
Optionally implement the connection:didFailWithError: delegate method
So your setup could look like this:
YourClass.m
#interface YourClass () <NSURLConnectionDelegate>
#property (strong, nonatomic) NSURLConnection *headerConnection;
#end
#implementation YourClass
- (void)viewDidLoad {
// You can do this in whatever method you want
NSMutableURLRequest *headerRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"http://www.google.com"] cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:10.0];
headerRequest.HTTPMethod = #"HEAD";
self.headerConnection = [[NSURLConnection alloc] initWithRequest:headerRequest delegate:self];
}
#pragma mark - NSURLConnectionDelegate Methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
if (connection == self.headerConnection) {
// Handle the case that you have Internet; if you receive a response you are definitely connected to the Internet
}
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// Note: Check the error using `error.localizedDescription` for getting the reason of failing
NSLog(#"Failed: %#", error.localizedDescription);
}

Have you tried putting an observer on the reachability status?
The Reachabilty extension (NPReachability) that I used to use, allows KVO on the status.

Related

MPMoviePlayerController and Auth-Based HLS Backend Server

I am currently serving videos in my iOS application with MPMoviePlayerController. The files are streamed from our backend server that requires authentication. It is key-based authenticated set in the Authorization HTTP Header.
It used to work perfectly with single video files. Now we’re trying to implement HLS adaptive streaming and we have faced a wall. I am currently using a custom NSURLProtocol subclass to catch requests made to our backend server and inject the proper Authorization header. For HLS it simply doesn’t work.
When we looked at the server logs, we clearly saw that the first request to the m3u8 file worked fine. Then all subsequent calls made (other m3u8 files and ts also) are 403 forbidden. It seems that MPMoviePlayerController doesn’t use NSURLProtocol for the other files. (side note: It does work on the simulator thought, but not on a physical device which let me think that both are not implemented in the same way).
MPMoviePlayerController instantiation
self.videoController = [[MPMoviePlayerController alloc] initWithContentURL:video.videoURL];
The URL Protocol interception
+ (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
NSMutableURLRequest *newRequest = request.mutableCopy;
[newRequest setValue:#"HIDDEN" forHTTPHeaderField:#"Authorization"];
return newRequest;
}
Any Ideas, suggestions, work arounds?
After verification with Apple Developer Technical Support, I figured what I wanted to achieve is impossible (and unsupported).
Here's the quote from the reply :
The problem you're seeing with NSURLProtocol and so on is that the movie playback subsystem does not run its HTTP requests within your process. Rather, these requests are run from within a separate system process, mediaserverd. Thus, all your efforts to affect the behaviour of that playback are futile.
By using NSURLProtocol, you can intercept the communication between MPMoviePlayerController and the streamed requests. To inject cookies along the way, or possibly save the stream offline videos. To do this, you should to create a new class extending NSURLProtocol:
Hope this helps you:
GAURLProtocol.h
#import <Foundation/Foundation.h>
#interface GAURLProtocol : NSURLProtocol
+ (void) register;
+ (void) injectURL:(NSString*) urlString cookie:(NSString*)cookie;
#end
GAURLProtocol.m
#import "GAURLProtocol.h"
#interface GAURLProtocol() <NSURLConnectionDelegate> {
NSMutableURLRequest* myRequest;
NSURLConnection * connection;
}
#end
static NSString* injectedURL = nil;
static NSString* myCookie = nil;
#implementation GAURLProtocol
+ (void) register
{
[NSURLProtocol registerClass:[self class]];
}
// public static function to call when injecting a cookie
+ (void) injectURL:(NSString*) urlString cookie:(NSString*)cookie
{
injectedURL = urlString;
myCookie = cookie;
}
+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
if([[[request allHTTPHeaderFields] objectForKey:#"Heeehey"] isEqualToString:#"Huuu"])
{
return NO;
}
return [[[request URL] absoluteString] isEqualToString:injectedURL];
}
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
return request;
}
// intercept the request and handle it yourself
- (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id<NSURLProtocolClient>)client {
if (self = [super initWithRequest:request cachedResponse:cachedResponse client:client]) {
myRequest = request.mutableCopy;
[myRequest setValue:#"Huuu" forHTTPHeaderField:#"Heeehey"]; // add your own signature to the request
}
return self;
}
// load the request
- (void)startLoading {
// inject your cookie
[myRequest setValue:myCookie forHTTPHeaderField:#"Cookie"];
connection = [[NSURLConnection alloc] initWithRequest:myRequest delegate:self];
}
// overload didReceive data
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[[self client] URLProtocol:self didLoadData:data];
}
// overload didReceiveResponse
- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse *)response {
[[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:[myRequest cachePolicy]];
}
// overload didFinishLoading
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[[self client] URLProtocolDidFinishLoading:self];
}
// overload didFail
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[[self client] URLProtocol:self didFailWithError:error];
}
// handle load cancelation
- (void)stopLoading {
[connection cancel];
}
#end
Register
// register protocol
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[NSURLProtocol registerClass:[GAURLProtocol class]];
return YES;
}
Usage
[GAURLProtocol injectURL:#"http://example.com/video.mp4" cookie:#"cookie=f23r3121"];
MPMoviePlayerController * moviePlayer = [[MPMoviePlayerController alloc]initWithContentURL:#"http://example.com/video.mp4"];
[moviePlayer play];
#Marc-Alexandre Bérubé I can think of below workaround:
Run a proxy server in your app to proxy all the video URL's. Download all the video content by injecting the necessary auth headers to the request and relay back the content via the proxy server to the media player to render it. This approach may not work for large videos as the video rendering would only start after entire video is downloaded.

Get NSURLConnection response (from a helper class) inside method of a different class

I have a class, "WebAPI", that handles all web API calls, the class uses NSURLConnection through its asynchronous delegate-based calls.
Whenever an object needs to communicate with the web API it will use an instance of WebAPI and call the required method as shown below in the case of signing in I make the folowing call from the AppDelegate:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
WebAPI *webAPI = [[WebAPI alloc] init];
[webAPI performLoginWithUserName:#"test1#myserver.com" andPassword:#"password"];
}
The problem is that once the performLoginWithUserName:andPassword call is made, the code progresses on and any/all response is received in the delegate methods that are implemented in WebAPI.m.
This is a real issue because I need to be able to get response codes and any data received within the class method from where the call to the WebAPI, originated . I would like to be able to this :
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
WebAPI *webAPI = [[WebAPI alloc] init];
WebAPIResponse * webAPIRespnse = [webAPI performLoginWithUserName:#"test1#myserver.com" andPassword:#"password"];
}
Where WebAPIResponse class is a custom class that will contain the HTTP Status code and any data that is received.
This is achievable if I change WebAPI.m to use NSURLConnection sendSynchronousRequest, but that doesnt enable me to receive all HTTP codes.
What would be the best way to fulfill this requirement?
Thank you for your help.
You could use blocks to handle responses.
For example:
WebApi.h
- (void)performLoginWithUsername:(NSString *)userName
andPassword:(NSString *)password
successBlock:(void(^)(NSData *response))successBlock
failureBlock:(void(^)(NSError *error))failureBlock;
WebApi.m
#interface WebAPI()
#property (nonatomic, copy) void(^authorizationSuccessBlock)(NSData *response);
#property (nonatomic, copy) void(^authorizationFailureBlock)(NSError *error);
#end
#implementation WebAPI
- (void)performLoginWithUsername:(NSString *)userName
andPassword:(NSString *)password
successBlock:(void(^)(NSData *response))successBlock
failureBlock:(void(^)(NSError *error))failureBlock {
self.authorizationSuccessBlock = successBlock;
self.authorizationFailureBlock = failureBlock;
// NSURLConnection call for authorization here
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
if (self.authorizationSuccessBlock != nil) {
self.authorizationSuccessBlock(data);
self.authorizationSuccessBlock = nil;
}
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
if (self.authorizationFailureBlock != nil) {
self.authorizationFailureBlock(error);
self.authorizationFailureBlock = nil;
}
}
AppDelegate.m
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
WebAPI *webAPI = [[WebAPI alloc] init];
[webAPI performLoginWithUserName:#"test1#myserver.com" andPassword:#"password" successBlock:^(NSData *response) {
// Handle result here
} failureBlock:^(NSError *error) {
// Handle error here
}];
}
Change your WebAPI class to provide a delegate interface of its own, or to use completion blocks on the request which are called when the asynchronous connection completes.

How to get response from static library in ios

In my application I am using one static library. In that library I implemented code for establish a connection with the server. For server interaction I used NSURLSession but it's delaying the UI response to avoid it I have started using NSURLConnection delegate methods now I am getting the response from server but here I don't know how to send the response back to actual code from did finish loading method.
In my team I want to distribute this library to both iphone and ipad development engineers. They don't have any control on server related code everything I implemented in static library. So please show me the solution for my problem thanks in advance.
The below is the code which I used in one class of static library:
StaticClass:
.h File
#interface StaticClass : NSObject<NSURLConnectionDelegate,NSURLSessionDelegate>
{
NSMutableDictionary *responseDictionary;
NSUserDefaults *serviceURlInUserDefaults;
NSData *responseData;
}
#property (nonatomic, weak) id <DataReciverDelegate>delegate;
#property(strong,nonatomic)NSData *responseData;
-(void)loginWithUsername:(NSString *)name password:(NSString*)password serviceUrl:(NSString*)serviceUrl domainName:(NSString*)domainName ;
#end
import "StaticClass.h"
#protocol DataReciverDelegate <NSObject>
#required
- (void)responseDictionary:(NSDictionary *)response;
#end
#implementation StaticClass
#synthesize responseData;
-(void)loginWithUsername:(NSString *)name password:(NSString*)password serviceUrl:(NSString*)serviceUrl domainName:(NSString*)domainName
{
NSString *ApiStr=[NSString stringWithFormat:#“http://login.com”];
NSURL *Url=[[NSURL alloc]initWithString:[loginApiStr stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
NSURLRequest *ApiRequest=[NSURLRequest requestWithURL:loginUrl];
NSURLConnection *connection=[[NSURLConnection alloc]initWithRequest:ApiRequest delegate:self];
[connection start];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
self.responseData=data;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
responseDictionary=[NSJSONSerialization JSONObjectWithData:self.responseData options:0 error:nil];
[_delegate responseDictionary:responseDictionary];
}
#end
Response where I want to use is in class1 :
Here please let me know how can i include that delegate which i created in static library class
#interface Class1 : NSObject<NSURLConnectionDelegate,NSURLSessionDelegate>
{
}
#end
#implementation Class1
-(void)login
{
StaticClass *object1=[[StaticClass alloc]init];
[object loginWithUsername:#“AAA” password:#“BBB” serviceUrl:url domainName:dname];
}
You can either offer API to notify that response has been read from the connection, or you can send a notification.
The first can be accomplished by either implementing a delegate protocol and setting the delegate in the using app, or by using block-based API, where the using app would set a block to handle events. You see these two patterns very often in system-provided API, including NSUrlConnection.
Another option is to use notifications. You register for a particular notification name in the using app, and in the lib you post once your connection returns data.
You need to implement a protocol in your static library like:
#protocol DataReciverDelegate <NSObject>
#required
- (void)dataReceived:(NSData *)data;
#end
Also declare a property there like:
#property (nonatomic, weak) id <DataReciverDelegate>delegate;
In your static library implementation, implement the connectionDidFinishLoading like:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[_delegate dataReceived:_dataYouReceived];
}
Now you need to implement the DataReciverDelegate in the class you need to get the data, and When you create the object of your static library class, set the delegate.

Create category so that an object can become a delegate for another that normally wouldn't be able to

I have a UIWebView, and it would make my life a lot simpler if it could be a delegate for an NSURLConnection. I have made a category like this
#interface UIWebView (NSURLConnectionDelegate) <NSURLConnectionDelegate>
//these methods are used by the NSURLConnection, and are implemented in the .m
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection;
#end
the methods in the implementation do work, since i had another view that was the delegate before, but now i need to change it because of reasons.
i give the NSURLConnection a delegate like so
#import "UIWebView+NSURLConnectionDelegate.h"
[[NSURLConnection alloc] initWithRequest:request delegate:webview]; //used to be self but now i need the webview to know about its own connection because there are multiple webviews
but none of the delegate methods get called when its a category like this.
has anyone done something like this before or does this not work because the NSURLConnection thinks that webview isnt actually a delegate or something?
edit to show some more code:
- (BOOL)webView:(UIWebView *)webview shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSLog(#"shouldStartLoadWithRequest %# %d", request.URL.absoluteURL.description, navigationType);
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
if (!authed) {
authed = NO; //gets set to yes when delegate methods work (also are some print outs in the delegate methods which are not printing at all)
urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:webview];
return NO;
}
return YES;
}

consuming restful web service in iOS 5

In my first ViewController (MonitorViewController) this is in the interface file MonitorViewController.h:
#import <RestKit/RestKit.h>
#interface MonitorViewController : UIViewController <RKRequestDelegate>
In MonitorViewController.m ViewDidLoad method, I have this at the end:
RKClient* client = [RKClient clientWithBaseURL:#"http://192.168.2.3:8000/DataRecorder/ExternalControl"];
NSLog(#"I am your RKClient singleton : %#", [RKClient sharedClient]);
[client get:#"/json/get_Signals" delegate:self];
The implementation of delegate methods in MonitorViewController.m:
- (void) request: (RKRequest *) request didLoadResponse: (RKResponse *) response {
if ([request isGET]) {
NSLog (#"Retrieved : %#", [response bodyAsString]);
}
}
- (void) request:(RKRequest *)request didFailLoadWithError:(NSError *)error
{
NSLog (#"Retrieved an error");
}
- (void) requestDidTimeout:(RKRequest *)request
{
NSLog(#"Did receive timeout");
}
- (void) request:(RKRequest *)request didReceivedData:(NSInteger)bytesReceived totalBytesReceived:(NSInteger)totalBytesReceived totalBytesExectedToReceive:(NSInteger)totalBytesExpectedToReceive
{
NSLog(#"Did receive data");
}
My AppDelegate method DidFinishLaunchingWithOptions method only returns YES and nothing else.
I recommend using RestKit framework. With restkit, you simply do:
// create the parameters dictionary for the params that you want to send with the request
NSDictionary* paramsDictionary = [NSDictionary dictionaryWithObjectsAndKeys: #"00003",#"SignalId", nil];
// send your request
RKRequest* req = [client post:#"your/resource/path" params:paramsDictionary delegate:self];
// set the userData property, it can be any object
[req setUserData:#"SignalId = 00003"];
And then, in the delegate method:
- (void)request:(RKRequest *)request didLoadResponse:(RKResponse *)response {
// check which request is responsible for the response
// to achieve this, you can do two things
// check the parameters of the request like this
NSLog(#"%#", [request URL]); // this will print your request url with the parameters
// something like http://myamazingrestservice.org/resource/path?SignalId=00003
// the second option will work if your request is not a GET request
NSLog(#"%#", request.params); // this will print paramsDictionary
// or you can get it from userData if you decide to go this way
NSString* myData = [request userData];
NSLog(#"%#", myData); // this will log "SignalId = 00003" in the debugger console
}
So you will never need to send the parameters that are not used on the server side, just to distinguish your requests. Additionally, the RKRequest class has lots of other properties that you can use to check which request corresponds to the given response. But if you send a bunch of identical requests, I think the userData is the best solution.
RestKit will also help you with other common rest interface tasks.

Resources