NSNotification used between two controllers is crashing - ios

i am using following code
First.m:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(registerTok) name:#"registerTok" object:nil];
[Second serviceCall:[NSString stringWithFormat:#"%#%#",BASEURL, USER_LOGIN] withParameter:parameters ofType:USER_SIGNIN];
Second.m:
+(void)serviceCall:(NSString*)url withParameter:(NSDictionary*)parameter ofType:(int)type{
AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:[NSURL URLWithString:url]];
--------------line1------------------
[manager.requestSerializer setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
manager.requestSerializer = [AFHTTPRequestSerializer serializer];
[manager POST:url parameters:parameter success:^(AFHTTPRequestOperation *operation, id responseObject) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"registerToken" object:nil userInfo:nil];---breaking101
}
The notification code above is breaking. If I write
[[NSNotificationCenter defaultCenter] postNotificationName:#"registerToken" object:nil userInfo:nil]; at line1, it is working. I think it is related to some object issue. Please help. I have never used notification center.

There are a few things wrong here:
The notification that you are sending ("registerToken") is not the one you are listening ("registerTok").
The selector that you define should take one parameter - (NSNotification *), as mentioned in the apple doc here
The success block of the POST:parameters:success: method of the AFHTTPRequestOperationManager is executed on arbitrary thread. You might want to specify the thread/queue on which you want to execute the method on when the notification is fired. When you post the notification on line 1 it works because that notification is executed on the current thread.
P.S. If you still have problems, add complete logs of the errors that you are getting, then one can better answer your question.

You post the notification with name : #"registerToken" and you observing for #"registerTok" notification. These two names must be the same
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(registerTok:) name:#"registerToken" object:nil];
Also add the registerTok method
-(void)registerTok:(NSNotification*)notification {
...
}

i think you are not define the selector method registerTok thats way it crashed.
-(void)registerTok:(NSNotification*)notification {
...
}

Related

How to stop call which is sent in operation queue in Objective-C

I am working on iOS App, and I am using AFNetworking for interacting with server API.
My issue is I want to send call and don't want to restrict user until response get from server, so issue is crash. When user move back to that particular screen lets say I have listing screen where I am getting data which is taking 6-7 seconds and meanwhile user move back to previous screen and when data come from API and call back that delete to listing screen but user move backed to that screen then App crashes
Here below is code for fetching data call.
+ (void) getRequestForDocumentListing:(NSDictionary *)headerParams urlQuery: (NSString*)action parameters:(NSDictionary*)params
onComplete:(void (^)(id json, id code))successBlock
onError:(void (^)(id error, id code))errorBlock
{
NSString *authorizationValue = [self setAuthorizationValue:action];
NSString *selectedLanguage = [ApplicationBaseViewController getDataFromDefaults:#"GLOBALLOCALE"];
NSString *language = selectedLanguage;
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
//set headers values
[manager.requestSerializer setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[manager.requestSerializer setValue:language forHTTPHeaderField:#"Accept-Language"];
[manager.requestSerializer setValue:authorizationValue forHTTPHeaderField:#"authorization"];
[manager.requestSerializer setValue:#"x-folder" forHTTPHeaderField:#"inbox"];
[manager GET:action parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"document listing success");
NSInteger statusCode = [operation.response statusCode];
NSNumber *statusObject = [NSNumber numberWithInteger:statusCode];
successBlock(responseObject, statusObject);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSInteger statusCode = [operation.response statusCode];
NSNumber *statusObject = [NSNumber numberWithInteger:statusCode];
id responseObject = operation.responseData;
id json = nil;
id errorMessage = nil;
if (responseObject) {
json = [NSJSONSerialization JSONObjectWithData:responseObject options:kNilOptions error:&error];
errorMessage = [(NSDictionary*)json objectForKey:#"Message"];
}else{
json = [error.userInfo objectForKey:NSLocalizedDescriptionKey];
errorMessage = json;
}
errorBlock(errorMessage, statusObject);
}];
}
What I need is to stop call in ViewdidDisappear View delegate
- (AFHTTPRequestOperation *)GET:(NSString *)URLString
parameters:(id)parameters
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:#"GET" URLString:URLString parameters:parameters success:success failure:failure];
[self.operationQueue addOperation:operation];
return operation;
}
How to solve this particular issue?
I got your point, I think the problem is not about the AFNetWorking or download, it is about how you organize your view controllers.
In short, you need to make sure the synchronization of the data and view.
What cause your crash is when users do some operation(eg. delete, move...), the data is not the same with what view shows.
Let's play back an example:
An array with 12 objects and show it with a table view.
User call a web request to change the array. As we know, it needs time.
User leave and come back again. In this view, table view shows with the old array.
At this point, web request comes back. The array is modified to 10 object.But at this time, the call back dose not cause the table view to load the new data.
When user do some operation, just like delete the 11st object in the table view. Actually, there is no 11st object in array.
So crash comes.
How to deal with it is to keep the synchronization of the data and view.
First get a reference to the Operation object by
AFHTTPRequestOperation *operation = [manager GET:action parameters:nil success:^...blah blah blah...];
Then you can set the completion block to nil when you move away from this screen.
[operation setCompletionBlock:nil];
Please note that even though you move away from the screen, the request may actually execute successfully. However, your app will not crash now.
Thanks RuchiraRandana and childrenOurFuture for your answer, I got help from your answers and finally I come to solution where I am not going to cancel operation and set nil delegate, because my others operation are also in working which is trigger on other screen.
I create a just BOOL and set YES default value in singleton class and also set to no in - (void)dealloc on that particular class and in API class where I am triggering that delegate I added that check.
if ([SHAppSingleton sharedInstance].isDocListControllerPop == YES) {
[delegate documentListResponse:documentList andStatusCode:code];
}
I know this might not be perfect solution but this resolved my issue.
Thanks

AFNetworking 2.0 and Unit Test

I am trying to unit test a class that uses AFNEtworking in XCode 5 using XCTest. The issue I am having is that the completion blocks for my AFHTTPRequestOperation are never being executed. I assume this is some disconnect between XCode running the unit test and AFNetworking's dispatch queue. The following test case passes but the NSLog statements in the completion blocks are never reached (no log output and no breakpoints set on these statements are caught). The same code works outside of a unit test. Does anyone know how to work around this issue? I am using Nocilla to mock the actual requests, the result is the same using a real server retuning valid responses?
Edited to make test fail and log vars set in block
- (void)setUp
{
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
[[LSNocilla sharedInstance] start];
stubRequest(#"POST", #"http://www.example.com/module/api/ping").
andReturn(200).
withHeaders(#{#"Content-Type": #"application/json"}).
withBody(#"{\"success\":true}");
stubRequest(#"GET", #"http://www.example.com/module/api/ping?testkey=testval").
andReturn(200).
withHeaders(#{#"Content-Type": #"application/json"}).
withBody(#"{\"success\":true}");
}
- (void)tearDown
{
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
[[LSNocilla sharedInstance] stop];
[[LSNocilla sharedInstance] clearStubs];
}
- (void)testSanity
{
AFSecurityPolicy *policy = [[AFSecurityPolicy alloc] init];
//[policy setAllowInvalidCertificates:YES];
AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:[NSURL URLWithString:#"http://www.example.com/module/api/ping"]];
//manager.operationQueue = [NSOperationQueue mainQueue];
[manager setSecurityPolicy:policy];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
__block id resObj = nil;
__block id resError = nil;
AFHTTPRequestOperation *req = [manager POST:#"http://www.example.com/module/api/ping"
parameters:[NSDictionary dictionaryWithObject:#"testval" forKey:#"testkey"]
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Response: %#", responseObject);
resObj = responseObject;
return;
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
resError = error;
return;
}];
[req waitUntilFinished];
NSLog(#"req.status: %d", req.response.statusCode);
NSLog(#"req.responseObj: %#", req.responseObject);
XCTAssertTrue(req.isFinished);
NSLog(#"resObj: %#", resObj);
NSLog(#"resError: %#", resError);
XCTAssertEqual([[req.responseObject objectForKey:#"success"] boolValue], YES);
XCTAssertEqual([[resObj objectForKey:#"success"] boolValue], YES);
}
Console Output
Test Case '-[AppSupportTests testSanity]' started.
2014-04-29 16:45:07.424 xctest[72183:303] req.status: 200
2014-04-29 16:45:07.424 xctest[72183:303] req.responseObj: {
success = 1;
}
2014-04-29 16:45:07.424 xctest[72183:303] resObj: (null)
2014-04-29 16:45:07.425 xctest[72183:303] resError: (null)
/Users/jlujan/Code/AppSupport/AppSupportTests/AppSupportTests.m:114: error: -[AppSupportTests testSanity] : (([[resObj objectForKey:#"success"] boolValue]) equal to (__objc_yes)) failed: ("NO") is not equal to ("YES")
Test Case '-[AppSupportTests testSanity]' failed (0.003 seconds).
As per the discussion in comments we found that waitUntilFinished is once the background operation is complete, and it does not wait till after the completion blocks have been called.
There's a much better framework for asynchronous testing - Expecta.
Then instead of calling:
XCTAssertTrue(req.isFinished);
XCTAssertEqual([[resObj objectForKey:#"success"] boolValue], YES);
You can do:
expect(req.isFinished).will.beTruthy();
expect([[resObj objectForKey:#"success"] boolValue]).will.beTruthy();
There are lots of other matchers, just make sure you set the timeout with +[Expecta setAsynchronousTestTimeout:] in your +setUp method.

IOS blocks AFNetworking

I am using the AFNetworking to connect to API in my code.
Now I have general things on my view controllers like switches and picker views which will determine what is sent to the API.
So I will need to call the AFNetworking block like this :
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:returncompletedURL];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
operation.responseSerializer.acceptableContentTypes = [NSSet setWithObject:#"text/html"];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
and at the end I use [operation start] to run the block, quite easy!
but I need this in some sort of a method/function so that I can call it and pass arguments over to it as and when a switch is changed or a label is changed. Instead of writing the same block over and over again i want it as a function.
I would use [operation start] in the function to check if a switch is on/off but it does not see it within the function.
If i wrap a method around the AFNetworking block would that bad and I cannot use the RETURN statement within a block.
Generally advice is needed if i have switches and labels and if the user changes any of this then the block needs to be called straight the way to amend the API call.
thanks
I could do something like this, create a method that can return for you value:
Method that you could insert your request.
-(void)callRequestWithParameter:(id)parameter returnState:(void(^)(id response))response{
// here make request
// use parameter to send what you want on request
if (requestSucess){
response(data for return our status);
}
};
//Method for handle request and answer
-(void)requestData:(id)dataParameter {
[ClassName callRequestWithParameter:#(2) returnState: (id response)^{
if(response){
// call some update method like
[self updateItensWithData:data];
}
}
};`

AFHTTPRequestOperation intercept start of the operation

There is setCompletionBlock in AFHTTPRequestOperation.
Is there a way to intercept when AFHTTPRequestOperation starts as well?
Yes or no, depending on your meaning of "intercept"…
Since you control when it starts (either by adding it to an NSOperationQueue, or by calling [operation start]), no interface is provided for conditionally starting.
If you just want to be notified when it starts, you can register for the AFNetworkingOperationDidStartNotification, which is broadcast when a notification starts.
You can implement it like this:
// In some method…
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(HTTPOperationDidStart:) name:AFNetworkingOperationDidStartNotification object:nil];
- (void)HTTPOperationDidStart:(NSNotification *)notification {
AFHTTPRequestOperation *operation = (AFHTTPRequestOperation *)[notification object];
if (![operation isKindOfClass:[AFHTTPRequestOperation class]]) {
return;
}
NSLog(#"%# '%#': %#", [operation.request HTTPMethod], [[operation.request URL] absoluteString], [operation.request allHTTPHeaderFields]);
break;
}
This sample code is a slightly modified excerpt from AFHTTPRequestOperationLogger, which logs AFNetworking information to your console.

MBProgressHud, AFnetworking and refreshing data

I'm using AFnetworking to make a call to a server. While downloading I am using MBProgressHUD to show that data is being downloaded. So far everything works great. My issue is when I press the home button and then relaunch the app. I would like for the page to automatically refresh itself and for MBProgressHUD to display to the user that something is being downloaded. That I cannot do. I can download the data, but I cannot get the HUD part to work.
First, in the viewDidLoad Method I add this:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationDidBecomeActiveNotificationAction)
name:UIApplicationDidBecomeActiveNotification
object:nil];
Now in the method applicationDidBecomeActiveNotificationAction, I call [self downloadWebsites].
In the method downloadWebsites is where the bulk of the work is done: Here it is:
//show the hud
MBProgressHUD* progressHUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
progressHUD.labelText = #"Loading";
progressHUD.mode = MBProgressHUDAnimationFade;
[self.list_websites getPath:[NSString stringWithFormat:#"%#?%#", #"websites", self.auth_header] parameters: nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
//download data in the success block
//refresh the ui
[self.tableView reloadData];
[progressHud self.view animated:YES];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//failure block. log the error
NSLog([error description]);
}];
Why doesn't this work? I can get the data. But I can't get the progressHud to display. How do I fix this?
The Notification Runloop and the HTTP request runloop maybe not the same. So method that show a progress HUD maybe not called.

Resources