Modify Request of webView shouldStartLoadWithRequest: - ios

Currently I am developing an hybrid app which uses webView shouldStartLoadWithRequest: to provide a token for the login. My function works fine for every normal request I make (a click e.g.)
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request
NSLog([NSString stringWithFormat:#"Loading View: %#",[[request URL] absoluteString]]);
if ([[[request URL] absoluteString] rangeOfString:BASE_URL].location != NSNotFound) {
NSString *token = [[NSUserDefaults standardUserDefaults] stringForKey:kDefaultsKeyLoginToken];
NSString *hash = [[NSUserDefaults standardUserDefaults] stringForKey:kDefaultsKeyLoginHash];
NSString *params = [NSString stringWithFormat:#"mobile=app&user_token=%#&user_hash=%#",token,hash];
if([[request URL] query] == nil) {
[self LoadUrl:[[request URL] absoluteString] withGetParams:params append:NO];
return NO;
}else{
if([[[request URL] absoluteString] rangeOfString:params].location == NSNotFound){
[self LoadUrl:[[request URL] absoluteString] withGetParams:params append:YES];
return NO;
}
}
}
-(void)LoadUrl:(NSString *)url withGetParams:(NSString *)params append:(BOOL)append{
NSString *PreUrl;
if(append == YES) PreUrl = [NSString stringWithFormat:#"%#&%#",url,params];
else PreUrl = [NSString stringWithFormat:#"%#?%#",url,params];
NSURL *nsurl = [NSURL URLWithString: PreUrl];
NSURLRequest *request = [NSURLRequest requestWithURL:nsurl];
[self.WebView loadRequest:request];
}
The Problem I have with this Code is that if I load an Image e.g. it will be detected as "to be hashed-appended" (which is correct, I want every request to have the Auth included) BUT the Image will get loaded in the Webview itself.
My first try (before I switched to this model) was to modify the request parsed. But every change got Ignored....
Has anyone an Idea how I could fix this problem? Is there a way to really modify requests? Or if not, can I at least determine the "target" of the request or forward it?
Thanks for any help

I found a Solution for my Problem. Sublcassing was the right approach but not UIWebView but a own NSURLProtocol.
So what I did:
Create an own Sublcass of NSURLProtocol
#interface MyURL : NSURLProtocol <NSURLConnectionDelegate>
Add some standard handling for HTTP-Connections
#interface MyURL () <NSURLConnectionDelegate>
#property (nonatomic, strong) NSURLConnection *connection;
#property (nonatomic, strong) NSMutableData *mutableData;
#property (nonatomic, strong) NSURLResponse *response;
#end
#implementation MyURL
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
return request;
}
- (void)stopLoading
{
[self.connection cancel];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.client URLProtocol:self didLoadData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[self.client URLProtocol:self didFailWithError:error];
self.connection = nil;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[self.client URLProtocolDidFinishLoading:self];
self.connection = nil;
}
#end
And now the interesting part - Modifying every request that goes to my server
So first: Check if this Request goes to my Server and determine if my protocol should take care of it
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
NSString *token = [[NSUserDefaults standardUserDefaults] stringForKey:kDefaultsKeyLoginToken];
NSString *hash = [[NSUserDefaults standardUserDefaults] stringForKey:kDefaultsKeyLoginHash];
if([NSURLProtocol propertyForKey:#"TokenSet" inRequest:request]) return NO; // We already handled it
if((hash == nil) || (token == nil) ) return NO; // We are not logged in
NSString *params = [NSString stringWithFormat:#"mobile=app&user_token=%#&user_hash=%#",token,hash];
if (([[[request URL] absoluteString] rangeOfString:BASE_URL].location != NSNotFound) && ([[[request URL] absoluteString] rangeOfString:#"/assets/"].location == NSNotFound)){
if([[[request URL] absoluteString] rangeOfString:params].location == NSNotFound){
return YES; // URL does not contain the login token & we're not requesting an asset (js/img/etc.)
}
}
return NO;
}
So if + (BOOL)canInitWithRequest:(NSURLRequest *)request returned yes, I have to handle the request. I already know that it does not contain the login token & hash so I've got to determine if it has to be appended or not. To modify the request in general, I create a MutableCopy of our request, Modify it and set our URLConnection to the request.
- (void)startLoading
{
NSMutableURLRequest *newRequest = [self.request mutableCopy];
NSString *PreURL;
NSString *token = [[NSUserDefaults standardUserDefaults] stringForKey:kDefaultsKeyLoginToken];
NSString *hash = [[NSUserDefaults standardUserDefaults] stringForKey:kDefaultsKeyLoginHash];
NSString *params = [NSString stringWithFormat:#"mobile=app&user_token=%#&user_hash=%#",token,hash];
if([[newRequest URL] query] == nil) {
PreURL = [NSString stringWithFormat:#"%#?%#",[[newRequest URL] absoluteString],params];
}else{
if([[[newRequest URL] absoluteString] rangeOfString:params].location == NSNotFound){
PreURL = [NSString stringWithFormat:#"%#&%#",[[newRequest URL] absoluteString],params];
}
}
NSURL *nsurl = [NSURL URLWithString: PreURL];
[newRequest setURL:nsurl];
[NSURLProtocol setProperty:#"YES" forKey:#"TokenSet" inRequest:newRequest];
self.connection = [NSURLConnection connectionWithRequest:newRequest delegate:self];
}
And to finish it all, we register our URL-Protocol as Protocol in AppDelegate.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[NSURLProtocol registerClass:[MyURL class]];
}
With this Solution I first have the advantage of having my login token in ANY request ANY part of my App sends to my Server. No more worries about this. And I can do cool stuff, like saving resources after loading them the first time or even use Images from my App-Bundle in Webviews...
I hope this helps someone.

Related

Unable to load video stream in

I am new to iOS development,
I want to stream video from webapi, The files are streamed from our backend server which requires authentication. It is key-based authenticated set in the Authorization HTTP Header.
I tried with AVPlayer didn't got my output. After doing some more research, i found customProtocol will be more useful do it so i have used customProtocol and tried with this code but the customProtocol is not getting called customProtocol.h, customProtocol.m
[NSURLProtocol registerClass:[MyCustomProtocol class]];
NSString *theURLString = #"customProtocol://abcd.com/download";
player = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL URLWithString:theURLString]];
[self.view addSubview:player.view];
player.view.frame = self.view.frame;
[player play];
Can any one help me here? where i a making mistake?
Thank you in advance!
This is my customProtocol code :
#implementation MyCustomProtocol
+ (BOOL) canInitWithRequest:(NSURLRequest *)request {
NSURL* theURL = request.URL;
NSString* scheme = theURL.scheme;
if([scheme isEqualToString:#"customProtocol"]) {
return YES;
}
return NO;
}
// You could modify the request here, but I'm doing my legwork in startLoading
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
return request;
}
// I'm not doing any custom cache work
+ (BOOL) requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b {
return [super requestIsCacheEquivalent:a toRequest:b];
}
// This is where I inject my header
// I take the handled request, add a header, and turn it back into http
// Then I fire it off
- (void) startLoading {
NSMutableURLRequest* mutableRequest = [self.request mutableCopy];
Constants *constants = [Constants sharedInstance];
[mutableRequest setValue:[NSString stringWithFormat:#"Bearer %#",constants.access_token] forHTTPHeaderField:#"Authorization"];
NSURL* newUrl = [[NSURL alloc] initWithScheme:#"http" host:[mutableRequest.URL host] path:[mutableRequest.URL path]];
[mutableRequest setURL:newUrl];
self.connection = [NSURLConnection connectionWithRequest:mutableRequest delegate:self];
}
- (void) stopLoading {
[self.connection cancel];
}
// Below are boilerplate delegate implementations
// They are responsible for letting our client (the MPMovePlayerController) what happened
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[self.client URLProtocol:self didFailWithError:error];
self.connection = nil;
}
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.client URLProtocol:self didLoadData:data];
}
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed];
}
- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
[self.client URLProtocolDidFinishLoading:self];
self.connection = nil;
}
if you have passed authentication header with url then you can use below code
NSMutableDictionary * headers = [NSMutableDictionary dictionary];
[headers setObject:#"Your UA" forKey:#"User-Agent"];
AVURLAsset * asset = [AVURLAsset URLAssetWithURL:URL options:#{#"AVURLAssetHTTPHeaderFieldsKey" : headers}];
AVPlayerItem * item = [AVPlayerItem playerItemWithAsset:asset];
self.player = [[AVPlayer alloc] initWithPlayerItem:item];
it may work...try it
MPMoviePlayerViewController * movieController = [[MPMoviePlayerViewController alloc] initWithContentURL:url];
movieController.moviePlayer.movieSourceType = MPMovieSourceTypeStreaming;
[self presentMoviePlayerViewControllerAnimated:movieController];
[movieController.moviePlayer play];

WKWebView: trouble using evaluateJavaScript from remote URL requiring Basic Auth

I have a remote JavaScript file that I would like to load from WKWebView. The dev Website the JavaScript file is on requires Basic Auth in order to Access.
The JavaScript file needs needs to load as a result of a button.
In otherwords, I can't use the WKUserScript injectionTime options.
I have two code examples. Both of them only half work. I can't test if the auth works without the EvaluateJavascript working, and I can't test the EvaluateJavascript function without the Basic Auth working... so... using WKWebView * webView...
NSString *authStr = #"username:password";
NSData *authData = [authStr dataUsingEncoding:NSUTF8StringEncoding];
NSString *authValue = [NSString stringWithFormat: #"Basic %#",[authData base64EncodedStringWithOptions:0]];
NSURL* jsURL = [NSURL URLWithString:#"http://dev.xxxx.com/js/xxxxx.js"];
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:jsURL];
[request setValue:authValue forHTTPHeaderField:#"Authorization"];
[_webView loadRequest:request];
I can see the javascript in the webView window, but it is not being evaluated.
Then I have this other strategy:
- (void)handleButton {
NSURL* jsURL = [NSURL URLWithString:#"http://dev.xxxx.com/js/xxxxx.js"];
_scriptString = [NSString stringWithContentsOfURL:jsURL usedEncoding:NSUTF8StringEncoding error:nil];
}
-(void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler{
if (challenge.previousFailureCount == 0){
NSURLCredentialPersistence persistence = NSURLCredentialPersistenceForSession;
NSURLCredential *credential = [NSURLCredential credentialWithUser:#"username" password:#"password" persistence:persistence];
completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
NSLog(#"in Auth");
}
else{
NSLog(#"%s: challenge.error = %#", __FUNCTION__, challenge.error);
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
}
}
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
NSLog(#"navigation complete");
NSLog(#"scriptString %#", _scriptString); //Says UNAUTHORIZED
if ([_scriptString length] > 0) {
[_webView evaluateJavaScript:_scriptString completionHandler:^(NSString *result, NSError *evaluateError) {
if (result == nil) {
NSLog(#"no go dude: %#", evaluateError);
return;
}
NSData *data = [result dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"i think it worked: #%", data);
}];
}
}
Any help would be greatly appreciated!!
I loaded the javascript URL in Safari on my iPhone, and entered the basic auth username and password into the popup.
Then, this is the code that worked:
NSURL *jsURL = [NSURL URLWithString:#"http://username:password#dev.xxxx.com/js/xxxxxx.js"];
NSString *injectedJS = [NSString stringWithContentsOfURL:jsURL encoding:NSUTF8StringEncoding error:nil];
[_webView evaluateJavaScript:injectedJS completionHandler:nil];

My app is asking for permission to “Have offline access”, why?

My app is asking for permission to “Have offline access”, why? It's the weirdest thing. I've done a bit of searching and haven't really found anything that's worked. I've tried using these for scopes:
https://www.googleapis.com/auth/plus.profile.emails.read
https://www.googleapis.com/auth/plus.login
and that didn't seem to help.
Below is a screenshot and some of my code to help you see what's going on:
Some of my code:
#import "ViewController.h"
NSString *callbakc = #"http://localhost/";
NSString *client_id = #“CLIENT ID“;
NSString *scope = #"https://www.googleapis.com/auth/userinfo.email+https://www.googleapis.com/auth/userinfo.profile+https://www.google.com/reader/api/0/subscription";
NSString *secret = #“SECRET”;
NSString *visibleactions = #"http://schemas.google.com/AddActivity";
#interface ViewController () {
NSString *authAccessToken;
UIAlertController *alertController;
}
#property (strong, nonatomic) NSMutableData *receivedData;
#property (weak, nonatomic) IBOutlet UIWebView *webView;
#end
#implementation ViewController
#pragma mark - Lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
NSString *url = [NSString stringWithFormat:#"https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=%#&redirect_uri=%#&scope=%#&data-requestvisibleactions=%#",client_id,callbakc,scope,visibleactions];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:10];
[_webView loadRequest:request];
}
#pragma mark - WebView Delegate
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
[self performSelector:#selector(progressDelay:) withObject:nil afterDelay:0.0];
if ([[[request URL] host] isEqualToString:#"localhost"]) {
// Extract oauth_verifier from URL query
NSString* verifier = nil;
NSArray* urlParams = [[[request URL] query] componentsSeparatedByString:#"&"];
for (NSString* param in urlParams) {
if (![param isEqualToString:#"error=access_denied"]) {
NSArray* keyValue = [param componentsSeparatedByString:#"="];
NSString* key = [keyValue objectAtIndex:0];
if ([key isEqualToString:#"code"]) {
verifier = [keyValue objectAtIndex:1];
// NSLog(#"verifier %#",verifier);
break;
}
}
else {
[self.navigationController popViewControllerAnimated:NO];
}
}
if (!verifier==0) {
[self showAlertViewWithTitle:#"" message:#"Please wait" okAction:NO];
NSString *data = [NSString stringWithFormat:#"code=%#&client_id=%#&client_secret=%#&redirect_uri=%#&grant_type=authorization_code", verifier,client_id,secret,callbakc];
NSString *url = [NSString stringWithFormat:#"https://accounts.google.com/o/oauth2/token"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:[data dataUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPShouldHandleCookies:NO];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
NSLog(#"Connection: %#", theConnection);
self.receivedData = [[NSMutableData alloc] init];
}
else {
// cancel button click
NSLog(#"not Verified!!");
}
return NO;
}
return YES;
}
- (void)webViewDidStartLoad:(UIWebView *)webView {
// show progress
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
[alertController dismissViewControllerAnimated:YES completion:nil];
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
if (error.code==102) //Frame load interrupted
return;
[alertController dismissViewControllerAnimated:YES completion:nil];
[self showAlertViewWithTitle:#"Error" message:[error localizedDescription] okAction:YES];
}
#pragma mark - NSURLConnection Delegate
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
[self showAlertViewWithTitle:#"Error" message:[NSString stringWithFormat:#"%#", error] okAction:YES];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *response = [[NSString alloc] initWithData:self.receivedData encoding:NSUTF8StringEncoding];
NSData *data = [response dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *tokenData = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if ([tokenData objectForKey:#"access_token"]) {
authAccessToken = [tokenData objectForKey:#"access_token"];
[self getUserInfo:authAccessToken];
}
else {
[alertController dismissViewControllerAnimated:YES completion:nil];
NSLog(#"RESULT: %#", tokenData);
[self showAlertViewWithTitle:[tokenData objectForKey:#"name"] message:[NSString stringWithFormat:#"%#", tokenData] okAction:YES];
// Flush all cached data
[[NSURLCache sharedURLCache] removeAllCachedResponses];
}
}
#pragma mark - Private Method Implementation
-(void)getUserInfo:(NSString *)token {
NSString *url = [NSString stringWithFormat:#"https://www.googleapis.com/oauth2/v1/userinfo?access_token=%#",token];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
[request setHTTPMethod:#"GET"];
[request setHTTPShouldHandleCookies:NO];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:request delegate:self];
NSLog(#"Connection: %#", theConnection);
self.receivedData = [[NSMutableData alloc] init];
}
-(void)progressDelay:(id)sender {
// Dismiss progress
}
#end
Any help would be greatly appreciated!
Thank you
This is from https://stackoverflow.com/questions/32210920/why-is-my-app-asking-for-permission-to-have-offline-access?answertab=oldest#tab-top:
This is normal behavior and occurs when the user has granted
permission already.
Basically, no need to worry about it unless you really don't want that
showing up, in that case, you need to un auth the users old token
before requesting a new one.
I'm not exactly sure how because I haven't done this before, but before you authorize a new token you need to un-authorize the old one.
You'll need to modify the -(void)getUserInfo:(NSString *)token method.
For some reason unknown to me. The email scope pops up with
Have offline access
If you want to remove the have offline access remove the email scope. Personally I think it is miss leading to users that you are asking for email access yet are prompted for offline access. Technically speaking all OAuth2 that returns a refresh token gives offline access so the user should always be told that you are getting offline access but it doesnt.

iOS - Async NSURLConnection inside NSOperation

I know this question was asked many times on SO, but I didn't manage to make it work in my project...
So, I want to subclass NSOperation and make it download a file using NSURLConnection. What is the right way to do it?
here is my code which doesn't work:
First, I'm adding all my operations in a loop:
DownloadFileOperation *operation;
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
for (int i=0; i<10; i++) {
operation = [[DownloadFileOperation alloc] init];
operation.urlString = pdfUrlString;
[queue addOperation:operation];
operation = nil; }
And here is my subclass:
#interface DownloadHandbookOperation : NSOperation <NSURLConnectionDelegate>
{
}
#property (strong, nonatomic) NSString *urlString;
#end
#implementation DownloadHandbookOperation
{
NSString *filePath;
NSFileHandle *file;
NSURLConnection * connection;
}
- (void)start
{
if (![NSThread isMainThread])
{
[self performSelectorOnMainThread:#selector(start) withObject:nil waitUntilDone:NO];
return;
}
NSURL *url = [[NSURL alloc] initWithString:[self.urlString stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
[req addValue:#"Basic ***=" forHTTPHeaderField:#"Authorization"];
connection = [[NSURLConnection alloc] initWithRequest:req delegate:self startImmediately:YES];
}
- (void)connection:(NSURLConnection *)conn didReceiveResponse:(NSURLResponse *)response
{
NSString *filename = [[conn.originalRequest.URL absoluteString] lastPathComponent];
filename = [filename stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
filePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:filename];
[[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil];
file = [NSFileHandle fileHandleForUpdatingAtPath:filePath] ;
if (file)
{
[file seekToEndOfFile];
}
else
[self finish];
}
- (void)connection:(NSURLConnection *)conn didReceiveData:(NSData *)data
{
if (file) {
[file seekToEndOfFile];
}
[file writeData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)conn
{
[file closeFile];
[self finish];
}
- (void)connection:(NSURLConnection *)conn didFailWithError:(NSError *)error
{
connection = nil;
[self finish];
}
- (void)cancel
{
[super cancel];
[connection cancel];
}
- (void)finish
{
NSLog(#"operationfinished.");
}
#end
What am I doing wrong?
You need to properly configure your operation to execute as a "concurrent operation"
Concurrency Programming Guide: Configuring Operations for Concurrent Execution
You need to return isConcurrent = YES and properly manage the other state flags, isExecuting and isFinished in a KVO compliant manner.
To illustrate the general idea here is a post from the engineers at Pulse that describes their solution with some easy to follow demo code you can download and review.
Pulse Engineering Blog: Concurrent Downloads using NSOperationQueues **
This code also handles the requirement that NSURLConnection is started on a thread with an active runloop by ensuring that it starts it on the main thread.
(** link is now to archive.org, I think pulse was acquired and have taken their old site down)

Object-c/iOS :How to use ASynchronous to get a data from URL?

My friend saw my code, a part is get a plist data from URL
And he told me not to use Synchronous,Use ASynchronous
But I don't know how to do ASynchronous in simple way
This is the code I use in my program
NSURL *theURL = [[NSURL alloc]initWithString:#"http://someurllink.php" ];
NSURLRequest *theRequest=[NSURLRequest requestWithURL:theURL
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
NSData *returnData = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:nil error:nil];
NSString *listFile = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
self.plist = [listFile propertyList];
[self.tableView reloadData];
[listFile autorelease];
How can I change my code use ASynchronous to get the data ?
Great thanks for all reply and answers : )
Short answer: You can use
+ (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id)delegate;
See NSURLConnectionDelegate for the informal delegate protocol (all methods are optional)
Long answer:
Downloading data asynchronously is not as straightforward as the synchronous method. First you have to create your own data container e.g. a file container
//under documents folder/temp.xml
file = [[SomeUtils getDocumentsDirectory] stringByAppendingPathComponent:#"temp.xml"]
NSFileManager *fileManager = [NSFileManager defaultManager];
if(![fileManager fileExistsAtPath:file]) {
[fileManager createFileAtPath:file contents:nil attributes:nil];
}
When you connect to server:
[NSURLConnection connectionWithRequest:myRequest delegate:self];
You have to fill the container with the data you receive asynchronously:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSFileHandle *fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:file];
[fileHandle seekToEndOfFile];
[fileHandle writeData:data];
[fileHandle closeFile];
}
You have to manage errors encountered using:
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
If you want to capture the server response:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response
Handle when connection finished loading:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
For asynchronous fetch of HTML source code, I recommend you to use AFNetworking
1) Then subclass AFHTTPCLient, for example:
//WebClientHelper.h
#import "AFHTTPClient.h"
#interface WebClientHelper : AFHTTPClient{
}
+(WebClientHelper *)sharedClient;
#end
//WebClientHelper.m
#import "WebClientHelper.h"
#import "AFHTTPRequestOperation.h"
NSString *const gWebBaseURL = #"http://dummyBaseURL.com/";
#implementation WebClientHelper
+(WebClientHelper *)sharedClient
{
static WebClientHelper * _sharedClient = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedClient = [[self alloc] initWithBaseURL:[NSURL URLWithString:gWebBaseURL]];
});
return _sharedClient;
}
- (id)initWithBaseURL:(NSURL *)url
{
self = [super initWithBaseURL:url];
if (!self) {
return nil;
}
[self registerHTTPOperationClass:[AFHTTPRequestOperation class]];
return self;
}
#end
2) Request asynchronously HTML source code, put this code in any relevant part
NSString *testNewsURL = #"http://whatever.com";
NSURL *url = [NSURL URLWithString:testNewsURL];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operationHttp =
[[WebClientHelper sharedClient] HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSString *szResponse = [[[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding] autorelease];
NSLog(#"Response: %#", szResponse );
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"Operation Error: %#", error.localizedDescription);
}];
[[WebClientHelper sharedClient] enqueueHTTPRequestOperation:operationHttp];

Resources