Wednesday, June 16, 2010

 

HttpHelper for all

Well, that's flattering: the lead developer of WordPress for iOS (WordPress is the world's most popular blogging software) just emailed me, saying nice things about my previously-posted HttpHelper class, and asking for permission to use it in their project.

And why stop with them? So I hereby formally release HttpHelper under the Apache License 2.0, for anyone to use. I've made a few tweaks to the class since I last posted it - one bug fix, and some synchronous accessors for use when you're already in a background thread - so here it is in its entirety:

HttpHelper.h


// HttpHelper.h
// Created by Jonathan Evans on 04/09/09.
// Copyright 2009 Jonathan Evans.

#import


@interface HttpHelper : NSObject {
}
+(NSURLRequest *) buildRequestWithPostKeys:(NSArray *) postKeys postValues:(NSArray *) postValues urlString:(NSString *)urlString;
+(BOOL) doPost:(NSURLRequest *)request forCaller:(id)caller onSuccess:(SEL)successSelector onFailure:(SEL)failSelector;
-(BOOL) doHttp:(NSDictionary *)args;

+(NSURLRequestCachePolicy)getCachePolicyFor:(NSString *)urlString;
+(NSURLRequest *)getURLRequestFor:(NSString *)urlString;
+(BOOL) doGet:(NSString *)urlString forCaller:(id)caller onSuccess:(SEL)successSelector onFailure:(SEL)failSelector;
+(NSString *) doSynchronizedGet:(NSString *)urlString;
+(NSData *) doSynchronizedDataGet:(NSString *)urlString;
+(NSString *) doSynchronizedPostTo:(NSString *)urlString withKeys:(NSArray*)postKeys andValues:(NSArray*)postValues;

@end


HttpHelper.m


// HttpHelper.m
// Created by Jonathan Evans on 04/09/09.
// Copyright 2009 Jonathan Evans.
//

#import "HttpHelper.h"


@implementation HttpHelper

static HttpHelper *singleton=nil;

#pragma mark -
#pragma mark Singleton methods

+(HttpHelper *) getInstance {
if (singleton==nil)
singleton = [[[HttpHelper alloc] init] autorelease];
return singleton;
}

+(id)allocWithZone:(NSZone *)zone {
if (singleton == nil) {
singleton = [super allocWithZone:zone];
return singleton;
}
return nil;
}

-(id)copyWithZone:(NSZone *)zone {
return self;
}

-(id)retain {
return self;
}

-(unsigned)retainCount {
return UINT_MAX;
}

-(void)release {
//pass
}

-(id)autorelease {
return self;
}

- (void)dealloc {
[super dealloc];
}

#pragma mark -
#pragma mark Business logic

+(NSURLRequest*) buildRequestWithPostKeys:(NSArray *) postKeys postValues:(NSArray *) postValues urlString:(NSString *)urlString {
NSURL * url = [NSURL URLWithString:urlString];
NSURLRequestCachePolicy policy = NSURLRequestReloadIgnoringCacheData; // we never want a cache of a post response
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:policy timeoutInterval:30.0]; //TODO: extend timeout
[request setHTTPMethod:@"POST"];

NSMutableData *paramData = [NSMutableData dataWithCapacity:8192];

NSMutableString *params=[[NSMutableString alloc] initWithCapacity:1024];
for (int i=0; i<[postValues count]; i++) {
NSObject* valueToPost = [postValues objectAtIndex:i];
if (i>0)
[params appendString:@"&"];
[params appendString:[postKeys objectAtIndex:i]];
[params appendString:@"="];
[params appendString:[NSString stringWithFormat:@"%@",valueToPost]];
}

[paramData appendData:[params dataUsingEncoding:NSUTF8StringEncoding]];

NSString *msgLength = [NSString stringWithFormat:@"%d", [paramData length]];
[request addValue: msgLength forHTTPHeaderField:@"Content-Length"];
[request setHTTPBody: paramData];
[params release];

return request;
}

