Search code examples
iosobjective-cafnetworkinginstapaper

AFNetworking spits out an error, but how can I display it?


I have an app where the user can authenticate with Instapaper. They need an Instapaper subscription to be able to do this, however, so if they try to log in with an account that isn't subscribed to Instapaper, I want to display an error to them.

But when they try to log in, AFNetworking sees it as successful, then displays this error to the console:

Error: Error Domain=AFNetworkingErrorDomain Code=-1011 "Expected status code in (200-299), got 400" UserInfo=0x8374840 {NSLocalizedRecoverySuggestion=[{"error_code": 1041, "message": "Subscription account required", "type": "error"}], AFNetworkingOperationFailingURLRequestErrorKey=https://www.instapaper.com/api/1/bookmarks/list>, NSErrorFailingURLKey=https://www.instapaper.com/api/1/bookmarks/list, NSLocalizedDescription=Expected status code in (200-299), got 400, AFNetworkingOperationFailingURLResponseErrorKey=}

All I'm using is AFXAuthClient which is a modification of AFNetworking. I subclassed it to create a custom Instapaper API client that looks like this:

#import "AFInstapaperClient.h"
#import "AFJSONRequestOperation.h"

@implementation AFInstapaperClient

+ (AFInstapaperClient *)sharedClient {
    static AFInstapaperClient *sharedClient = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedClient = [[AFInstapaperClient alloc] initWithBaseURL:[NSURL URLWithString:@"https://www.instapaper.com/"]
                                                   key:@"..."
                                                   secret:@"..."];
    });

    return sharedClient;
}

- (id)initWithBaseURL:(NSURL *)url {
    if (self = [super initWithBaseURL:url]) {
        [self registerHTTPOperationClass:[AFJSONRequestOperation class]];
        [self setDefaultHeader:@"Accept" value:@"application/json"];
    }

    return self;
}

@end

And when they log in, the following code is executed:

- (IBAction)doneButtonPressed:(UIBarButtonItem *)sender {       
    [[AFInstapaperClient sharedClient] authorizeUsingXAuthWithAccessTokenPath:@"/api/1/oauth/access_token"
                 accessMethod:@"POST"
                 username:self.loginBox.text
                 password:self.passwordBox.text
                 success:^(AFXAuthToken *accessToken) {
                     // Save the token information into the Keychain
                     [UICKeyChainStore setString:accessToken.key forKey:@"InstapaperKey"];
                     [UICKeyChainStore setString:accessToken.secret forKey:@"InstapaperSecret"];

                     UIAlertView *alert = [[UIAlertView alloc]
                                           initWithTitle:@"Login Successful"
                                           message:@"Your articles are being downloaded now and will appear in your queue."
                                           delegate:nil
                                           cancelButtonTitle:@"OK"
                                           otherButtonTitles: nil];
                     [alert show];

                     [[NSUserDefaults standardUserDefaults] setObject:@"YES" forKey:@"IsLoggedInToInstapaper"];

                     [self dismissViewControllerAnimated:YES completion:nil];
                 }
                 failure:^(NSError *error) {                     
                     UIAlertView *alert = [[UIAlertView alloc]
                                           initWithTitle:@"Login Failed."
                                           message:@"Are you connected to the internet? Instapaper may also be down. Try again later."
                                           delegate:nil
                                           cancelButtonTitle:@"Okay"
                                           otherButtonTitles: nil];
                     [alert show];
                 }];
}

But the code never goes into the failure block. How could I modify my code so that it would allow me to tell them they need an Instapaper subscription account?


Solution

  • Based on your situation, I don't think you will ever trigger the failure block because your request isn't failing. You are getting a response from the web service. In my experience the failure block only executes if you fail to get a response because of something like network availability or something like it.

    Therefore, you need to handle the account error in the success block. One way you could do it is to read the status code that is returned in the response. If the status code is 400 like your console is showing then alert the user.

    You can follow the method used here "https://stackoverflow.com/q/8469492/2670912"