1

I’m trying to use CloudApp’s API (https://github.com/cloudapp/objective-c) to develop my app. I currently want to make it where the user can see the details of their account (e.g.: email, subscription details, etc.). Their API doesn’t seem to work properly to do that sort of thing, but their curl example works perfectly.

curl --digest -u dev2@trijstudios.ca:trij2323 \
     -H "Accept: application/json" \
     "http://my.cl.ly/account"

Which gets outputted to this:

{"created_at":"2015-01-11T21:08:56Z","domain":null,"domain_home_page":null,"email":"dev2@trijstudios.ca","id":1778166,"private_items":true,"updated_at":"2015-01-11T21:08:56Z","activated_at":"2015-01-11T21:08:56Z","subscribed":false,"socket":{"auth_url":"http://my.cl.ly/pusher/auth","api_key":"4f6dbc3b89fa4ee9a8ff","app_id":"4721","channels":{"items":"private-items_1778166"}},"subscription_expires_at":null}

I wanted to do something as much as the curl statement as possible. I looked around Google and StackOverflow and found this answer (Objective-C equivalent of curl request):

NSURL *url = [NSURL URLWithString: [NSString stringWithFormat:@"http://%@:%@@www.example.com/myapi/getdata", API_USERNAME, API_PASSWORD]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

[request setURL:url];
[request setHTTPMethod:@"GET"];
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];

NSError *error;
NSURLResponse *response;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

I had to slightly modify it, though:

NSString *apiUserName = [NSString stringWithFormat:@"dev2@trijstudios.ca"];
NSString *apiPassword = [NSString stringWithFormat:@"trij2323"];

NSURL *url = [NSURL URLWithString: [NSString stringWithFormat:@"http://%@:%@@my.cl.ly/account", apiUserName, apiPassword]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

[request setURL:url];
[request setHTTPMethod:@"GET"];
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];

NSError *error;
NSURLResponse *response;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

NSLog(@"%@", data);

But when I tried it, instead of getting the result of the curl statement in Terminal, I got this in the console:

<3c21646f 63747970 65206874 6d6c3e0a 0a3c6874 6d6c2078 6d6c6e73 3a6f673d 22687474 703a2f2f 6f70656e 67726170 6870726f 746f636f 6c2e6f72 672f7363 68656d61 2f222078 6d6c6e73 3a66623d 22687474 703a2f2f 7777772e 66616365 626f6f6b 2e636f6d 2f323030 382f6662 6d6c2220 6974656d 73636f70 65206974 656d7479 70653d22 68747470 3a2f2f73 6368656d 612e6f72 672f5468 696e6722 20636c61 73733d22 73717561 72657370 6163652d 64616d61 736b2220 6c616e67 3d22656e 2d434122 3e0a0a20 203c6865 61643e0a 20202020 0a202020 203c6d65 74612063 68617273 65743d22 7574662d 38223e0a 20202020 3c6d6574 61206874 74702d65 71756976 3d22582d 55412d43 6f6d7061 7469626c 65222063 6f6e7465 6e743d22 49453d65 6467652c

61676522 297c7c68 61734174 74722861 5b625d2c 22646174 612d7372 63222929 26262266 616c7365 22213d3d 67657441 74747228 615b625d 2c226461 74612d6c 6f616422 292b2222 2626496d 6167654c 6f616465 722e6c6f 61642861 5b625d29 7d696e69 7428293b 77696e64 6f772e59 55492626 5955492e 61646428 22737175 61726573 70616365 2d696d61 67656c6f 61646572 222c6675 6e637469 6f6e2861 297b7d29 3b0a7d29 28293b3c 2f736372 6970743e 0a3c7363 72697074 3e537175 61726573 70616365 2e616674 6572426f 64794c6f 61642859 293b3c2f 73637269 70743e0a 0a0a2020 20200a20 203c2f62 6f64793e 0a0a3c2f 68746d6c 3e200a>

(There’s a lot more, but I won’t make it long. Let me know if you need to see the full version.)

I’m not too sure what to do from here. Is there anyone that knows how to fix this? Thanks in advance.

Community
  • 1
  • 1
chrisjr
  • 760
  • 3
  • 11
  • 18
  • 2
    Instead of ` NSLog(@"%@", data);` Use `NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"%@",str);` – Midhun MP Jan 11 '15 at 22:16
  • You need to pass your data to NSJSONSerialization - https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSJSONSerialization_Class/index.html – Paulw11 Jan 11 '15 at 22:57
  • The text shown from CURL is JSON. From the OP's comment he may have an issue with the URL as he is getting HTML, but JSONSerialization is ultimately what will be needed – Paulw11 Jan 11 '15 at 23:08
  • @MidhunMP I tried that (with several variations), and oddly enough, it shows the code for my website instead. – chrisjr Jan 11 '15 at 23:09
  • @chrisjr: So your request is having some issue, that's why it returns the site html instead of result – Midhun MP Jan 11 '15 at 23:10
  • You are probably having a problem because the embedded username/password will be stripped from the URL as it is insecure, so your request is effectively unauthenticated. You need to set the authentication header on your request - http://stackoverflow.com/questions/1973325/nsurlconnection-and-basic-http-authentication-in-ios – Paulw11 Jan 11 '15 at 23:14
  • The username & password is being inserted before the host in the URL - this is basic authentication but will be stripped as it is insecure (well, more insecure than basic already is) as it may be logged in proxy files etc – Paulw11 Jan 11 '15 at 23:43
  • So authentication wasn't being used? I honestly didn't realize. Hmm. I'll see what I can do about that. – chrisjr Jan 11 '15 at 23:48

1 Answers1

0

A couple of thoughts

  1. Your response is HTML:

    <!doctype html>
    
    <html xmlns:og="http://opengraphprotocol.org/schema/" xmlns:fb="http://www.facebook.com/2008/fbml" itemscope itemtype="http://schema.org/Thing" class="squarespace-damask" lang="en-CA">
    
      <head>
    
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge,
    
    ...
    
    age")||hasAttr(a[b],"data-src"))&&"false"!==getAttr(a[b],"data-load")+""&&ImageLoader.load(a[b])}init();window.YUI&&YUI.add("squarespace-imageloader",function(a){});
    })();</script>
    <script>Squarespace.afterBodyLoad(Y);</script>
    
    
    
      </body>
    
    </html> 
    

    This generally means that there was some problem in the request (but without seeing the full text of the HTML, it's hard to say precisely what's wrong).

  2. Your curl specified an Accept header of application/json, but the Objective-C example used that value for the Content-Type header. The request isn't JSON (in this case, at least), so I suspect you meant to set the Accept header in your Objective-C code, as in the curl, not the Content-Type header.

  3. Your curl request specified "digest" authentication. The CloudApp's documentation also says it uses digest authentication. But the Objective-C code is not doing any authentication.

  4. You are performing synchronous network request. You never want to perform synchronous requests from the main thread.