+(BOOL) doPost:(NSURLRequest *)request forCaller:(id)caller onSuccess:(SEL)onSuccess onFailure:(SEL)onFailure {

NSArray *keys = [NSArray arrayWithObjects:@"request", @"caller", @"onSuccess", @"onFailure", nil];
NSArray *values = [NSArray arrayWithObjects:request, caller,
[NSValue valueWithBytes:&onSuccess objCType:@encode(SEL)],
[NSValue valueWithBytes:&onFailure objCType:@encode(SEL)],
nil];
NSDictionary *args = [NSDictionary dictionaryWithObjects:values forKeys: keys];
NSThread* uploadThread = [[NSThread alloc] initWithTarget:[self getInstance] selector:@selector(doHttp:) object:args];
[uploadThread start];
[uploadThread release];
return TRUE;
}

+(NSURLRequestCachePolicy)getCachePolicyFor:(NSString *)urlString {
//Hived out to a separate method because we might fine-tune this later.
return NSURLRequestUseProtocolCachePolicy;
// return NSURLRequestReloadIgnoringCacheData;
}

+(NSURLRequest *)getURLRequestFor:(NSString *)urlString {
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequestCachePolicy policy = [self getCachePolicyFor:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:policy timeoutInterval:30.0];
return request;
}

+(BOOL) doGet:(NSString *)urlString forCaller:(id)caller onSuccess:(SEL)onSuccess onFailure:(SEL)onFailure {
NSURLRequest *request = [self getURLRequestFor:urlString];
NSArray *keys = [NSArray arrayWithObjects:@"request", @"caller", @"onSuccess", @"onFailure", nil];
NSArray *values = [NSArray arrayWithObjects:request, caller,
[NSValue valueWithBytes:&onSuccess objCType:@encode(SEL)],
[NSValue valueWithBytes:&onFailure objCType:@encode(SEL)],
nil];
NSDictionary *args = [NSDictionary dictionaryWithObjects:values forKeys: keys];
NSThread* uploadThread = [[NSThread alloc] initWithTarget:[self getInstance] selector:@selector(doHttp:) object:args];
[uploadThread start];
[uploadThread release];
return TRUE;
}

-(BOOL) doHttp:(NSDictionary *)args
{
@synchronized (self) {
//autorelease pool
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

NSURLRequest *request = [args objectForKey:@"request"];
NSObject *caller = [args objectForKey:@"caller"];

SEL onSuccess;
[[args objectForKey:@"onSuccess"] getValue:&onSuccess];
SEL onFailure;
[[args objectForKey:@"onFailure"] getValue:&onFailure];

NSError *error=nil;
NSHTTPURLResponse *response=nil;
NSData *returnData = [NSURLConnection sendSynchronousRequest: request returningResponse: &response error: &error ];
NSString * responseString = [[[NSString alloc] initWithData: returnData encoding: NSUTF8StringEncoding] autorelease];

NSString *errorString=nil;

if (error)
errorString = [NSString stringWithFormat:@"HTTP error from %@: %@", [request URL], [error localizedDescription]];
else if (response==nil || returnData==nil)
errorString = [NSString stringWithFormat:@"No response from URL %@",[request URL]];
else if (response.statusCode!=200)
errorString = [NSString stringWithFormat:@"Server error from URL %@",[request URL]];

if (errorString && [errorString length]>0) {
NSLog(@"iTravel Error ",errorString);
[caller performSelectorOnMainThread: onFailure withObject:errorString waitUntilDone:NO];
}
else
[caller performSelectorOnMainThread: onSuccess withObject:responseString waitUntilDone:YES];
[pool release];
}
return TRUE;
}

+(NSString *) doSynchronizedGet:(NSString *)urlString {
NSData *returnData = [HttpHelper doSynchronizedDataGet:urlString];
if (!returnData)
return [NSString stringWithFormat: @"Error: No data received from server",urlString];

NSString * responseString = [[[NSString alloc] initWithData: returnData encoding: NSUTF8StringEncoding] autorelease];
return responseString;
}

+(NSData *) doSynchronizedDataGet:(NSString *)urlString {
NSURLRequest *request = [self getURLRequestFor:urlString];
NSHTTPURLResponse *response=nil;
NSError *error=nil;
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

NSString *errorString=@"";
if (error)
errorString = [NSString stringWithFormat:@"Error loading page: %@", urlString, [error localizedDescription]];
else if (response==nil || returnData==nil)
errorString = [NSString stringWithFormat:@"Error - No response from URL %@", urlString];
else if (response.statusCode!=200)
errorString = [NSString stringWithFormat:@"Error - HTTP Error %i from URL %@", response.statusCode, urlString];

if ([errorString length]>0)
return [errorString dataUsingEncoding:NSUTF8StringEncoding];

return returnData;
}

