I have a PHP RESTful server that I connect to to invoke methods based on the URL path called from my objective-c program. I use the ASIHTTPRequest and SBJson.
Everything works well but I'm doing requests in different controllers and now duplicated methods are in all those controllers. What is the best way to abstract that code (requests) in a single class, so I can easily re-use the code, so I can keep the controllers more simple.
Please save me from what I feel to be a redundant and repetitive coding sequence.
I have tried to make a singleton class, but each view controller requires different request connect points, and most view controllers require specific actions to be called when the succeeded method is called and Im not sure how to handle it all.
Here is what my code template currently looks like in the Objective-c program:
//*1) Somewhere at the top of my .m file I have this*
//These are the suffixes to the URL path that I connect to at my server,
//depending on the action required
NSString *const RequestCreateCustomer = #"Create/Customer";
NSString *const RequestUpdateCustomer = #"Update/Customer";
NSString *const RequestDeleteCustomer = #"Delete/Customer";
//*2) Then I have my connection invocation code*
//Method invocation, all of them look something like this
-navigationButton{
...
[self retrieveWithRequestStringType:RequestUpdateCustomer];
}
-(void)retrieveWithRequestStringType:(NSString*)typeOfRequest{
NSLog(#"Retrieve %# method called", typeOfRequest);
NSString *urlString = [NSString stringWithFormat:#"%#/Secure/CB/%#", #"http://www.defaultStapleURLToMyServer.com/CB", typeOfRequest];
NSString *encodedUrlString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [[NSURL alloc] initWithString:encodedUrlString];
serverRequest = nil;
serverRequest = [ASIFormDataRequest requestWithURL:url];
[serverRequest addRequestHeader:#"Content-Type" value:#"application/json"];
[serverRequest addRequestHeader:#"Request-Method" value:#"POST"];
//Normally at this point depending on the request type, I prepare some data that needs to be sent along with the request
NSMutableDictionary *completeDataArray = [[NSMutableDictionary alloc] init];
if([typeOfRequest isEqualToString:RequestCreateCustomer]){
[serverRequest setUserInfo:[NSDictionary dictionaryWithObject:RequestCreateCustomer forKey:#"RequestType"]];
if( ! [self validateAndPrepareAllData:&completeDataArray]){
return;
}
}
else if([typeOfRequest isEqualToString:RequestUpdateCustomer]){
[serverRequest setUserInfo:[NSDictionary dictionaryWithObject:RequestUpdateCustomer forKey:#"RequestType"]];
if( ! [self validateAndPrepareAllData:&completeDataArray]){
return;
}
}
else if([typeOfRequest isEqualToString:RequestDeleteCustomer]){
[serverRequest setUserInfo:[NSDictionary dictionaryWithObject:RequestDeleteCustomer forKey:#"RequestType"]];
if( ! [self validateAndPrepareCustomerIdData:&completeDataArray]){
return;
}
}
NSString *jsonString = [completeDataArray JSONRepresentation];
[serverRequest appendPostData:[jsonString dataUsingEncoding:NSUTF8StringEncoding]];
[serverRequest setDelegate:self];
[serverRequest setDidFinishSelector:#selector(requestSucceeded:)];
[serverRequest setDidFailSelector:#selector(requestFailed:)];
[serverRequest startAsynchronous];
}
//*3) And heres the connection did succeed, and connection did fail methods*
-(void)requestSucceeded:(ASIHTTPRequest*)request{
NSInteger statusCode = [[[request responseHeaders] objectForKey:#"StatusCode"] intValue];
NSLog(#"StatusCode: %#", [[request responseHeaders] objectForKey:#"StatusCode"]);
NSString *myString = [[NSString alloc] initWithData:[request responseData] encoding:NSUTF8StringEncoding];
NSDictionary *JSONDictionary = [myString JSONValue];
switch (statusCode) {
case 400:
case 401:
{
NSLog(#"display error message");
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:[[request.responseString JSONValue] objectForKey:#"Message"] delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[alert show];
break;
}
case 200:
NSLog(#"status code = 200 so successful");
//Created customer request succeeded
if([[[request userInfo] objectForKey:#"RequestType"] isEqualToString:RequestCreateCustomer]){
[self.delegate addedCustomerVCWithCustomer:[[CustomerDataModel alloc] initWithJSONData:[JSONDictionary objectForKey:#"CustomerObject"]]];
[self cancelModalView];
}
//Edit customer request succeeded
else if([[[request userInfo] objectForKey:#"RequestType"] isEqualToString:RequestUpdateCustomer]){
[self.delegate editedCustomerVCWithCustomer:[[CustomerDataModel alloc] initWithJSONData:[JSONDictionary objectForKey:#"CustomerObject"]]];
[self cancelModalView];
}
//Delete customer request succeeded
else if([[[request userInfo] objectForKey:#"RequestType"] isEqualToString:RequestDeleteCustomer]){
[self.delegate deletedCustomer:customer];
[self cancelModalView];
}
break;
default:
[SVProgressHUD showErrorWithStatus:[NSString stringWithFormat:#"went to none: %d or %#", (int)statusCode, [[request responseHeaders] objectForKey:#"StatusCode"]]];
NSLog(#"went to none: %d or %#", (int)statusCode, [[request responseHeaders] objectForKey:#"StatusCode"]);
break;
}
[SVProgressHUD dismiss];
}
-(void)requestFailed:(ASIHTTPRequest*)request{
if([[[request userInfo] objectForKey:#"RquestType"] isEqualToString:RequestCreateCustomer]){
NSLog(#"retrieving states failed so trying again");
[self addCustomerRequest];
}
else if(){
//... you get the point
}
}
Can someone help out with an alternative solution?
create a new class that has blocks/delegate (but prefers block) on it.. let's say Request.h and Request.m.. and make a public method with a block parameter.. example
this should be public methods
typedef void(^CompletionBlock)(id results);
typedef void(^ErrorBlock)(NSError *error);
- (void)setCompetionBlock:(CompletionBlock)block; and
- (void)setErrorBlock:(ErrorBlock)error;
and make a variable name.. this should be private variables
CompletionBlock completionBlock;
ErrorBlock errorBlock;
and declare this methods in your .m
- (void)setCompletionBlock:(CompletionBlock)aCompletionBlock {
completionBlock = [aCompletionBlock copy];
}
- (void)setErrorBlock:(ErrorBlock)aError {
errorBlock = [aError copy];
}
- (void)reportSuccess:(id)results {
if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock(results);
});
}
}
- (void)reportFailure:(NSError *)error {
if (errorBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
errorBlock(error);
});
}
}
and call [self reportSuccess:jsonSuccess]; under your success methods, in your code it is -(void)requestSucceeded:(ASIHTTPRequest*)request{ and [self reportFailure:NSerror*] under -(void)requestFailed:(ASIHTTPRequest*)request
and to call this class
Request *req = [Request alloc]init];
[req retrieveWithRequestStringType:#"sample"];
[req setCompletion:^(id result){
}];
[req setErrorBlock:(NSError *error) {
}];
Related
I've been trying to get a value from inside a block for a few hours now, I can't understand how to use the handlers on completion and literally everything.
Here's my code:
+ (void)downloadUserID:(void(^)(NSString *result))handler {
//Now redirect to assignments page
__block NSMutableString *returnString = [[NSMutableString alloc] init]; //'__block' so that it has a direct connection to both scopes, in the method AND in the block
NSURL *homeURL = [NSURL URLWithString:#"https://mistar.oakland.k12.mi.us/novi/StudentPortal/Home/PortalMainPage"];
NSMutableURLRequest *requestHome = [[NSMutableURLRequest alloc] initWithURL:homeURL];
[requestHome setHTTPMethod:#"GET"]; // this looks like GET request, not POST
[NSURLConnection sendAsynchronousRequest:requestHome queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *homeResponse, NSData *homeData, NSError *homeError) {
// do whatever with the data...and errors
if ([homeData length] > 0 && homeError == nil) {
NSError *parseError;
NSDictionary *responseJSON = [NSJSONSerialization JSONObjectWithData:homeData options:0 error:&parseError];
if (responseJSON) {
// the response was JSON and we successfully decoded it
//NSLog(#"Response was = %#", responseJSON);
} else {
// the response was not JSON, so let's see what it was so we can diagnose the issue
returnString = (#"Response was not JSON (from home), it was = %#", [[NSMutableString alloc] initWithData:homeData encoding:NSUTF8StringEncoding]);
//NSLog(returnString);
}
}
else {
//NSLog(#"error: %#", homeError);
}
}];
//NSLog(#"myResult: %#", [[NSString alloc] initWithData:myResult encoding:NSUTF8StringEncoding]);
handler(returnString);
}
- (void)getUserID {
[TClient downloadUserID:^(NSString *getIt){
NSLog([NSString stringWithFormat:#"From get userID %#", getIt]);
}];
}
So I'm trying to NSLog the returnString from the downloadUserID method.
I first tried returning, then I realized you can't do a return from inside a block. So now I've been trying to do it with the :(void(^)(NSString *result))handler to try and access it from another class method.
So I'm calling downloadUserID from the getUserID method, and trying to log the returnString string. It just keeps going to nil. It just prints From get userID and nothing else.
How do I access the returnString that's inside the block of the downloadUserID method?
The problem is not the block itself, the problem is realizing that the block is executed asynchronously.
In your code, at the time you call handler(returnString); the block is probably still executing on another thread, so there's no way you can catch the value at this point.
Probably what you want to do is move that line inside the block (probably at the end, before the closing braces).
You can do this if you write such a wrapper.
In this situation, you need a while loop that will wait for a response from the block.
Method which shoud return value of enum
- (RXCM_TroubleTypes) logic_getEnumValueOfCurrentCacheProblem
{
RXCM_TroubleTypes result = RXCM_HaveNotTrouble;
NetworkStatus statusConnection = [self network_typeOfInternetConnection];
RXCM_TypesOfInternetConnection convertedNetStatus = [RXCM convertNetworkStatusTo_TypeOfInternetConnection:statusConnection];
BOOL isAllowed = [self someMethodWith:convertedNetStatus];
if (isAllowed){
return RXCM_HaveNotTrouble;
}else {
return RXCM_Trouble_NotSuitableTypeOfInternetConnection;
}
return result;
}
Method which calls delegate's method with block.
And waits answer from it.
Here I use while loop. Just check every 0.5sec answer from block
- (BOOL) isUserPermissioned:(RXCM_TypesOfInternetConnection)newType
{
__block BOOL isReceivedValueFromBlock = NO;
__block BOOL result = NO;
__block BOOL isCalledDelegateMethod = NO;
dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_sync(aQueue,^{
while (!isReceivedValueFromBlock) {
NSLog(#"While");
if (!isCalledDelegateMethod){
[self.delegate rxcm_isAllowToContinueDownloadingOnNewTypeOfInternetConnection:newType
completion:^(BOOL isContinueWorkOnNewTypeOfConnection) {
result = isContinueWorkOnNewTypeOfConnection;
isReceivedValueFromBlock = YES;
}];
isCalledDelegateMethod = YES;
}
[NSThread sleepForTimeInterval:0.5];
}
});
return result;
}
Delegate's method in ViewController
- (void) rxcm_isAllowToContinueDownloadingOnNewTypeOfInternetConnection:(RXCM_TypesOfInternetConnection)newType
completion:(void(^)(BOOL isContinueWorkOnNewTypeOfConnection))completion
{
__weak ViewController* weak = self;
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:#"Alert"
message:#"to continue download on the new type of connection"
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ok = [UIAlertAction actionWithTitle:#"YES" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completion(YES);
}];
UIAlertAction *cancel = [UIAlertAction actionWithTitle:#"NO" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completion(NO);
}];
[alert addAction:cancel];
[alert addAction:ok];
[weak presentViewController:alert animated:YES completion:nil];
});
}
I have my iOS app working fine till now but today I faced the problem "We were unable to find the authorization token." which is happening sometimes(most of the time it is working fine).Strangely when I tried to login my LinkedIn account on my desktop I faced the same issue for the first time.I have been implementing same code in many apps but all were working fine.But today I got this issue in my new app.
Code Snippet:
Request Token:
- (void)requestTokenFromProvider
{
LOAMutableURLRequest *request =
[[[LOAMutableURLRequest alloc] initWithURL:requestTokenURL
consumer:self.consumer
token:nil
callback:linkedInCallbackURL
signatureProvider:nil] autorelease];
[request setHTTPMethod:#"POST"];
LOARequestParameter *nameParam = [[LOARequestParameter alloc] initWithName:#"scope" value:#"r_fullprofile+w_messages+r_network+r_emailaddress+rw_nus"];
NSArray *params = [NSArray arrayWithObjects:nameParam, nil];
[request setParameters:params];
LOARequestParameter * scopeParameter=[LOARequestParameter requestParameter:#"scope" value:#"r_fullprofile w_messages r_network r_emailaddress rw_nus"];
[request setParameters:[NSArray arrayWithObject:scopeParameter]];
LOADataFetcher *fetcher = [[[LOADataFetcher alloc] init] autorelease];
[fetcher fetchDataWithRequest:request
delegate:self
didFinishSelector:#selector(requestTokenResult:didFinish:)
didFailSelector:#selector(requestTokenResult:didFail:)];
}
- (void)requestTokenResult:(LOAServiceTicket *)ticket didFinish:(NSData *)data
{
if (ticket.didSucceed == NO)
return;
NSString *responseBody = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
self.requestToken = [[LOAToken alloc] initWithHTTPResponseBody:responseBody];
[responseBody release];
[self allowUserToLogin];
}
- (void)requestTokenResult:(LOAServiceTicket *)ticket didFail:(NSData *)error
{
NSLog(#"%#",[error description]);
}
Linkedin Login Page And Access Token:
- (void)allowUserToLogin
{
NSString *userLoginURLWithToken = [NSString stringWithFormat:#"%#?oauth_token=%#",
userLoginURLString, self.requestToken.key];
userLoginURL = [NSURL URLWithString:userLoginURLWithToken];
NSURLRequest *request = [NSMutableURLRequest requestWithURL: userLoginURL];
[webView loadRequest:request];
}
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
{
NSURL *url = request.URL;
NSString *urlString = url.absoluteString;
addressBar.text = urlString;
[activityIndicator startAnimating];
BOOL requestForCallbackURL = ([urlString rangeOfString:linkedInCallbackURL].location != NSNotFound);
if ( requestForCallbackURL )
{
BOOL userAllowedAccess = ([urlString rangeOfString:#"user_refused"].location == NSNotFound);
if ( userAllowedAccess )
{
[self.requestToken setVerifierWithUrl:url];
[self accessTokenFromProvider];
}
else
{
// User refused to allow our app access
// Notify parent and close this view
// [[NSNotificationCenter defaultCenter]
// postNotificationName:#"loginViewDidFinish"
// object:self
// userInfo:nil];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"LinkedInCancelled"];
[[ImpActivityAgent defaultAgent] makeBusy:NO];
[self dismissViewControllerAnimated:NO completion:nil];
}
}
else
{
// Case (a) or (b), so ignore it
}
return YES;
}
- (void)accessTokenFromProvider
{
[[NSUserDefaults standardUserDefaults] setObject:self.consumer forKey:#"LinkedinConsumer"];
LOAMutableURLRequest *request =
[[[LOAMutableURLRequest alloc] initWithURL:accessTokenURL
consumer:self.consumer
token:self.requestToken
callback:nil
signatureProvider:nil] autorelease];
[request setHTTPMethod:#"POST"];
LOADataFetcher *fetcher = [[[LOADataFetcher alloc] init] autorelease];
[fetcher fetchDataWithRequest:request
delegate:self
didFinishSelector:#selector(accessTokenResult:didFinish:)
didFailSelector:#selector(accessTokenResult:didFail:)];
}
- (void)accessTokenResult:(LOAServiceTicket *)ticket didFinish:(NSData *)data
{
NSString *responseBody = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
[[NSUserDefaults standardUserDefaults] setObject:responseBody forKey:#"AccessTokenresponseBody"];
BOOL problem = ([responseBody rangeOfString:#"oauth_problem"].location != NSNotFound);
if ( problem )
{
NSLog(#"%#",responseBody);
}
else
{
self.accessToken = [[LOAToken alloc] initWithHTTPResponseBody:responseBody];
[[NSUserDefaults standardUserDefaults] setObject:responseBody forKey:#"accessToken"];//save here
[[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:#"TokenRefreshDate"];//save here
[[NSUserDefaults standardUserDefaults] synchronize];
}
// Notify parent and close this view
[[NSNotificationCenter defaultCenter]
postNotificationName:#"loginViewDidFinish"
object:self];
[self dismissViewControllerAnimated:NO completion:nil];
[responseBody release];
}
Request Urls:
- (void)initLinkedInApi
{
apikey = #"vwu3pmtjaxyz";
secretkey = #"XkPxP1DNANMg0Dzq";
self.consumer = [[LOAConsumer alloc] initWithKey:apikey
secret:secretkey
realm:#"http://api.linkedin.com/"];
requestTokenURLString = #"https://api.linkedin.com/uas/oauth/requestToken";
accessTokenURLString = #"https://api.linkedin.com/uas/oauth/accessToken";
userLoginURLString = #"https://www.linkedin.com/uas/oauth/authorize";
linkedInCallbackURL = #"hdlinked://linkedin/oauth";
requestTokenURL = [[NSURL URLWithString:requestTokenURLString] retain];
accessTokenURL = [[NSURL URLWithString:accessTokenURLString] retain];
userLoginURL = [[NSURL URLWithString:userLoginURLString] retain];
}
Can somebody please tell me what can be reason behind it?
I have posted this question to LinkedIn forum too where I got the response that there was some issue from their side as other other developers have also reported them the issue.
As per Kamyar Mohager LinkedIn Employee
Is this happening when you're authorizing a new user or are you trying to make calls with existing access tokens? Based on that error message, my assumption is that you're taking users through the auth flow and seeing the error message upon redirect to LinkedIn.com. Please confirm. We're looking into this issue as other developers have reported it.
Then only the following comment he has assured that the problem is resolved.
He Said:
Our team has resolved the "We were unable to find the authorization token" issue when redirecting users through the auth flow. Please let me know if any of you continue to experience this issue.
I've been having intermittent problems with NSURLConnection requests timing out in our iPhone app. It seems to be occurring more of late. Once it enters this state, it stays in that state. The only resolution seems to be killing the app and restarting it.
Observations:
The core code that executes the NSURLConnection has not changed (except for some custom user-agent code recently added).
Have yet to find a reproducible case, but timeouts seem to occur after the app has been sitting in the background for a while, particularly if running on 3G (no WiFi).
Apache on server is logging no requests from client while it's experiencing these timeouts.
Some indications that other apps, like Mail and Safari are affected (i.e., experiencing timeouts), although not consistently.
3G coverage is solid where I'm at, not to rule out a transitory issue triggering the problem (assumed not likely).
All requests are going to our own API server, and are restful POST requests.
We use our own NSTimer-based timeout, due to the issues with timeoutInterval and POST requests. I've tried playing around with increasing the timeout value -- problem still occurs.
Other miscellaneous stuff:
App was recently converted to ARC.
Running app under iOS 5.1.1.
App uses latest versions of UrbanAirship, TestFlight and Flurry SDKs.
Also using ARC branch of TouchXML to parse responses.
As you can see below, the code runs on the main thread. I assumed something is blocking on that thread, but the stack traces I see when suspending the app suggest the main thread is fine. I take it that NSURLConnection is using its own thread and that must be blocked.
#define relnil(v) (v = nil)
- (id) initWebRequestController
{
self = [super init];
if (self)
{
//setup a queue to execute all web requests on synchronously
dispatch_queue_t aQueue = dispatch_queue_create("com.myapp.webqueue", NULL);
[self setWebQueue:aQueue];
}
return self;
}
- (void) getStuffFromServer
{
dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(aQueue, ^{
dispatch_sync([self webQueue], ^{
error_block_t errorBlock = ^(MyAppAPIStatusCode code, NSError * error){
dispatch_async(dispatch_get_main_queue(), ^{
[[self delegate] webRequestController:self didEncounterErrorGettingPointsWithCode:code andOptionalError:error];
});
};
parsing_block_t parsingBlock = ^(CXMLDocument * doc, error_block_t errorHandler){
NSError * error = nil;
CXMLNode * node = [doc nodeForXPath:#"apiResult/data/stuff" error:&error];
if (error || !node) {
errorHandler(MyAppAPIStatusCodeFailedToParse, error);
}
else {
stuffString = [node stringValue];
}
if (stuffString) {
dispatch_async(dispatch_get_main_queue(), ^{
[[self delegate] webRequestController:self didFinishGettingStuff:stuffString];
});
}
else {
errorHandler(MyAppAPIStatusCodeFailedToParse, error);
}
};
NSURL * url = [[NSURL alloc] initWithString:[NSString stringWithFormat:MyAppURLFormat_MyAppAPI, #"stuff/getStuff"]];
NSMutableURLRequest * urlRequest = [[NSMutableURLRequest alloc] initWithURL:url];
NSMutableDictionary * postDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[[NSUserDefaults standardUserDefaults] objectForKey:MyAppKey_Token], #"token",
origin, #"from",
destination, #"to",
transitTypeString, #"mode",
time, #"time",
nil];
NSString * postString = [WebRequestController httpBodyFromDictionary:postDictionary];
[urlRequest setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];
[urlRequest setHTTPMethod:#"POST"];
if (urlRequest)
{
[self performAPIRequest:urlRequest withRequestParameters:postDictionary parsing:parsingBlock errorHandling:errorBlock timeout:kTimeout_Standard];
}
else
{
errorBlock(MyAppAPIStatusCodeInvalidRequest, nil);
}
relnil(url);
relnil(urlRequest);
});
});
}
- (void) performAPIRequest: (NSMutableURLRequest *) request
withRequestParameters: (NSMutableDictionary *) requestParameters
parsing: (parsing_block_t) parsingBlock
errorHandling: (error_block_t) errorBlock
timeout: (NSTimeInterval) timeout
{
NSAssert([self apiConnection] == nil, #"Requesting before previous request has completed");
NSString * postString = [WebRequestController httpBodyFromDictionary:requestParameters];
[request setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];
NSString * erVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleShortVersionString"];
NSString * erBuildVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleVersion"];
if ([erBuildVersion isEqualToString:erVersion] || [erBuildVersion isEqualToString:#""]) {
erBuildVersion = #"";
} else {
erBuildVersion = [NSString stringWithFormat:#"(%#)", erBuildVersion];
}
NSString * iosVersion = [[UIDevice currentDevice] systemVersion];
NSString * userAgent = [NSString stringWithFormat:#"MyApp/%#%# iOS/%#", erVersion, erBuildVersion, iosVersion];
[request setValue:userAgent forHTTPHeaderField:#"User-Agent"];
[request setTimeoutInterval:(timeout-3.0f)];
dispatch_sync(dispatch_get_main_queue(), ^{
NSURLConnection * urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
if (urlConnection)
{
[self setApiConnection:urlConnection];
requestParseBlock = [parsingBlock copy];
requestErrorBlock = [errorBlock copy];
NSMutableData * aMutableData = [[NSMutableData alloc] init];
[self setReceivedData:aMutableData];
relnil(aMutableData);
[urlConnection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[urlConnection start];
relnil(urlConnection);
NSTimer * aTimer = [NSTimer scheduledTimerWithTimeInterval:timeout target:self selector:#selector(timeoutTimerFired:) userInfo:nil repeats:NO];
[self setTimeoutTimer:aTimer];
}
else
{
errorBlock(MyAppAPIStatusCodeInvalidRequest, nil);
}
});
//we want the web requests to appear synchronous from outside of this interface
while ([self apiConnection] != nil)
{
[NSThread sleepForTimeInterval:.25];
}
}
- (void) timeoutTimerFired: (NSTimer *) timer
{
[[self apiConnection] cancel];
relnil(apiConnection);
relnil(receivedData);
[self requestErrorBlock](MyAppAPIStatusCodeTimeout, nil);
requestErrorBlock = nil;
requestParseBlock = nil;
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[self requestErrorBlock](MyAppAPIStatusCodeFailedToConnect, error);
relnil(apiConnection);
relnil(receivedData);
[[self timeoutTimer] invalidate];
relnil(timeoutTimer);
requestErrorBlock = nil;
requestParseBlock = nil;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[receivedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
MyAppAPIStatusCode status = MyAppAPIStatusCodeFailedToParse;
CXMLDocument *doc = [[self receivedData] length] ? [[CXMLDocument alloc] initWithData:[self receivedData] options:0 error:nil] : nil;
DLog(#"response:\n%#", doc);
if (doc)
{
NSError * error = nil;
CXMLNode * node = [doc nodeForXPath:#"apiResult/result" error:&error];
if (!error && node)
{
status = [[node stringValue] intValue];
if (status == MyAppAPIStatusCodeOK)
{
[self requestParseBlock](doc, [self requestErrorBlock]);
}
else if (status == MyAppAPIStatusCodeTokenMissingInvalidOrExpired)
{
[Definitions setToken:nil];
[self requestMyAppTokenIfNotPresent];
[Definitions logout];
dispatch_async(dispatch_get_main_queue(), ^{
[[self delegate] webRequestControllerDidRecivedExpiredTokenError:self];
});
}
else
{
[self requestErrorBlock](status, nil);
}
}
else
{
[self requestErrorBlock](status, nil);
}
}
else
{
status = MyAppAPIStatusCodeUnexpectedResponse;
[self requestErrorBlock](status, nil);
}
relnil(doc);
relnil(apiConnection);
relnil(receivedData);
[[self timeoutTimer] invalidate];
relnil(timeoutTimer);
requestErrorBlock = nil;
requestParseBlock = nil;
}
URLs below are some screenshots of the queues/threads when the app was in the problematic state. Note, I believe thread 10 is related to the cancel performed on the previous timeout, although the mutex wait is curious. Also, the bit in thread 22 about Flurry does not consistently appear when experiencing the problem on other occasions.
Stack trace screenshots:
http://img27.imageshack.us/img27/5614/screenshot20120529at236.png
http://img198.imageshack.us/img198/5614/screenshot20120529at236.png
Perhaps I'm overlooking something obviously wrong in those traces, as I'm relatively new to iOS/Apple development.
All of this would be much simpler to solve if I had the source for NSURLConnection and related code, but such as it is, I'm taking stabs in the dark at this point.
Removing the TestFlight 1.0 SDK seemed to fix the problem. TestFlight also confirmed that they're working on a fix. Given that it's been over a month since the bug was originally confirmed by others, I wonder how close we are to getting a fix.
After hours of searching on google I still cant find a single topic about this one:
1. I need to put error handling functions if the user does not have internet connection.
2. the user have internet connection but cant connect to the server .
And where can I put these? I tried one topic but it don't show the alert view message.
Here is my code:
- (IBAction)getDataPressed
{
if([myRequest_ isExecuting])
{
return;
}
if(myRequest_ != nil)
{
[myRequest_ release];
}
myRequest_ = [[ASIHTTPRequest alloc]initWithURL:[NSURL URLWithString:URL_PATH]];
myRequest_.delegate = self;
[myRequest_ startAsynchronous];
}
#pragma ASI Delegate methods
- (void)requestFinished:(ASIHTTPRequest *)request
{
NSLog(#"Request finished successfully");
NSLog(#"%#",[request responseString]);
NSDictionary *responseDictionary = [[request responseString]JSONValue];
NSDictionary *arrayElement = [responseDictionary objectForKey:#"user"];
NSString *ID = [arrayElement valueForKeyPath:#"id"];
NSLog(#"id: %#",ID);
NSString *usr = [arrayElement valueForKeyPath:#"usr"];
NSLog(#"usr: %#",usr);
NSString *gd = [arrayElement valueForKeyPath:#"gd"];
NSLog(#"gd: %#",gd);
NSString *ag = [arrayElement valueForKeyPath:#"ag"];
NSLog(#"ag: %#",ag);
NSString *st = [arrayElement valueForKeyPath:#"st"];
NSLog(#"st: %#",st);
NSString *lf = [arrayElement valueForKeyPath:#"lf"];
NSLog(#"lf: %#",lf);
NSString *da = [arrayElement valueForKeyPath:#"da"];
NSLog(#"da: %d",da);
for(NSString *value in [arrayElement allValues]){
NSLog(#"Found Value %#",value);
label.text = [value stringByAppendingFormat:#",%#,%#,%#,%#,%#,%#,",ID,usr,gd,ag,st,lf];
}
}
- (void)requestFailed:(ASIHTTPRequest *)request
{
NSLog(#"Error %#", [request error]);
if([request error])
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Destination Unreachable" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[alert release];
}
}
-(void) dealloc
{
[super dealloc];
}
Thanks to those who will help ^_^
I think you could try the Reachbility application to help you out checking if you have internet or not. As for the server itself, you can use the NSUrlConnection Delegate methods to check if there was a problem with your request (by seeing the kind of HTTP code that comes)
Im am trying to properly integrate MBProgressHUD in a project while i am downloading and processing some data. Forgive me my ignorance if i am asking stupid things, but i'm a complete noob...
I have a "Data" class that is handling my HTTPRequests, so i thing here is the proper place to put some stuff in:
-(void)resolve
{
// Create the request.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[[NSURL alloc] initWithString:[self url]]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
NSLog(#"Resolving content from: %#",[self url]);
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
receivedData = [[NSMutableData data] retain];
} else {
NSLog(#"Content - resolve: connection failed");
}
// Here is the part that it makes me crazy...
HUD = [[MBProgressHUD showHUDAddedTo:self.view animated:YES] retain];
return;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// This method is called when the server has determined that it
// has enough information to create the NSURLResponse.
// It can be called multiple times, for example in the case of a
// redirect, so each time we reset the data.
// receivedData is an instance variable declared elsewhere.
expectedLength = [response expectedContentLength];
currentLength = 0;
HUD.mode = MBProgressHUDModeDeterminate;
[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
currentLength += [data length];
HUD.progress = currentLength / (float)expectedLength;
// Append the new data to receivedData.
// receivedData is an instance variable declared elsewhere.
[receivedData appendData:data];
return;
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[HUD hide:YES];
// release the connection, and the data object
[connection release];
// receivedData is declared as a method instance elsewhere
[receivedData release];
// inform the user
NSLog(#"Content - Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
return;
}
Now i know that putting the "showHUDaddedTo" in the -(void)resolve is not the right way...
I am calling this data function from a view controller with an IBaction like this:
-(IBAction) btnClicked:(id) sender {
if ([[issue status] intValue] == 1 ) // issue is not downloaded
{
[(Content *)[issue content] resolve];
//HUD = [[MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES] retain];
}
else // issue is downloaded - needs to be archived
{
NSError * error = nil;
[[NSFileManager defaultManager] removeItemAtPath:[(Content *)[issue content] path] error:&error];
if (error) {
// implement error handling
}
else {
Content * c = (Content *)[issue content];
[c setPath:#""];
[issue setStatus:[NSNumber numberWithInt:1]];
[buttonView setTitle:#"Download" forState:UIControlStateNormal];
[gotoIssue setTitle:#"..." forState:UIControlStateNormal];
// notify all interested parties of the archived content
[[NSNotificationCenter defaultCenter] postNotificationName:#"contentArchived" object:self]; // make sure its persisted!
}
}
}
Simply said: i would like to call all the MBProgressHUD stuff from my Conten.m file in the moment i push the download button in my IssueController.m file. I think you see now where my problem is: i am not a coder ;-) Any help is appreciated.
Cheers,
sandor
I would try and use the HUD method showWhileExecuting. Wrap your calls up in a new method and call that to download your information. When your method finishes, so will your HUD.
Also, is there a reason why you are retaining your calls for the HUD? I have not seen that before and I'm not sure if you need to retain those as they are autoreleased.