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.
Related
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 {
...
}
This question already has answers here:
Return value for function inside a block
(3 answers)
Closed 8 years ago.
+ (NSString *) simpleAuth {
[SimpleAuth authorize:#"instagram" completion:^(NSDictionary *responseObject, NSError *error) {
NSLog(#"plump: %#", responseObject);
NSString *accessToken = responseObject[#"credentials"][#"token"];
}];
return accessToken
}
trying to get my instagram accesstoken as a string so I can use it to download data in my swift viewcontroller file. I had to write simple auth in Objective C since it doesn't work for swift atm.
Since the method is run asynchronously, you cant return the access token just like that. I would suggest you to add a completion block in your simpleAuth: method which passses the access token to the callee when it gets the accesstoken.
Something like this would be a better approach,
+ (void)simpleAuth:(void(^)(NSString*))completionHandler
{
[SimpleAuth authorize:#"instagram" completion:^(NSDictionary *responseObject, NSError *error) {
NSString *accessToken = responseObject[#"credentials"][#"token"];
completionHandler(accessToken)
}];
}
That way you would call it like this,
[SomeClass simpleAuth:^(NSString *accessToken){
NSLog(#"Received access token: %#", accessToken);
}];
It's not possible to 'return' an object from a response block. This is because response blocks run asynchronized, and thus your code continues running besides the Auth call.
To solve this you can use a delegate, or use NSNotifications. And example for NSNotifications would be:
In the listening controller add something like:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(authCompleted:)
name:#"NotificationIdentifier"
object:nil];
The listening method:
-(void)authCompleted:(NSNotification *)notification {
NSString *accessToken = [notification object];
//now continue your operations, like loading the profile, etc
}
And in the completion block of the add:
[[NSNotificationCenter defaultCenter] postNotificationName:#"NotificationIdentifier" object: accessToken];
I was wondering if you have seen this or might have some ideas as to why I see the following behavior in my code: I have an NSURLsession with background config. I initiate periodic download task when the program runs in the Foreground, and everything works. WhenI simulate backgroundfetch (in xcode), my task gets a null value (eventhough the request and the session are not null). of course in this case, my session delegate never gets fired to do completionhandler. if I simulate subsequent background fetches, they all work afterward. at this point, if I bring the app to the foreground in the simulator, and I simulate another backgroundfetch, the symptoms star all over. I am using this code in my appdelegate class.
your help is greatly appreciated.
- (NSURLSession *)FlickrSession
{
if(!_FlickrSession)
{
NSLog(#"setting new FlickrSession");
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:FLICKR_SESSION];
configuration.allowsCellularAccess = NO;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_FlickrSession = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
_FlickrSession.sessionDescription = FLICKR_SESSION;
NSLog(#" new self is %#", _FlickrSession);
NSLog(#"queue in session %#", dispatch_get_current_queue());
});
}
return _FlickrSession;
}
-(void) startFlickrFetch
{
// initialize session config and the background session
[self.FlickrSession getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks)
{
if(![downloadTasks count])
{
NSLog(#"new downloadtask session %#", self.FlickrSession.sessionDescription);
NSURLRequest *request = [NSURLRequest requestWithURL:[FlickrFetcher URLforRecentGeoreferencedPhotos]];
// NSURLSessionDownloadTask *task = [self.FlickrSession downloadTaskWithURL:[FlickrFetcher URLforRecentGeoreferencedPhotos]];
NSURLSessionDownloadTask *task = [self.FlickrSession downloadTaskWithRequest:request];
task.taskDescription = FLICKR_DOWNLOAD_TASK;
NSLog(#"new request %#", request);
NSLog(#"new downloadtask %#", task);
[task resume];
//task?[task resume]:[self fireBackgroungFetchCompletionHandler];;
//[self fireBackgroungFetchCompletionHandler];
NSLog(#"queue in task %#", dispatch_get_current_queue());
}
else
{
NSLog(#"resuming old downloadtask %d", [downloadTasks count]);
for(NSURLSessionDownloadTask *task in dataTasks) [task resume];
}
}];
NSLog(#"queue outside the block %#", dispatch_get_current_queue());
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions
{
// Override point for customization after application launch.
// open the file, if there is no managedContext. this is the case wjere the application was launched directly by user
// it did not come from the bckground state;
//need to enable background fetch
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
//[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(documentChangedState) name:UIDocumentStateChangedNotification object: self.document];
NSLog(#"in application didfinishlaunching");
[self openDatabaseFile];
[self startFlickrFetch];
return YES;
}
-(void) application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
self.backgroundFetchCompletionHandler = completionHandler;
if(self.document.documentState == UIDocumentStateNormal)
{
//[self openDatabaseFile];
NSLog(#"in performFetchWithCompletionHandler");
[self startFlickrFetch];
}
}
- (void) application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler
{
self.completionhandler = completionHandler;
NSLog(#"handle event for backgroundURLsession***********");
}
This could have something to do with a strange interaction of the background fetch and a background session, in which case I have no advice.
However, in the background, iOS doesn't know to wait for async calls, e.g. getTasksWithCompletionHandler. You can solve this by wrapping those calls with a UIBackgroundTaskIdentifier†-based [UIApplication] begin/end task (in this case, with the "end (app) task" inside the "get (session) tasks completion handler" block).
But if all you need is a count, here's what I did, which I think is simpler:
Create an ivar:
NSMutableSet *activeTaskIDs;
When you create a task, add it to the set:
[activeTaskIDs addObject:#(task.taskIdentifier)];
When the task completes, remove it.
You can get your count from there, no async.
† Confusingly, a different kind of task. I differentiate with the terms "app tasks" vs. "session tasks".
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.
Using Restkit, I've setup the RKObjectManager in my AppDelegate and everything is working fine. I would like to know if there's some way to setup a default action for specific response codes.
For example, a user uses my iPhone app to login to my api and gets an auth_token back to use. If at any point, for any request, I get back I get a 403 response (like if the auth_token expires) I want to change the RootViewController to my login screen.
What would be the best way to set this up in my app?
In RestKit 0.20 you can register your RKObjectRequestOperation, so you can pass all requests/responses through your own success/failure blocks before anything else.
http://blog.higgsboson.tk/2013/09/03/global-request-management-with-restkit/
#import "RKObjectRequestOperation.h"
#interface CustomRKObjectRequestOperation : RKObjectRequestOperation
#end
#implementation CustomRKObjectRequestOperation
- (void)setCompletionBlockWithSuccess:(void ( ^ ) ( RKObjectRequestOperation *operation , RKMappingResult *mappingResult ))success failure:(void ( ^ ) ( RKObjectRequestOperation *operation , NSError *error ))failure
{
[super setCompletionBlockWithSuccess:^void(RKObjectRequestOperation *operation , RKMappingResult *mappingResult) {
if (success) {
success(operation, mappingResult);
}
}failure:^void(RKObjectRequestOperation *operation , NSError *error) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"connectionFailure" object:operation];
if (failure) {
failure(operation, error);
}
}];
}
#end
Then register your subclass:
[[RKObjectManager sharedManager] registerRequestOperationClass:[CustomRKObjectRequestOperation class]];
And listen for the "connectionFailure" you are sending above:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(connectionFailedWithOperation:) name:#"connectionFailure" object:nil];
In the listener (e.g. your AppDelegate or a login manager):
- (void)connectionFailedWithOperation:(NSNotification *)notification
{
RKObjectRequestOperation *operation = (RKObjectRequestOperation *)notification.object;
if (operation) {
NSInteger statusCode = operation.HTTPRequestOperation.response.statusCode;
switch (statusCode) {
case 0: // No internet connection
{
}
break;
case 401: // not authenticated
{
}
break;
default:
{
}
break;
}
}
}
When using RestKit 0.10 you can use the given delegate method objectLoaderDidLoadUnexpectedResponse.
- (void)objectLoaderDidLoadUnexpectedResponse:(RKObjectLoader *)objectLoader {
if ([[objectLoader response] statusCode] == 403) {
// Your action here
}
}
In RestKit 0.20 you can use a response descriptor for a single code or a set of codes.
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:responseMapping
pathPattern:nil
keyPath:#"yourKeyPath"
statusCodes:[NSIndexSet indexSetWithIndex:403]];
More status code sets in the documentation.
Update
When using your BaseViewController for handling the errors of the request made in one of the other view controllers, you can set up notifications.
BaseViewController
- (void)viewDidLoad
{
// ...
// Set observer for notification e.g. "requestFailedWith403Error"
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handle403Error:) name:#"requestFailedWith403Error" object:self];
}
- (void)handle403Error:(NSNotification)notification
{
// Code for handling the error
}
SubViewController
- (void)loginToServer
{
// ...
// Set authorization header
[[RKObjectManager sharedManager].HTTPClient setAuthorizationHeaderWithUsername:#"username" password:#"password"];
// e.g. POST to server
[[RKObjectManager sharedManager] postObject:yourObject
path:#"path/toserver"
parameters:nil
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
// Handling success
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
// Handling error with notification
[[NSNotificationCenter defaultCenter] postNotificationName:#"requestFailedWith403Error" object:self];
}];
}
Too optimize your central configuration with the handling of errors you can have another look at the example code given in the RestKit Wiki (where the error mapping is added).