Wanted: Up-to-date example for JSON/POST with basic auth using AFNetworking-2

14,353

UPDATE: The JSON portion of the following was found to work for PUT/POST, but NOT for GET/HEAD/DELETE

After some wrangling, and help outside SO, I got something working, which I wanted to leave as a memento. In the end, I was really very impressed with AFNetworking-2. It was so simple, I kept trying to make it harder than it should have been. Given a jsonDict method that returns the json packet to send, I created the following:

- (void) submitAuthenticatedRest_PUT
{
    // it all starts with a manager
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    // in my case, I'm in prototype mode, I own the network being used currently,
    // so I can use a self generated cert key, and the following line allows me to use that
    manager.securityPolicy.allowInvalidCertificates = YES;
    // Make sure we a JSON serialization policy, not sure what the default is
    manager.requestSerializer = [AFJSONRequestSerializer serializer];
    // No matter the serializer, they all inherit a battery of header setting APIs
    // Here we do Basic Auth, never do this outside of HTTPS
    [manager.requestSerializer
        setAuthorizationHeaderFieldWithUsername:@"basic_auth_username"
        password:@"basic_auth_password"];
    // Now we can just PUT it to our target URL (note the https).
    // This will return immediately, when the transaction has finished,
    // one of either the success or failure blocks will fire
    [manager
        PUT: @"https://101.202.303.404:5555/rest/path"
        parameters: [self jsonDict]
        success:^(AFHTTPRequestOperation *operation, id responseObject){
            NSLog(@"Submit response data: %@", responseObject);} // success callback block
        failure:^(AFHTTPRequestOperation *operation, NSError *error){
            NSLog(@"Error: %@", error);} // failure callback block
    ];
}

3 setup statements, followed by 2 message sends, it really is that easy.

EDIT/ADDED: Here's an example @jsonDict implementation:

- (NSMutableDictionary*) jsonDict
{
    NSMutableDictionary *result = [[NSMutableDictionary alloc] init];
    result[@"serial_id"] = self.serialID;
    result[@"latitude"] = [NSNumber numberWithDouble: self.location.latitude];
    result[@"longitude"] = [NSNumber numberWithDouble: self.location.longitude];
    result[@"name"] = self.name;
    if ([self hasPhoto])
    {
        result[@"photo-jpeg"] = [UIImageJPEGRepresentation(self.photo, 0.5)
            base64EncodedStringWithOptions: NSDataBase64Encoding76CharacterLineLength];
}
return result;

}

It should just return a dictionary with string keys, and simple objects as values (NSNumber, NSString, NSArray (I think), etc). The JSON encoder does the rest for you.

Share:
14,353

Related videos on Youtube

Travis Griggs
Author by

Travis Griggs

Updated on October 14, 2022