You'll probably want to perform your request using NSURLSession (or if you need to support iOS versions prior to 7.0, NSURLConnection). This solves both points three and four, where you can perform authentication, as well perform the request asynchronously. For example, with NSURLSession, you can use the authentication delegate method, while still enjoying the elegance of the completion block pattern for the request, itself:

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
    if (challenge.previousFailureCount == 0) {
        NSURLCredential *credential = [NSURLCredential credentialWithUser:self.userid password:self.password persistence:NSURLCredentialPersistenceForSession];
        completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
    } else {
        completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
    }
}

// you might have to implement the session rendition of the above authentication routine; it depends upon your server configuration; the implementation will look almost identical to the above code
//
// - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler

- (void)performQuery {
    // note, no user credentials in the URL; will be handled by authentication delegate method
    NSURL *url = [NSURL URLWithString: @"http://www.example.com/myapi/getdata"];

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request setHTTPMethod:@"GET"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];

    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:[NSOperationQueue mainQueue]];

    NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        if (!data) {
            NSLog(@"dataTaskWithURL error: %@", error);
            return;
        }

        NSError *parseError;
        id responseObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
        if (responseObject) {
            // got the JSON I was expecting; go ahead and use it; I'll just log it for now
            NSLog(@"responseObject = %@", responseObject);
        } else {
            // if it wasn't JSON, it's probably some error, so it's sometimes useful to see what the HTML actually says
            NSLog(@"responseString = %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
        }
    }];
    [task resume];
}
Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Ok. Tried it. It's pulling the results that I wanted. Thanks for your help and I apologize for not responding immediately. – chrisjr Jan 14 '15 at 01:53