+(NSString *) doSynchronizedPostTo:(NSString *)urlString withKeys:(NSArray*)postKeys andValues:(NSArray*)postValues {
NSURLRequest *request = [self buildRequestWithPostKeys:postKeys postValues:postValues urlString:urlString];
NSHTTPURLResponse *response=nil;
NSError *error=nil;
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

NSString *errorString=@"";
if (error)
errorString = [NSString stringWithFormat:@"Error - HTTP Error posting to URL %@: %@", urlString, [error localizedDescription]];
else if (response==nil || returnData==nil)
errorString = [NSString stringWithFormat:@"Error - No response from URL %@", urlString];
else if (response.statusCode!=200)
errorString = [NSString stringWithFormat:@"Error - HTTP Error %i from URL %@", response.statusCode, urlString];

if ([errorString length]>0)
return errorString;

NSString * responseString = [[[NSString alloc] initWithData: returnData encoding: NSUTF8StringEncoding] autorelease];

if ([[responseString lowercaseString] hasPrefix:@"error"])
errorString = [NSString stringWithFormat:@"Error message %@ from URL %@",responseString, urlString];
else if ([[responseString lowercaseString] hasPrefix:@"fail"])
errorString = [NSString stringWithFormat:@"Error - Failure message '%@' when posting to URL %@", responseString, urlString];

if ([errorString length]>0)
return errorString;

return nil; //indicates success
}

@end


How To Use It

The three chief asynchronous interface methods are, for a GET -


+(BOOL) doGet:(NSString *)urlString forCaller:(id)caller onSuccess:(SEL)onSuccess onFailure:(SEL)onFailure


and for a POST -


+(NSURLRequest*) buildRequestWithPostKeys:(NSArray *) postKeys postValues:(NSArray *) postValues urlString:(NSString *)urlString