Comments

  • Travis Griggs
    Travis Griggs over 1 year

    I have a toy app which submits an https JSON/POST using basic auth security. I've been told I should consider using AFNetworking. I've been able to install AFNetwork-2 into my XCode project (ios7 target, XCode5) just fine. But none of the examples out there seem to be relevant to current versions of AFNetworking-2, but rather previous versions. The AFNetworking docs are pretty sparse, so I'm struggling how to put the pieces together. The non-AFNetworking code looks something like:

    NSURL *url = [NSURL URLWithString:@"https://xxx.yyy.zzz.aaa:bbbbb/twig_monikers"];
    NSMutableURLRequest *request = [NSMutableURLRequest
        requestWithURL:url
        cachePolicy: NSURLRequestUseProtocolCachePolicy
        timeoutInterval: 10.0];
    
    NSData *requestData = [NSJSONSerialization dataWithJSONObject: [self jsonDict] options: 0 error: nil];
    [request setHTTPMethod: @"POST"];
    [request setValue: @"application/json" forHTTPHeaderField: @"Accept"];
    [request setValue: @"application/json" forHTTPHeaderField: @"Content-Type"];
    [request setValue:[NSString stringWithFormat: @"%d", [requestData length]] forHTTPHeaderField: @"Content-Length"];
    NSData *plainPassText = [@"app_pseudouser:sample_password" dataUsingEncoding:NSUTF8StringEncoding];
    NSString *base64PassText = [plainPassText base64EncodedStringWithOptions: NSDataBase64Encoding76CharacterLineLength];
    [request setValue:[NSString stringWithFormat: @"Basic %@", base64PassText] forHTTPHeaderField: @"Authorization"];
    
    FailedCertificateDelegate *fcd=[[FailedCertificateDelegate alloc] init];
    NSURLConnection *c=[[NSURLConnection alloc] initWithRequest:request delegate:fcd startImmediately:NO];
    [c setDelegateQueue:[[NSOperationQueue alloc] init]];
    [c start];
    NSData *data=[fcd getData];
    
    if (data)
        NSLog(@"Submit response data: %@", [NSString stringWithUTF8String:[data bytes]]);
    

    I'm not looking for someone to write my code for me. I just can't seem to figure out how to map the AFNetworking-2 parts over to that. Any links, or examples, or explanations much welcome.

    UPDATE 1

    The above is a non AF version that is known to work. Moving trying to get it all in one go, I just tried:

    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    manager.requestSerializer = [AFJSONRequestSerializer serializer];
    [manager.requestSerializer
        setAuthorizationHeaderFieldWithUsername:@"app_pseudouser"
        password:@"sample_password"];
    AFHTTPRequestOperation *operation =
    [manager
        PUT: @"https://172.16.214.214:44321/twig_monikers"
        parameters: [self jsonDict]
        success:^(AFHTTPRequestOperation *operation, id responseObject){
            NSLog(@"Submit response data: %@", responseObject);}
        failure:^(AFHTTPRequestOperation *operation, NSError *error){
            NSLog(@"Error: %@", error);}
    ];
    

    Which produces the following error:

    2013-10-09 11:41:38.558 TwigTag[1403:60b] Error: Error Domain=NSURLErrorDomain Code=-1012 "The operation couldn’t be completed. (NSURLErrorDomain error -1012.)" UserInfo=0x1662c1e0 {NSErrorFailingURLKey=https://172.16.214.214:44321/twig_monikers, NSErrorFailingURLStringKey=https://172.16.214.214:44321/twig_monikers}
    

    Watching on the server side, nothing ever makes it through. I don't know if it is because the https, or what, but I can flip the app back to the original code, and it gets through just fine.

    • Travis Griggs
      Travis Griggs over 10 years
      Ouch, anything I can do to avoid the downvotes? The AFNetworking home page explicitly encourages questions to be asked on SO.
    • Jano
      Jano over 10 years
      Why are you going for AFNetworking-2 instead NSURLSession?
    • Travis Griggs
      Travis Griggs over 10 years
      @Jano Some colleagues encouraged me to look at AFNet after seeing stuff like the above. Having watched some presentations on AFNet-2, there appears to be those that still feel that using AFNet-2 (which wraps around NSURLSession) offers compelling advantages.
    • PostCodeism
      PostCodeism over 10 years
      Wow there were downvotes here? It's good to see justice has prevailed. Some people are real jerks - people, help the guy or STFU!
    • Rok Jarc
      Rok Jarc over 10 years
      Agreed: even when looking at the first revision of the question i see no reason for downvoting this.
    • Travis Griggs
      Travis Griggs over 10 years
      The down votes were at origination time. It's all been uphill since then. I've noticed a real trend lately amongst the SO community to be overly curative. It's disappointing.
  • jerik
    jerik over 10 years
    How does the code for the jsonDict look like? Is it serialized as string?
  • jerik
    jerik over 10 years
    Perfect. Solves perhaps my image uploading issue as well. Thanks!
  • Joshua Dance
    Joshua Dance over 10 years
    From the AFNetworking docs it says that the default requestSerializer is "set to an instance of AFHTTPSerializer, which serializes query string parameters for GET, HEAD, and DELETE requests, or otherwise URL-form-encodes HTTP message bodies."
  • PostCodeism
    PostCodeism over 10 years
    Ok but I have that exact same setup for a GET request and it gives me a 3840 error: JSON text did not start with array or object and option to allow fragments not set
  • PostCodeism
    PostCodeism over 10 years
    This didn't work for me. AFNetworking 2 doesn't work with a token and GET request: stackoverflow.com/questions/20480072/…
  • Travis Griggs
    Travis Griggs over 10 years
    I ran into this myself yesterday. I've asked on the AFNetworking github forum and had it confirmed that it's not a bug per se (github.com/AFNetworking/AFNetworking/issues/1678). I'll update the answer above to reflect that and see if I can't contribute to your new question.
  • Trianna Brannon
    Trianna Brannon almost 9 years
    Thanks, this works exactly the same for a POST as well if you switch out the PUT.