Get JSON from Snapchat/Snaphax API in objective C - objective-c

I"m trying to post data to a server from objective C and I am trying to get a JSON returned in it.
I am looking at the Snaphax API for PHP and Snaphaxpy API and trying to rewrite it from PHP into Objective C.
The links for the code are:
https://github.com/tlack/snaphax
https://github.com/jasonanovak/snaphaxpy/blob/master/snaphaxpy.py
I'm also especially looking at: http://adamcaudill.com/2012/06/16/snapchat-api-and-security/
but apparently this is outdated
My code is:
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"https://feelinsonice.appspot.com/ph/login"]];
[request setHTTPMethod:#"POST"];
[request addValue:#"******testusername******" forHTTPHeaderField:#"username"];
[request addValue:#"*********" forHTTPHeaderField:#"password"];
[request addValue:#"M02cnQ51Ji97vwT4" forHTTPHeaderField:#"blob_enc_key"];
[request addValue:#"false" forHTTPHeaderField:#"debug"];
[request addValue:#"iEk21fuwZApXlz93750dmW22pw389dPwOk" forHTTPHeaderField:#"secret"];
[request addValue:#"m198sOkJEn37DjqZ32lpRu76xmw288xSQ9" forHTTPHeaderField:#"static_token"];
[request addValue:#"Snaphax 4.0.1 (iPad; iPhone OS 6.0; en_US)" forHTTPHeaderField:#"user_agent"];
[request addValue:#"930cf95a6731dc986ef3bceef6abbaf420e94d8d197dca87b9b47314d8c51b3b" forHTTPHeaderField:#"req_token"];
[request addValue:#"1355776346532" forHTTPHeaderField:#"timestamp"];
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *dataString = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
NSLog(#"%#", dataString);
I haven't included a real username/password. How come, however, does this not work as I have literally copied everything I have found and implemented in a new language...
Am I not POSTing the data correctly? I tried using ASIHTTPRequest but I couldn't get that working either...
Any suggestions or ideas based on experience??

Check my answer here: how can I use NSURLConnection Asynchronously?
You can send a POST request by using
HTTPCachedController *ctrl = [[[HTTPCachedController alloc] initWithRequestType:1 andDelegate:self] autorelease];
[ctrl postRequestToURL:#"https://feelinsonice.appspot.com/ph/login" withData:#"username=user1&password=pass& ... "];
Source code of the HTTPCachedController can be found here: HTTPCachedController

If you are looking for updated endpoints and whatnot, I wrote a quick Python script that you should be able to read through for some endpoints and the parameters they require. Some of the endpoints are as follow:
Base URL https://feelinsonice-hrd.appspot.com/
Login https://feelinsonice.appspot.com/ph/login
Get Snap by ID https://feelinsonice.appspot.com/ph/blob?id={}
Shameless Plug: I'm hosting a wrapper for the Snapchat API right now so you don't have to implement encryption into your project and you can safe time and your project can be more efficient. Check it out here!
You will also notice a lot of strange abbreviations in the JSON (like Adam said, Snapchat's API wasn't really built for human consumption), so I've gone ahead and mapped that out for you:
ID: id
Snap ID: c_id
Media Type: m = 0 (pic), 1 (video)
Sent Timestamp: sts
Opened Timestamp: ts
Sender: sn
Recipient: rp
Status: st = 1 (sent to you), 2 (sent by you)
Time: t
Screenshot Count: c
Note: I will be updating that repo with some new code within the next month or so. Also, the most up-to-date endpoints often vary between /ph/ and /bq/ for different requests for some reason, so don't accidentally use one when you meant to use the other.

Related

afnetworking request parameter sequence

I am trying to produce a request using afnetworking in objective c, however, it seems like the hardware that I am trying to connect to only applies requests when the parameters of the request are in a specific order. So I am wondering if there is a way to make the request so that the parameters are in a specific order. (As just doing it normally seems to jumble the sequence of the params up)
Here's my code:
NSDictionary *params = #{
#"param1" : #"bla",
#"param2" : #"bla2",
#"param3" : #"bla3"
};
[requestManager GET:#"somewhere" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
DLog(#"Success!");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
DLog(#"Fail: %#", error);
}];
It actually goes to success every time, its just that the request I had applied would be practically ignored.
The actual request body becomes something like "param3=bla3&param1=bla1&param2=bla2 etc which would be ignored as it seems.
You can't do that using the request manager in the way you currently are.
Instead, you would need to create the parameter list yourself, and then create a request from that. Then you could use AFN to handle the request transmission and response.
Note that the server shouldn't require a specific order and that this should be changed if possible. Note also that the dictionary of parameters has no order (even though you add the keys in a set order).
Keeping the order of the parameters have a great impact on server performance. This sounds silly at first, but just think about GET requests which contain the query string as part of the URL. Web servers can cache the response for the given URL. If you mess with the order of the parameters, the cache won't be as effective as it could be.
The case is even worse if you call an API from different platforms (iOS, Android, Web) and they all reorder the params, which means that the same content will be found on 3 different cache keys.
Keeping the order is a performance issue at the first place.

Quickblox: analog of "push" method in the RESTful API

In the Quickblox RESTful API there's a special update operator "push" that appends specified values to array.
Is there any analog in the Quickblox iOS SDK that doesn't require to send the whole array to the server to update one value?
Hope this help
QBCOCustomObject *object = [QBCOCustomObject customObject];
object.className = #"SuperSample";
object.ID = #"51d5a979efa357c7fa000006";
NSMutableDictionary *specialUpdateParams = [NSMutableDictionary dictionary];
[specialUpdateParams setObject:#"phone" forKey:#"push[interests]"];
[QBCustomObjects updateObject:object specialUpdateOperators:specialUpdateParams delegate:self];
Feel free to try Snippets https://github.com/QuickBlox/quickblox-ios-sdk/tree/master/snippets
This project contains lots of examples

AFNetworking - Request method as a parameter

I'm seeing in AFHTTPClient documentation these pretty tasks for Making HTTP Requests:
- getPath:parameters:success:failure:
- postPath:parameters:success:failure:
- putPath:parameters:success:failure:
- deletePath:parameters:success:failure:
- patchPath:parameters:success:failure:
Now if I need update or create something on my server,
let's say I want to differentiate the update/create by the method:
create -> PUT
update -> POST
Would there be a way to specify the method as a parameter somewhere, like:
- requestPath:method:parameters:success:failure:
Obviously I can create my own dispatcher, but the question is more about why this seems to be done intentionnaly as it is (hoping I'm clear).
postPath:parameters:success:failure: is a convenience method for the following:
NSMutableURLRequest *request = [client requestWithMethod:#"POST" path:#"/path" parameters:#{...}];
AFHTTPRequestOperation *operation = [client HTTPRequestOperationWithRequest:request success:^{...} failure:^{...}];
[client enqueueHTTPRequestOperation:operation];
You can customize any of the objects along the way using the long-form version.

Upload a photo with some parameters to a Ruby-on-rails web app using Objective-C and an ASIFormDataRequest for iOS

I need to upload a list of photos (can be one at a time) to my client's server from an iOS app (iOS 4). His server is using the Phusion Passenger 2.2.14 module on Apache Server (Ubuntu) to deploy his Ruby app.
Here is the Ruby code my client used to upload a photo using a Ruby rest client and it worked for one image. He mentonned that the image should be in an array but since here we are only sending one image, it worked.
resource = RestClient::Resource.new 'http://serveraddress.com/id.json', :user => '********', :password => '*****'
resource.put :property => {:new_images => {:public => '0',:t_viewable => '0', :l_viewable => '0', :name => 'name3', :room => 'Bathroom', :attachment_type => 'periodic', :attached_file => File.new('/Users/me/Pictures/12618298.jpg','rb')} }
My question is, how should I encode this data using Objective-C collections?
Authentication works fine.
Here is the code I am using, but it gets a 500 Internal Server Error:
[request setPostValue:photoName forKey:#"property[new_images][0][name]"];
[request setPostValue:[NSString stringWithFormat:#"%f",[[NSDate date] timeIntervalSince1970]] forKey:#"property[new_images][0][created_at]"];
[request setFile:photoPathFull withFileName:[self.localCaptions objectAtIndex:index] andContentType:#"image/jpeg"
forKey:#"property[new_images][0][attached_file]"];
[request setPostValue:self.room forKey:#"property[new_images][0][room]"];
[request setPostValue:imageCategory forKey:#"property[new_images][0][attachment_type]"];
[request setPostValue:public forKey:#"property[new_images][0][public]"];
[request setPostValue:tViewable forKey:#"property[new_images][0][t_viewable]"];
[request setPostValue:lViewable forKey:#"property[new_images][0][l_viewable]"];
*Note that the request variable is an ASIFormDataRequest object.
He also specified the server expects the following parameters:
property[new_images][1][name]=Sink
property[new_images][1][created_at]=<timestamp>
property[new_images][1][attached_file]=<filedata>
property[new_images][1][room]=Kitchen
property[new_images][1][attachment_type]=periodic
property[new_images][1][public]=0
property[new_images][1][t_viewable]=0
property[new_images][1][l_viewable]=0
Thanks,
I finally got access to the errors log on the server side, so here is what it looks like:
ActiveRecord::UnknownAttributeError: unknown attribute: 0
{"format"=>"json",
"property"=>
{"new_images"=>
{"0"=>
{"tenant_viewable"=>"0",
"name"=>"photo2011-10-01 08:43:07 +0000.jpg",
"landlord_viewable"=>"0",
"attached_file"=>"#<File:0xda0361c>",
"created_at"=>"1317459424.523741",
"attachment_type"=>"periodic_check",
"public"=>"0",
"room"=>"Kitchen"}}},
"action"=>"update",
"id"=>"866",
"controller"=>"properties"}
Action properties#update
URL http://server.com/000.json
File [GEM_ROOT]/gems/activerecord-2.2.2/lib/active_record/base.rb:2587
It seems clear now that the attribute 0 (after attribute "new_images") is not expected from the server. Seems like it's more of an array position than an attribute.
to do it for one image at a time you need to set the full property key value
[request setPostValue:#"Sink" forKey:#"property[new_images][1][name]"];
[request setPostValue:<timestamp> forKey:#"property[new_images][1][created_at]"];
....
Tedious, but I'm not sure you can set the single key property using an NSDictionary directly. Dont think ASI is that smart.
To do it for an array, just enumerate through your array of images and set the [1] portion of the request key name to the actual index (e.g. [1], [2], [3], etc...)
I think that should work.
ASIHTTPRequest can't automatically do a conversion of an array to a HTTP POST like that.
Try using ASIFormDataRequest instead:
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
// Upload a file on disk
[request setFile:#"/Users/ben/Desktop/ben.jpg" withFileName:#"myphoto.jpg" andContentType:#"image/jpeg" forKey:#"photo"];
You'll probably have to experiment to some extent to figure out exactly the right format. I've advise using charlesproxy to compare the requests send by your working ruby code and the non-working iOS code on the emulator.

How to download a file (iOS/Cocoa SDK) from URL where file name is not the part of URL

I was wondering on how to download a file using NSUrl where the filename is not available or is not part of the request URL. I have been looking at ASIHTTPRequest (http://allseeing-i.com/ASIHTTPRequest/) library. It has
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDownloadDestinationPath:#"/Users/ben/Desktop/my_file.txt"];
But the point is you need to know the file name here. For example if you put the URL (not real URL) http://some-website/foo.pdf
It downloads a file foo.pdf and I want to save it like that. Any ideas thanks.
In the NSURLConnection delegate callback - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response you can retrieve the suggestedFileName from the NSURLResponse. If you get unknown for a name then you need to come up with another name for your file. At this point you can specify your download path before you start saving data.
suggestedFilename
Returns a suggested
filename for the response data.
(NSString *)suggestedFilename
Return Value
A suggested filename for the
response data.
Discussion
The method tries to create
a filename using the following, in
order:
A filename specified using the content
disposition header.
The last path
component of the URL.
The host of the
URL.
If the host of URL can't be
converted to a valid filename, the
filename “unknown” is used.
In most cases, this method appends the
proper file extension based on the
MIME type. This method will always
return a valid filename regardless of
whether or not the resource is saved
to disk.
Availability
Available in iOS 2.0 and
later. Declared In NSURLResponse.h