I'm trying to test the behavior of my app when connection fails. I am testing on an iPad with wifi turned off. When Restkit attempts a web service call, I get the following error:
CPL[7713:6203] E restkit.network:RKRequest.m:545 Failed to send request to https://xxxxxxxx/APNS_WebService/rest/operations/initializeDevice?deviceID=c4a17f855d3cc824b174b71908480d4e505ebfb221cb4643da9270a07344c367 due to unreachable network.
The problem is that I would like to handle this situation in a delegate callback method, but none of the delegate methods are being called. I have set the delegate on the request, and have requestDidFailLoadWithError, requestDidCancelLoad, requestDidTimeout, and objectLoaderDidFailWithError implemented. None of these are called.
Why aren't my delegates being called?
EDIT: After setting a breakpoint inside RKRequest.m, I see that the following line is in fact being executed:
[self performSelector:#selector(didFailLoadWithError:) withObject:error afterDelay:0];
However, my delegate methods are not getting called.
Here's where I set the delegate:
request = [client requestWithResourcePath:[NSString stringWithFormat:#"/initializeDevice?deviceID=%#",deviceID]];
request.delegate=self;
[request sendAsynchronously];
EDIT 2: Actually, the line in RKRequest.m that I posted above is just calling another method in RKRequest, except that it's not. Putting a breakpoint in didFailLoadWithError shows that this code is never reached. I don't get why that's not working.
Changing the performSelector to a regular method call appears on the surface to give me the behavior I'm looking for. Is this going to break anything? I guess I'm not sure why performSelector is being used to call a method in the same class.
EDIT 3: As requested, here's my delegate method:
-(void)request:(RKRequest *)request didFailLoadWithError:(NSError *)error{
NSLog(error.domain);
NSLog([NSString stringWithFormat:#"%d",error.code]);
NSLog(error.localizedDescription);
NSLog(error.localizedFailureReason);
[request reset];
[request send];
}
EDIT:
Actually, the line in RKRequest.m that I posted above is just calling another method in RKRequest, except that it's not. Putting a breakpoint in didFailLoadWithError shows that this code is never reached. I don't get why that's not working.
This is really strange. I would try doing a full clean of the project and rebuild.
As to what entails a direct call instead of using performSelector, you can see that afterDelay:
[self performSelector:#selector(didFailLoadWithError:) withObject:error afterDelay:0];
this will make the didFailLoadWithError: method be called at the next iteration of the run loop. I would keep this way of calling it.
You could try, though, with this alternative:
dispatch_async(dispatch_get_current_queue(), ^() {
[self didFailLoadWithError:error]; } );
I would suggest setting a breakpoint inside of the RestKit method you are using (I suppose sendAsynchronously) and check what happens. If you look into the method definition, the call to the delegate is effectively there:
} else {
self.loading = YES;
RKLogError(#"Failed to send request to %# due to unreachable network. Reachability observer = %#", [[self URL] absoluteString], self.reachabilityObserver);
NSString* errorMessage = [NSString stringWithFormat:#"The client is unable to contact the resource at %#", [[self URL] absoluteString]];
NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
errorMessage, NSLocalizedDescriptionKey,
nil];
NSError* error = [NSError errorWithDomain:RKErrorDomain code:RKRequestBaseURLOfflineError userInfo:userInfo];
[self performSelector:#selector(didFailLoadWithError:) withObject:error afterDelay:0];
}
Related
I am sure this has been asked before, but I don't know the keywords to search for it.
Suppose that I have a function validateConnection that returns a bool. This function makes a call to the server (using anetworking) and returns true if the request was successful (this is just a example).
I want my method to wait until the response is back in order to return the bool: how to achieve that?
Any help would be much appreciated!
Its Called Block you can learn more about block by Introduction to Objective-C Blocks
Following is the sample method to create block and its call back:
-(void)callmethodwith:(NSString*)string withCompletion:(void(^)(BOOL success, NSError* error, id responce))completion
{
NSString *str =[NSString stringWithFormat:#"MY FUNTn CALLBACK %#",string];
if (completion){
dispatch_async(dispatch_get_main_queue(), ^{
completion(YES,nil,str); // here that call when method complete
});
}
}
And you can call it like following:
[self callmethodwith:#"My new" withCompletion:^(BOOL success, NSError *error, id responce) {
if(success)
{
NSLog(#"==%#",responce); // here you get response once method camplet
}
}];
I think, once the method is executing then there is no way of stopping it.
But you can cancel if it is not fired.
Like following way;
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(yourMethod) object:nil];
[self performSelector:#selector(yourMethod) withObject:nil afterDelay:5.0];
You can use AFNetworking success and failure methods to wait for response. Alternatively, you can use performSelectorOnMainThread method
[yourViewController performSelectorOnMainThread:#selector(yourFunction:) withObject:nil
waitUntilDone:YES];
While working with blocks or Completion Handler, make sure you are not using a strong reference of your class or any view, as it affects retain count.
While working with blocks or completion handler use weak reference inside block, something like this,
__weak Class *weakSelf = self;
[manager communicateUsingGETMethod:#"www.google.com" parameterDictionary:#{} success:^(id successDicitoanary) {
// Call using weak reference.
[weakSelf handleSuccess];
} failure:^(NSError *error) {
[weakSelf handleFailure];
}];
and write whole code separately.This will make code more readable and you will get rid of retain cycle too as you are calling your completionCode with weak reference,
For your reference, read apple doc for Avoid Strong Reference Cycles when Capturing self
I have a problem loading a large web page in a UIWebView where the connection drops for some reason trying to load a java script file. The load stalls at the point with neither didFailLoadWithError or webViewDidFinishLoad ever being invoked. The delegate is setup correct as when there is no networking error the page loads fine and the webViewDidFinishLoad method is invoked.
While trying to figure out why the page load stalls I implemented and registered a custom NSURLProtocol where I found out that connection delegate didFailWithError would be invoked most of the time, but the web view delegate would never be invoked after that.
I changed the custom protocol to run the connection in the main thread, which caused the didFailWithError method to be invoked every time the network connection dropped
- (void)startLoading {
NSMutableURLRequest *newRequest = [self.request mutableCopy];
[NSURLProtocol setProperty:#YES forKey:ProtocolKey inRequest:newRequest];
self.connection = [[NSURLConnection alloc]
initWithRequest:newRequest
delegate:self startImmediately:NO];
[self.connection scheduleInRunLoop:[NSRunLoop mainRunLoop]
forMode:NSDefaultRunLoopMode];
[self.connection start];
}
The web view delegate webViewDidFinishLoad method was still not being called so I added a notification to the didFailWithError method, which allows me to handle the error in my web view controller
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSDictionary *dict = #{ #"errorDescription":error.localizedDescription };
[[NSNotificationCenter defaultCenter] postNotificationName:ErrorKey object:nil ];
[self.client URLProtocol:self didFailWithError:error];
}
I can't figure out why it seems that the request being run by the web view on the background thread seem to get destroyed before the delegate methods can be invoked and the errors handled?
I was having similar issues with WKWebView, I assume with the same resource javascript, but I can not trap the errors in the custom protocol handler so I don't know what is going on there.
Has any one run into this before? Or have a better way of resolving this problem?
I have a remote service that i call and it processes the request asynchronously. When the data is returned, i'll refresh my local UI.
But sometimes when the View disappears and if asynchronous call is still in the queue then the app crashes with error EXEC BAD ACCESS (i.e. the object is already released) i.e.
My app crashes when the service returns but the ViewController is disposed.
Mainly i am getting error when calling [delegate respondsToSelector:#selector (methodName:)], after the the view controller is no longer exist.
May be i need to cancel all my asynchronous calls (running or waiting in queue) in viewWillDisappear. But i am not able to cancel the running calls.
I have already tried this but in viewWillDisappear my self.navigationController.delegate is already nil.
Edit:
Method to call service:
{
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:#“%#method_name”,Base_URL]]];
[request setRequestMethod:#"POST"];
[request setTimeOutSeconds:600];
[request setPostValue:userID forKey:#“id”];
[request setDelegate:self];
[request setDidFinishSelector:#selector(requestFinished:)];
[request setDidFailSelector:#selector(requestFailed:)];
[request startAsynchronous];
}];
[operationQueue addOperation:operation];
}
And my requestFinished method (where my app crashes)
-(void)requestFinished:(ASIHTTPRequest *)request
{
// some stuff
// It's working fine when I normally run my app but fails when I rapidly changes the View Controller.
if ([delegate respondsToSelector:#selector(gotResponseData:)]) // Here my app crashes
{
[delegate gotResponseData:responseDict];
}
}
Delegate property in .h file:
#property (nonatomic,assign)id <protocolName>delegate;
Mainly this app crashes when I quickly switches between View Controller.
I'll edit my question if needed.
Kindly provide me some guidance.
In your block operation you should use a weak reference to self so that if/when self is released the operation doesn't call an invalid reference.
Indeed, you shouldn't actually need the operation as you are starting an asynchronous process inside the operation anyway.
Your delegate property in self should also be weak as your code indicates the problem is that the delegate is using an unsafe unretained approach (because of where you say the crash occurs).
Generally you would only have one request running from self so you should maintain a reference to the request and cancel it if self is destroyed.
My application implementing in-app purchase app crashes when connected to EDGE network, 100% loss,very poor network .There is no crash log. But it says "EXC BAD ACCESS code=1 address=0xc " on following line
_completionHandler(YES, skProducts);
Code for the method
#pragma mark - SKProductsRequestDelegate
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
sharedManager=[Mymanager sharedManager];
_productsRequest = nil;
sharedManager.bookidList=[[NSMutableArray alloc]init];
sharedManager.sharedProductPrice=[[NSMutableArray alloc]init];
NSArray * skProducts = response.products;
NSLog(#"sk product %#",skProducts);
// NSMutableArray *a=[[NSMutableArray alloc]init];
for (SKProduct * skProduct in skProducts) {
[sharedManager.sharedProductPrice addObject:skProduct.price];
[sharedManager.bookidList addObject:skProduct.productIdentifier];
}
[self updatePlist];
_completionHandler(YES, skProducts); //EXC BAD ACCESS CODE =1 ADDRESS=0XC
_completionHandler = nil;
}
I am following the in-app purchase tutorial on Ray Wenderlich's site (for iOS6.0). Minimum target of my application is iOS5. Any pointers how to fix this crash?
EDIT
New changes ,i made the NSArray to strong ARC property adn it still crashing check below images
tutorial
method to retrieve the product information from iTunes Connect:
- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler {
// 1
_completionHandler = [completionHandler copy];
// 2
_productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers];
_productsRequest.delegate = self;
[_productsRequest start];
}
This first squirrels a copy of the completion handler block inside the instance variable so it can notify the caller when the product request asynchronously completes.
It then creates a new instance of SKProductsRequest, which is the Apple-written class that contains the code to pull the info from iTunes Connect. It’s very easy to use – you just give it a delegate (that conforms to the SKProductsRequestDelegate protocol) and then call start to get things running.
We set the IAPHelper class itself as the delegate, which means that it will receive a callback when the products list completes (productsRequest:didReceiveResponse) or fails (request:didFailWithErorr).
Speaking of delegate callbacks, add those next! Add the following code before the #end:
It's quite likely that _completionHandler is nil when you are calling it in the method above. Attempting to execute a block variable when the variable is nil does give a bad access error.
You can confirm this by wrapping the execution of the completion handler block in an if statement and only executing it if the variable isn't nil:
if(_completionHandler)
{
_completionHandler(YES, skProducts);
}
Fixing the problem is another matter, you would need to trace the calls and see when the completion block is either not passed or removed.
I am using GCDAsyncSocket (CocoaAsyncSocket) for the socket communication in my app. Due to the asynchronous nature of GCDAsyncSocket, my network request (submitMessage below) is decoupled from the callback block that runs when data is received (socket:didReadData).
- (void)submitMessage:(NSDictionary *)messageObject onCompletion:(completionBlock)block {
...
[_socket writeData:requestData withTimeout:self.timeout tag:0];
[_socket readDataToLength:4 withTimeout:self.timeout tag:TAG_HEADER];
}
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
...
NSDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
if (self.completionBlock != nil)
self.completionBlock(responseObject);
}
}
This approach works fine for one-off exchanges. But there are some cases when I need to post a request, then using the received data, post another request. I can't get this to work properly. Basically, I need something like this:
[self submitMessage:request1 onCompletion:^(NSDictionary *response1) {
(...callback 1...)
}];
[self submitMessage:request2 onCompletion:^(NSDictionary *response2) {
(...callback 2...)
}];
}];
or
[self submitMessage:request1 onCompletion:^(NSDictionary *response1) {
(...callback 1...)
}];
[self submitMessage:request2 onCompletion:^(NSDictionary *response2) {
(...callback 2...)
}];
where the order is strictly request1 - callback1 - request2 - callback2.
So the question is, how can I block the second request to run after the callback of the first request? Would GCD (dispatch_sync?) be the way to go?
Edit
I ended up using a solution similar to what #tigloo suggested (hence accepting his answer), but using NSCondition instead of GCD (if anyone's interested in details, I followed this great discussion). I am already running multiple threads (UI in main, high-level socket comms in another thread, and the socket operations in a third thread). Setting a class property and using NSCondition to lock the GCDAsyncSocket delegate until the response arrive seems the cleanest approach.
I think you were almost there. What about
[self submitMessage:request1 onCompletion:^(NSDictionary *response1) {
// here, do something with response1 and create request2...
// then you can make request2 directly at the end of the callback:
[self submitMessage:request2 onCompletion:^(NSDictionary *response2) {
// here, do something with response2...
}];
}];
No need for the GCD directives, no need to block execution (which is a bad practice anyway). Does this solve your problem?
The easiest approach is to append your requests to a serial dispatch queue and then wait for them to be completed by using dispatch_sync(). A discussion on StackOverflow can be found here.
The actual way of implementing it is up to your preferences. A possible idea is the following:
Create a new class "SyncRequest"
This class ideally has a private property of type bool "requestFinished", initialized to NO in the class' init method
In a method such as "sendSyncRequest" you call submitMessage:completionBlock:
The completion block will set the "requestFinished" property to YES
The last line in "sendSyncRequest" will be dispatch_sync(syncRequestQueue, ^(void){while(!requestFinished);});
This way you can construct multiple instances of SyncRequest, each handling a synchronized request. Rough sketch implementation:
#interface SyncRequest
#property bool requestFinished;
#end
#implementation SyncRequest
dispatch_queue_t syncRequestQueue;
-(id)init
{
self = [super init];
if ( !self )
return nil;
self.requestFinished = NO;
syncRequestQueue = dispatch_queue_create("com.yourid.syncrequest", DISPATCH_QUEUE_SERIAL);
return self;
}
-(void) sendSyncRequest:(NSDictionary*)messageObject
{
// submit message here and set requestFinished = YES in completion block
// wait for completion here
dispatch_sync(syncRequestQueue, ^(void){while(!self.requestFinished);});
}
#end
NOTE: I wrote the code without having the compiler at hand, you may have to create an indirect reference to "self" in the dispatch_sync call in order to avoid a cyclic reference.