AFNetworking: Handle error globally and repeat request
In the AFHTTPClient's init method register for the AFNetworkingOperationDidFinishNotification
which will be posted after a request finishes.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(HTTPOperationDidFinish:) name:AFNetworkingOperationDidFinishNotification object:nil];
In the notification handler check the status code and copy
the AFHTTPRequestOperation
or create a new one.
- (void)HTTPOperationDidFinish:(NSNotification *)notification {
AFHTTPRequestOperation *operation = (AFHTTPRequestOperation *)[notification object];
if (![operation isKindOfClass:[AFHTTPRequestOperation class]]) {
return;
}
if ([operation.response statusCode] == 401) {
// enqueue a new request operation here
}
}
EDIT:
In general you should not need to do that and just handle the authentication with this AFNetworking method:
- (void)setAuthenticationChallengeBlock:(void (^)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge))block;
Alamofire: Handle error globally
At the moment you will have to handle that logic in your own custom response
implementation. We (the Alamofire TC) are currently working on ways that we can make this process easier, but it's very difficult to get right without complicating the rest of the APIs. We're still a ways off yet.
With that said, I built an OAuth 2.0 system that handles this process in a different non-open-source library. It is possible, it's just difficult to do. You will need to hook into the response
closure for all requests that could 401. See my answer here for a full breakdown of how to do this.
Hopefully that helps shed some light. Cheers /p>
Alamofire : How to handle errors globally
Handling refresh for 401 responses in an oauth flow is quite complicated given the parallel nature of NSURLSessions. I have spent quite some time building an internal solution that has worked extremely well for us. The following is a very high level extraction of the general idea of how it was implemented.
import Foundation
import Alamofire
public class AuthorizationManager: Manager {
public typealias NetworkSuccessHandler = (AnyObject?) -> Void
public typealias NetworkFailureHandler = (NSHTTPURLResponse?, AnyObject?, NSError) -> Void
private typealias CachedTask = (NSHTTPURLResponse?, AnyObject?, NSError?) -> Void
private var cachedTasks = Array<CachedTask>()
private var isRefreshing = false
public func startRequest(
method method: Alamofire.Method,
URLString: URLStringConvertible,
parameters: [String: AnyObject]?,
encoding: ParameterEncoding,
success: NetworkSuccessHandler?,
failure: NetworkFailureHandler?) -> Request?
{
let cachedTask: CachedTask = { [weak self] URLResponse, data, error in
guard let strongSelf = self else { return }
if let error = error {
failure?(URLResponse, data, error)
} else {
strongSelf.startRequest(
method: method,
URLString: URLString,
parameters: parameters,
encoding: encoding,
success: success,
failure: failure
)
}
}
if self.isRefreshing {
self.cachedTasks.append(cachedTask)
return nil
}
// Append your auth tokens here to your parameters
let request = self.request(method, URLString, parameters: parameters, encoding: encoding)
request.response { [weak self] request, response, data, error in
guard let strongSelf = self else { return }
if let response = response where response.statusCode == 401 {
strongSelf.cachedTasks.append(cachedTask)
strongSelf.refreshTokens()
return
}
if let error = error {
failure?(response, data, error)
} else {
success?(data)
}
}
return request
}
func refreshTokens() {
self.isRefreshing = true
// Make the refresh call and run the following in the success closure to restart the cached tasks
let cachedTaskCopy = self.cachedTasks
self.cachedTasks.removeAll()
cachedTaskCopy.map { $0(nil, nil, nil) }
self.isRefreshing = false
}
}
The most important thing here to remember is that you don't want to run a refresh call for every 401 that comes back. A large number of requests can be racing at the same time. Therefore, you want to act on the first 401, and queue all the additional requests until the 401 has succeeded. The solution I outlined above does exactly that. Any data task that is started through the startRequest
method will automatically get refreshed if it hits a 401.
Some other important things to note here that are not accounted for in this very simplified example are:
- Thread-safety
- Guaranteed success or failure closure calls
- Storing and fetching the oauth tokens
- Parsing the response
- Casting the parsed response to the appropriate type (generics)
Hopefully this helps shed some light.
Update
We have now released Alamofire 4.0 which adds the RequestAdapter
and RequestRetrier
protocols allowing you to easily build your own authentication system regardless of the authorization implementation details! For more information, please refer to our README which has a complete example of how you could implement on OAuth2 system into your app.
Full Disclosure: The example in the README is only meant to be used as an example. Please please please do NOT just go and copy-paste the code into a production application.
Detect Time Out Error With AFNetworking
Since AFNetworking is build on top on NSULConnection/NSULRSession you can just NSURLErrorTimedOut
to check if the error is a timeout error:
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (error.code == NSURLErrorTimedOut) {
//time out error here
}
}];
You where checking the HTTP status code, but since the connection timed out there is not statuscode.
AFNetworking - no response on all but the final request in a for loop
Your code looks fine, try using this code for downloading images, i'm able to use in loops successfully. Thats what i can help you for now.
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *requestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest];
requestOperation.responseSerializer = [AFImageResponseSerializer serializer];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
[imageView setImage: responseObject];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"Image error: %@", error);
}];
[requestOperation start];
How to intercept the response of AFNetworking request in Objective-C?
Take a look at the below. In the failure block, retry/resend your query X number of times. Be sure to add logic to end retries at some point, so you don't end up with an infinite loop.
AFHTTPRequestOperationManager *operationManager = [AFHTTPRequestOperationManager manager];
[operationManager POST:url
parameters:object
success:^(AFHTTPRequestOperation *operation, id responseObject) {
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (operation.response.statusCode == 403) {
// retry
}
}
];
This answer may also be helpful.
AFNetworking checking the network available
You can check the reachability by this:
AFNetworkReachabilityManager *reachability = [AFNetworkReachabilityManager sharedManager];
[reachability setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusReachableViaWWAN:
NSLog(@"WWN");
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
NSLog(@"WiFi");
break;
case AFNetworkReachabilityStatusUnknown:
NSLog(@"Unknown");
break;
case AFNetworkReachabilityStatusNotReachable:
NSLog(@"Not Reachable");
break;
default:
break;
}
}];
or you can use:
+(BOOL)IsInternet
{
Reachability *networkReachability = [Reachability reachabilityForInternetConnection];
NetworkStatus networkStatus = [networkReachability currentReachabilityStatus];
if (networkStatus == NotReachable)
{
return NO;
}
else
{
return YES;
}
}
Related Topics
Sending Data from One Viewcontroller to Another
Shake Animation for Uitextfield/Uiview in Swift
Icon Already Includes Gloss Effects
Disable the Uitableview Highlighting But Allow the Selection of Individual Cells
iOS Present Modal View Controller on Startup Without Flash
How to Adjust the Height of a Textview to His Content in Swift
Sectionindextitles for a Uicollectionview
How to Only Show Bottom Border of Uitextfield in Swift
iOS Bar Item Image Displaying Wrong Color
Why Are My Variables Empty When I Cast Them in My iOS Application
Cabasicanimation Does Not Animate Correctly When I Update Model Layer
Uitableview Disable Swipe to Delete, But Still Have Delete in Edit Mode
To Convert Image to Cartoon in iOS
How to Add Headerview in Uicollectionview Like Uitableview's Tableheaderview