AFNetworking and DELETE requests with Rails - ios

I'm putting the finishing touches on my own version of the heroku rails mobile iOS photo sharing app. I have implemented and successfully sent POST and GET requests via HTTP on iOS. Unfortunately, the Heroku tutorial explicitly states it won;t be going into how to write the DELETE request. Here's what I have so far:
+ (void)deletePhoto:(NSString *)owner
image:(NSString *)recordId
block:(void (^)(Photo *, NSError *))block
{
NSMutableDictionary *mutableParameters = [NSMutableDictionary dictionary];
[mutableParameters setObject:recordId forKey:#"photo[id]"];
NSLog(#"Destroying %#", recordId);
[[CloudGlyphAPIClient sharedClient] deletePath:#"/photo" parameters:mutableParameters success:^(AFHTTPRequestOperation *operation, id JSON) {
if (block) {
NSLog(#"DELETE sussessful");
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (block) {
block(nil, error);
}
}];
}
+ (void)getPhotos:(NSString *)owner
block:(void (^)(NSSet *photos, NSError *error))block
{
NSMutableDictionary *mutableParameters = [NSMutableDictionary dictionary];
[mutableParameters setObject:owner forKey:#"photo[owner]"];
[[CloudGlyphAPIClient sharedClient] getPath:#"/photos" parameters:mutableParameters success:^(AFHTTPRequestOperation *operation, id JSON) {
NSMutableSet *mutablePhotos = [NSMutableSet set];
for (NSDictionary *attributes in [JSON valueForKeyPath:#"photos"]) {
Photo *photo = [[Photo alloc] initWithAttributes:attributes];
[mutablePhotos addObject:photo];
}
if (block) {
block([NSSet setWithSet:mutablePhotos], nil);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (block) {
block(nil, error);
}
}];
}
I've based the DELETE request off of the GET request, except in this case we are looking for a particular image user a certain owner. I get an error that the routes file doesn't contain a path for DELETE /photos... i added the destroy method to the rails app and raked the routes.rb file.
I feel like this is a rails GOTCHA somewhere.. thanks for your help ^_^
TL;DR trying to write DELETE request for a rails app with AFNetworking

In AFNetworking, the DELETE path is a url path like this: photos/18.json is the record marked for removal. Found the answer over here:
Answer
Effectively, one can concat together a string to the url of the ActiveREcord in a table.

Related

How to work with two Stripe account in 'one' app

I am a newbie in iOS and also newbie in Stripe pay with iOS I want to pay Two Stripe account in iOS like as first I want to pay in One and after successfully pay in one account as soon as possible I want to pay in to second account for that I write a code like as:
"Transaction Start"
-(void)startTransaction
{
if ([self validateCustomerInfo])
{
[Stripe setDefaultPublishableKey:STRIPE_TEST_PUBLIC_KEY1];
STPCardParams *card = [[STPCardParams alloc] init];
card.number = txtCardNumber.text;
card.expMonth =[btnMonth.titleLabel.text integerValue];
card.expYear = [btnYear.titleLabel.text integerValue];
card.cvc = txtCvv.text;
[[STPAPIClient sharedClient] createTokenWithCard:card
completion:^(STPToken *token, NSError *error) {
if (error) {
[GlobalClass StopSpinner:self.view];
[AppDelegate ShowAlert:[NSString stringWithFormat:#"%#",[error localizedDescription]]];
} else {
[self postStripeToken:token];
}
}];
}
}
#Generated First Token.
-(void)postStripeToken:(STPToken*)token
{
[GlobalClass ActivateSpinner:self.view StringMSG:#"Please wait"];
NSDictionary *parameter=#{#"secretkey":STRIPE_SECRET_KEY1,#"stripeToken":token.tokenId,#"amount":#"2",#"currency":#"usd",#"description":#"iOS Transaction"};
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer.acceptableContentTypes = [manager.responseSerializer.acceptableContentTypes setByAddingObject:#"text/html"];
[manager POST:#"http://s570166064.onlinehome.us/seadealersWS/payment/payment.php" parameters:parameter success:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"Responce Object %#",responseObject);
if ([[responseObject valueForKey:#"status"]isEqualToString:#
"Success"])
{
# Here i want to Pay in second account #
[Stripe setDefaultPublishableKey:STRIPE_TEST_PUBLIC_KEY2];
STPCardParams *card = [[STPCardParams alloc] init];
card.number = txtCardNumber.text;
card.expMonth =[btnMonth.titleLabel.text integerValue];
card.expYear = [btnYear.titleLabel.text integerValue];
card.cvc = txtCvv.text;
[[STPAPIClient sharedClient] createTokenWithCard:card
completion:^(STPToken *token, NSError *error) {
if (error) {
[GlobalClass StopSpinner:self.view];
[AppDelegate ShowAlert:[NSString stringWithFormat:#"%#",[error localizedDescription]]];
} else {
[GlobalClass StopSpinner:self.view];
[GlobalClass ActivateSpinner:self.view StringMSG:#"Please wait"];
[self postStripeTokenTwo:token];
}
}];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
[GlobalClass StopSpinner:self.view];
[AppDelegate ShowAlert:#"Please try again"];
NSLog(#"Error %#",error);
}];
}
-(void)postStripeTokenTwo:(STPToken*)token
{
NSDictionary *parameter=#{#"secretkey":STRIPE_SECRET_KEY2,#"stripeToken":token.tokenId,#"amount":#"0.5",#"currency":#"usd",#"description":#"iOS Transaction"};
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer.acceptableContentTypes = [manager.responseSerializer.acceptableContentTypes setByAddingObject:#"text/html"];
[manager POST:#"http://s570166064.onlinehome.us/seadealersWS/payment/payment.php" parameters:parameter success:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"Responce Object %#",responseObject);
if ([[responseObject valueForKey:#"status"]isEqualToString:#
"Success"])
{
[GlobalClass StopSpinner:self.view];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
[GlobalClass StopSpinner:self.view];
[AppDelegate ShowAlert:#"Please try again"];
NSLog(#"Error %#",error);
}];
}
But for First account i get success response but for Second account it says that Your token is not valid i not want to set stripe connect.
Thank you, and sorry for bad english.
A token can only be used once and is only valid in the account that created it. You would have to use two tokens (one created per account)
Also, you should not be shipping your Stripe secret API key in your app. Strings in application code can easily be discoverable and you do not want to leak your API key. The standard approach is to send the token to a server you control and from your server send the API call.
In addition, you may want to take a look at Stripe Connect, it may be a better approach to your issue then storing api keys of multiple accounts.
This problem is exist in the older versions of Stripe SDK
In order to resolve this issue
1) upgrade the SDK to the latest version(13.2.0)
pod Stripe, '~>13.2.0'
pod install --repo-update
2) Instead of setting
STPPaymentConfiguration.shared().publishableKey = STRIPE_TEST_PUBLIC_KEY1
use
STPAPIClient.shared().publishableKey = STRIPE_TEST_PUBLIC_KEY1
3) Clean and run the project and it will work as expected.
Please refer this link for more information

iOS Run two asynchronous method if failed

I have a scenario where I need to quiet refresh auth token (relogin) again if it expired when I accessing other API but I'm having a hard time thinking how to code this without creating redundant codes for every APIs even though the flow is similar.
When user has expired auth token > call paid API A (return 401 unauthorised) > relogin again > call paid API A (run successfully)
I'm having difficult in wrapping my mind to call paid API A the second time with less code and not falling into infinite loop trap. Is there any method useful for this case like NSNotification center?
Note: I need to use API in this format from AFNetworkinglogin
- (NSURLSessionDataTask *)getApiA:(CallbackBlock)block{
CallbackBlock _block = [block copy];
NSString *urlString = [[NSURL URLWithString:GET_API_A_URL relativeToURL:[NSURL URLWithString:HOME_URL]] absoluteString];
return [self GET:urlString parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
NSDictionary *response = (NSDictionary *)responseObject;
BLOCK_SAFE_RUN(block, response, nil, task);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
if([self unauthorizedAccess:task]){ //401
***//call Login once again > run getApiA again***
}else if ([self forbiddenAccess:task]){ //403
}
BLOCK_SAFE_RUN(block, nil, error, task);
}];
}
If i get it right you could split it into 2 methods. And pass a bool for trying again. e.g.:
- (NSURLSessionDataTask *)getApiA:(id)block {
NSString *urlString = [[NSURL URLWithString:GET_API_A_URL relativeToURL:[NSURL URLWithString:HOME_URL]] absoluteString];
return [self doApiACallWithURL:urlString firstTry:YES completion:block];
}
- (NSURLSessionDataTask *)doApiACallWithURL:(NSString *)url firstTry:(BOOL)first completion:(CallbackBlock)completion {
__weak typeof(self) wself = self;
return [self GET:urlString parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
NSDictionary *response = (NSDictionary *)responseObject;
BLOCK_SAFE_RUN(block, response, nil, task);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
if ([wself unauthorizedAccess:task]) { //401
if (first) {
[wself doApiACallWithURL:url firstTry:NO completion:completion];
}
} else if ([wself forbiddenAccess:task]) { //403
}
BLOCK_SAFE_RUN(block, nil, error, task);
}];
}
and use a weak self for blocks is in most cases a good idea.

How to get JSON out of AFNetworing 2.0 GET Method

I am using AFNetworking to make a bunch of API calls at once to Instagram. I am using AFNetworking 2.0 AFHTTPSessionManager GET request.
I am using a for loop to create the URL which requires a location ID that I am searching. The for loop then makes in this case 3 separate API calls and stores each response in an array.
I then create a mutable array to store the objects in the array. I need to get the mutable array once it has all three objects in it. I have tried passing the data to another method, but then I get into the same problem - can't really use the array until the previous method is done running.
-(void)sendGetRequestForLocationMedia:(NSMutableArray*)mutableArray{
for (int i = 0; i<3; i++) {
NSString *getLocationMediaString = [[NSString alloc]initWithFormat:#"locations/%#/media/recent?client_id=%#", self.locationIDObjectArray[i], client_id];
NSLog(#"getLocationMediaString %#", getLocationMediaString);
[[LPAuthClient sharedClient]GET:getLocationMediaString parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
if (httpResponse.statusCode == 200) {
dispatch_async(dispatch_get_main_queue(), ^{
_locationMediaArray = responseObject[#"data"];
if (!_locationMediaJSONArray) {
_locationMediaJSONArray = [[NSMutableArray alloc]init];
}
for (NSDictionary *dictionary in _locationMediaArray) {
[_locationMediaJSONArray addObject:dictionary];
}
});
}
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(#"Failure %#", error);
}];
}
}

AFNetworking 2.0 - how to pass response to another class on success from subclassed AFHTTPSessionManager

Beginner ios AFNetworking 2.0 Qns: Having subclassed AFHTTPSessionManager to something like "MyAPIManager" and placed my all my API calls (GET/POST/PUT etc.) in this custom manager class, I'm having problems making use of the response on request success in another class (say class B).
I know I can refactor this and pluck out the POST call portion to class B, so that I can dump the relevant class B methods in the callback, but this would get messy, especially with multiple API calls.
I want to pass this response (e.g. the returned objectId) to another class and right now I'm just using a NSNotification which class B listens for, but this still feels a bit 'hackish' and am wondering if there is a better way to do this.
Currently in MyAPIManager : AFHTTPSessionManager:
- (void) POSTRecordJson:(NSDictionary *)json
{
[self POST:#"classes/Record/" parameters:json success:^(NSURLSessionDataTask *task, id responseObject) {
NSLog(#"Posted JSON: %#", json.description);
if ([responseObject isKindOfClass:[NSDictionary class]]) {
NSLog(#"Response: %#", responseObject);
//Notify objectId received
[[NSNotificationCenter defaultCenter]
postNotificationName:#"ReceivedObjectIdNotification"
object:self
userInfo:responseObject];
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
And in Class B I've called:
MyApiManager *manager = [MyApiManager sharedInstance];
[manager POSTRecordJson:someJSONdict];
you could do 2 things.. by using a protocol/delegate or a block..
but i, personally, prefers block soo..
first make a block Datatype
typedef void(^SuccessBlock)(id success); example
and add the parameter with the block on it
- (void) POSTRecordJson:(NSDictionary *)json success:(SuccessBlock)success
{
[self POST:#"classes/Record/" parameters:json success:^(NSURLSessionDataTask *task, id responseObject) {
NSLog(#"Posted JSON: %#", json.description);
if ([responseObject isKindOfClass:[NSDictionary class]]) {
NSLog(#"Response: %#", responseObject);
//Notify objectId received
success(responseObject);
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
and to call the new function..
MyApiManager *manager = [MyApiManager sharedInstance];
[manager POSTRecordJson:someJSONdict success:^(id result){
NSDictionary *dictionary = (NSDictionary *)result;
NSLog(#"response: %#",dictionary)
}];
You would want to pass a completion block into your -POSTRecordJson: method.
For example, you would refactor your method to do the following:
- (void) POSTRecordJson:(NSDictionary *)json completion:(void(^)(BOOL success, id response, NSError *error))completion
{
[self POST:#"classes/Record/" parameters:json success:^(NSURLSessionDataTask *task, id responseObject) {
NSLog(#"Posted JSON: %#", json.description);
if ([responseObject isKindOfClass:[NSDictionary class]])
{
NSLog(#"Response: %#", responseObject);
if (completion) //if completion is NULL, calling it will crash your app so we always check that it is present.
{
completion(YES, responseObject, nil);
}
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(#"Error: %#", error);
if (completion)
{
completion(NO, nil, error);
}
}];
}
You could then handle this implementation like so:
//assuming `manager` and `dictionary` exist.
[manager POSTRecordJson:dictionary completion^(BOOL success, id response, NSError *error) {
if (success)
{
//do something with `response`
}
else
{
//do something with `error`
}
}];
However, if you are a beginner with AFNetworking and you want to adopt a great structure for handling web services, you should check out this excellent blog post.
You can use blocks to send the response back to the class after the response received from the server:
- (void) POSTRecordJson:(NSDictionary *)json response:(void (^)(id response, NSError *error))responseBlock
{
[self POST:#"classes/Record/" parameters:json success:^(NSURLSessionDataTask *task, id responseObject) {
NSLog(#"Posted JSON: %#", json.description);
if ([responseObject isKindOfClass:[NSDictionary class]]) {
NSLog(#"Response: %#", responseObject);
responseBlock(responseObject, nil);
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(#"Error: %#", error);
responseBlock(nil, error);
}];
}

AFNetworking completion block wait until done

I'm using AFNetworking for POST requests. I need to wait until the completion block is done to return data, and I've run into problems.
I had a solution that was working until I switched to AFNetworking:
int i = 0;
while (!done)
{
[NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
i++;
NSLog(#"While Loop:%i", i);
if (i == 149) //request timed out
done = YES;
}
Now, the solution works sporadically. Sometimes it completes with NSLog(#"While Loop:%i", i); only logging 1 or 2, but sometimes it logs until 149 and times out. It seems like the NSRunLoop sometimes runs in a different thread, but sometimes runs on the same thread blocking my request.
Here's the code that I currently have:
- (id)postRequestWithBaseURLString:(NSString *)baseURLString function:(NSString *)function parameters:(NSDictionary *)parameters
{
if (baseURLString)
function = [baseURLString stringByAppendingString:function];
__block BOOL done = NO;
__block id returnObject = nil;
[self.manager POST:function parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"Success: %#", responseObject);
done = YES;
returnObject = responseObject;
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"Error: %#", error);
done = YES;
}];
[manager.operationQueue waitUntilAllOperationsAreFinished];
int i = 0;
while (!done)
{
[NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
i++;
NSLog(#"While Loop:%i", i);
if (i == 149) //request timed out
done = YES;
}
return returnObject;
}
I've tried doing a dispatch_group without success. Help?
EDIT
Ok, I've got more info. My solution works when I first call the API (for example, when I first call a ViewController), but not afterwards. Perhaps, after the view is loaded, the while loop is called on the same thread as the API call, therefore blocking it?
This also seems likely, since the NSLog(#"Success: %#", responseObject); is called almost exactly after timeout happens.
Found an elegant solution online. I created my own completion block. Here's what I did:
+ (void)jsonRequestWithBaseURL:(NSString *)baseURL function:(NSString *)function parameters:(NSDictionary *)parameters completion:(void (^)(NSDictionary *json, BOOL success))completion
{
if (baseURL)
function = [baseURL stringByAppendingString:function];
NSLog(#"%# function:%#, parameters:%#", self.class, function, parameters);
[AFHTTPRequestOperationManager.manager POST:function parameters:parameters success:^(AFHTTPRequestOperation *operation, id jsonObject)
{
//NSLog(#"Success: %#", jsonObject);
NSDictionary *jsonDictionary = (NSDictionary *)jsonObject;
if (completion)
completion(jsonDictionary, YES);
} failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"%# AFError: %#", self.class, [error localizedDescription]);
completion(nil, NO);
}];
}
Then, when I need to call the method, I do this:
NSDictionary *parameters = #{#"email":#"me#example.com", #"password":#"mypassword"};
[ZAPRootViewController jsonRequestWithBaseURL:#"http://www.myserver.com/" function:#"login.php" parameters:parameters completion:^(NSDictionary *json, BOOL success)
{
if (success)
{
//do something
}
}];

Resources