+(BOOL) doPost:(NSURLRequest *)request forCaller:(id)caller onSuccess:(SEL)onSuccess onFailure:(SEL)onFailure {


I'll give you an example of the former first, as it's easier. The call itself is perfectly straightforward:


NSMutableString *urlString = [NSMutableString stringWithCapacity:128];
[urlString appendString:[Util getSearchPageURL]];
[urlString appendString:@"?locale="];
[urlString appendString:[UserSettings getLanguage]];
[urlString appendString:@"&searchTerms="];
[urlString appendString:[searchBar.text stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
[HttpHelper doGet:urlString forCaller:self onSuccess:@selector(parseSearchResults:) onFailure:@selector(searchFailed:)];


Not, however, the two "@selector" arguments. These must be methods on the caller, and they better expect an NSString* and an NSError*, respectively, as arguments. (Or they can take an NSObject* and cast from there, I suppose, but why bother, right?)

Inside HttpHelper we have to wrap the selectors in NSValues to pass them from method to method, which is a bit annoying, but hey, we only have to do it once.

To call a POST, by comparison:

NSArray *postKeys = [NSArray arrayWithObjects:@"title", @"location", @"comments", @"pageUri", @"sectionName", @"sectionNumber", @"listingName", nil];
NSArray *postValues = [NSArray arrayWithObjects:note.title, [note locationString], note.body, note.pageUri, note.sectionName, note.sectionNumber, note.listingName, nil];
NSURLRequest *request = [HttpHelper buildRequestWithPostKeys:postKeys postValues:postValues urlString:[Util getUploadNoteURL]];
[HttpHelper doPost:request forCaller:self onSuccess:@selector(onUploadSuccess) onFailure:@selector(onUploadError:)];


Note that "onUploadSuccess" here doesn't take an argument - I don't care about the web site's response, the fact of success is all that matters. OK, dubious wisdom that, but the example is relevant to show that the selector methods don't actually have to accept arguments.

We launch a new thread every time we call these methods, so we have to be careful lest we run into concurrency problems. So we synchronize the actual HTTP calls in the "doHttp:" method, which is the one place in the app where we actually go out and connect to the big bad scary Internet.

The synchronous methods are simple enough that I don't think they require examples:


+(NSString *) doSynchronizedGet:(NSString *)urlString;
+(NSData *) doSynchronizedDataGet:(NSString *)urlString;
+(NSString *) doSynchronizedPostTo:(NSString *)urlString withKeys:(NSArray*)postKeys andValues:(NSArray*)postValues;


The DataGet is in case you want NSData instead of an NSString - if you're loading an image, for instance.

Comments:
Thanks for the good news! As far as I know, localization is the process where you create support for other languages ​​for your application. Often, you first make an application with an English interface and then localize it into other languages, such as Japanese. The localization process is time consuming, and the steps slowly change as Xcode is updated. The Localizable.strings file is where you add translation data as key / value pairs. Earlier versions of Xcode generated a Localizable.strings file by default, which could be easily copied for other languages. Recent versions of Xcode do not create a Localizable.strings file by default. I learned this information from posts on Instagram in which the authors talk about the localization of applications for the iPhone. I've seen enough posts on this topic there and almost all of them had at least 60 thousand likes! I am sure in order to achieve such indicators, their authors resorted to using the services of https://soclikes.com/buy-instagram-likes to quickly cheat the number of likes.
 
Hello everyone, this site is awesome! I express my gratitude for this opportunity to express my thoughts. I wanted to tell you about one difficulty that I had to face recently and how I solved this problem. Recently I had to register for Linked in order to find a good job, and I was pushed by the fact that it is impossible to write to employers there directly if I have no general contacts with them, and recruiters do not really pay attention to my profile if I have few subscribers. After a little search on the Internet, I found an excellent service that helped me acquire subscribers on linkdin, it worked quickly and reliably, and most importantly, not expensive. I recommend all beginner job seekers to use the services of https://viplikes.net/buy-linkedin-followers in order to quickly increase the number of subscribers.
 
Reading this article was very useful-- particularly for me, as it gave me some more insight into writing tips. I wanted to add that the author did a good job of explaining those points clearly. This is particularly helpful for new entrepreneurs because it shows how important it is to consider how everyone perceives your business from the outside. I'm looking forward reading other articles soon.
AWS Training in Hyderabad
AWS Course in Hyderabad

 
Thanks for sharing this informative content.,
Leanpitch provides online training in Agile team facilitation ,everyone can use it wisely.

ICP ATF

Agile facilitator
 
Thanks for sharing this informative content.,
Leanpitch provides online training in Agile team facilitation ,everyone can use it wisely.
Team facilitator in Agile

ICP ATF

 
온라인카지노 Wow, amazing weblog structure! How long have you ever been blogging for? you made running a blog glance easy.

 
바카라사이트 Keep up the amazing works guys I’ve incorporated you guys to blogroll.

 
토토사이트 That is a very good tip particularly to those fresh to the blogosphere.
Short but very accurate information…


 
Hello, I'm happy to see some great articles on your site. Would you like to come to my site later? My site also has posts, comments and communities similar to yours. Please visit and take a look Feel free to visit my website; 배트맨토토


 
You finished certain solid focuses there. I did a pursuit regarding the matter and discovered almost all people will concur with your blog.

 
it was a wonderful chance to visit this kind of site and I am happy to know. thank you so much for giving us a chance to have this opportunity.. data science course in mysore
 
Always get the best suite charges, exclusive resort access, comps for gaming play, and more. You’ll just have 카지노 to activate your on-line account using your Grazie Rewards account number and PIN. By submitting this type, you might be} giving your consent for Crescent School of Gaming and Bartending to contact you regarding our applications and providers.
 
This comment has been removed by the author.
 
The author's ability to simplify complex concepts and present them in an accessible manner is commendable best data analytics courses in chennai.
 
Command is using for string value putting, I know little bit of programming things when it comes to live then you will get the things right on. I am delivering the paid thesis writing service online which is convenient for all the students.
 
مؤسسة الخير لعزل الاسطح بالرياض لتقديم حلول فعالة لمنع تسرب المياه وتحقيق العزل الحراري
شركة عزل اسطح بالرياض
 
تتألق "ماستر كلين" كافضل شركة تنظيف بتبوك بسبب التزامها الدائم بتقديم خدمات استثنائية.
افضل شركة تنظيف بتبوك
 

Post a Comment

Subscribe to Post Comments [Atom]





<< Home

This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]