My app sends receipt to server after restore purchase. To dictionary adds two additional keys: "bundleId" (app bundle id), "UUID" (app identifierForVendor).
After approved and first run, application everything is ok (after restoring i getting all keys). When the user deleted the application and reinstall, these to keys has null value.
Fetch current appStoreReceipt:
if(!self.receiptData){
NSURL *receiptURL = [[NSBundle mainBundle]
appStoreReceiptURL];
self.receiptData = [NSData
dataWithContentsOfURL:receiptURL]
receipt = [self.receiptData bkrBase64EncodedString];
}
else{
receipt = [self.receiptData bkrBase64EncodedString];
}
Apple request:
if(receipt){
NSError *error;
NSDictionary *requestContents = #{
#"receipt-data" : receipt,
#"password" : //purchaseAppSecreatKey
};
NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents
options:0
error:&error];
if (!requestData) { /* ... Handle error ... */
}
// Create a POST request with the receipt data.
NSURL *storeURL = ///iTunesVerificationURL
NSMutableURLRequest *storeRequest =
[NSMutableURLRequest requestWithURL:storeURL];
[storeRequest setHTTPMethod:#"POST"];
[storeRequest setHTTPBody:requestData];
// Make a connection to the iTunes Store on a background queue.
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
[NSURLConnection
sendAsynchronousRequest:storeRequest
queue:operationQueue
completionHandler:^(NSURLResponse *response, NSData *data,
NSError *connectionError) {
if (connectionError) {
NSLog(#"response error %#", connectionError.localizedDescription);
} else {
NSError *error;
NSDictionary *jsonResponse =
[NSJSONSerialization JSONObjectWithData:data
options:0
error:&error] ;
if(!error){
//success, sending to server
}else{
NSLog(#"parse error %#", error.localizedDescription);
}
}
}];
}
Send to server code
NSMutableDictionary *requestBodyDictonary = [NSMutableDictionary dictionaryWithDictionary:reciptDic];
[requestBodyDictonary setObject:[self bundleId] forKey:#"bundleId"];
[requestBodyDictonary setObject:[self UUID] forKey:#"uuid"];
NSURL *url = [NSURL URLWithString:];
NSError *error = nil;
NSData *bodyData = [NSJSONSerialization dataWithJSONObject:requestBodyDictonary options:0 error:&error];
if(error == nil){
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]
initWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:15.0];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:bodyData];
NSURLSession *defaultstSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLSessionDataTask *task = [defaultstSession dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(#"complete %ld", (long)[(NSHTTPURLResponse *)response statusCode]);
}];
[task resume];
}else{
NSLog(#"error parshe %#", [error localizedDescription]);
}
Fetched uuid and bundleid
#pragma mark - User ID
-(NSString *)UUID{
if(!_UUID){
_UUID = [[NSUserDefaults standardUserDefaults] stringForKey:#"identifierForVendor_UUID"];
if(!_UUID){
_UUID = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
[[NSUserDefaults standardUserDefaults]setObject:_UUID forKey:#"identifierForVendor_UUID"];
[[NSUserDefaults standardUserDefaults]synchronize];
}
}
return _UUID;
}
#pragma mark bundle id
-(NSString *)bundleId{
if(!_bundleId){
_bundleId = [[NSUserDefaults standardUserDefaults] stringForKey:#"app_BundleId"];
if(!_bundleId){
_bundleId = [[NSBundle mainBundle]bundleIdentifier];
if(!_bundleId){
_bundleId = (__bridge_transfer NSString *)CFDictionaryGetValue(CFBundleGetInfoDictionary(CFBundleGetMainBundle()),
(const void *)(#"CFBundleIdentifier"));
}
[[NSUserDefaults standardUserDefaults] setObject:_bundleId forKey:#"app_BundleId"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
return _bundleId;
}
Why my app return null after reinstall?
In sandbox mode everything is ok
If you delete an APP then it will clear all NSUserDefault values also, we have to use keychain to store values, it will remain same if you delete app.
use KeychainItemWrapper files to store both UDID and BundleID
download keychanItemwrapper from github
#import "KeychainItemWrapper.h"
then set values do Like this:
UIDevice *device = [UIDevice currentDevice];
NSString *div = [[device identifierForVendor]UUIDString];
keychain = [[KeychainItemWrapper alloc] initWithIdentifier:#"TestUDID" accessGroup:nil];
userDevice_id = [keychain objectForKey:(__bridge id)(kSecAttrAccount)];
if([userDevice_id isEqualToString:#""])
{
keychain = [[KeychainItemWrapper alloc] initWithIdentifier:#"TestUDID" accessGroup:nil];
[keychain setObject:div forKey:(__bridge id)(kSecAttrAccount)];
}
get values from variable:
keychain = [[KeychainItemWrapper alloc] initWithIdentifier:#"TestUDID" accessGroup:nil];
userDevice_id = [keychain objectForKey:(__bridge id)(kSecAttrAccount)];
Related
I'm facing a really strange issue and I hope somebody can help me understand where is the problem on which I'm stuck since three days ago.
In simple I make a NSMutableRequest to a simple endpoint where I send (using POST) a json and I'm supposed to receive a response 0 or 1.
The code works the first time the app is run on the device, but somehow it does not work anymore the following times.
To explain myself better, if I uninstall and reinstall the app every time, I get the correct response, but if I run the code twice the second time I get something like [CSRF verification failed] from the endpoint. This error means that I'm not sending the correct format ( or I'm sending something strange).
My question is: how is that possible? Is it possible that I'm sending something else?
The endpoint works correctly because with the android version I don't have any problems...
The code is the following, hope somebody can explain me what is happening under the hood and how I can manage to solve this problem.
NSString *mail = [profile valueForKey:#"email"];
NSString *provider = [profile valueForKey:#"provider"];
// making a GET request to endpoint
NSString *baseUrl = ENDPOINT_URL;
NSString *targetUrl = [NSString stringWithFormat:#"%#", baseUrl];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setHTTPMethod:#"POST"];
[request addValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request addValue:#"application/json" forHTTPHeaderField:#"Accept"];
NSString *body = [NSString stringWithFormat:#"{\"mail\":\"%#\", \"provider\":\"%#\"}",mail,provider];
NSData *postData=[body dataUsingEncoding:NSUTF8StringEncoding];
[request setHTTPBody:postData];
[request setURL:[NSURL URLWithString:targetUrl]];
[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:
^(NSData * _Nullable data,
NSURLResponse * _Nullable response,
NSError * _Nullable error) {
if (data){
NSString *myString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"Data received: %#", myString);
if ([myString isEqualToString:#"[\"CSRF validation failed\"]"]){
NSLog(#"ENDPOINT ERROR");
dispatch_async(dispatch_get_main_queue(), ^{
[(AppDelegate *)[[UIApplication sharedApplication] delegate] loginAborted];});
} else {
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
NSLog(#"Data received: %#", json);
NSNumber *value = [NSNumber numberWithInt:[[json objectForKey:#"profile_exists"] intValue]];
if ([value intValue] == 1){
//Profile exists.
NSLog(#"Profile exists.");
[self silentLogin:profile];
} else if ([value intValue] == 0) {
//Profile does not exists.
NSLog(#"Profile does not exist.");
[self silentRegistration:profile];
}
else {
//Error.
NSLog(#"Error in ENDPOINT VALUE");
dispatch_async(dispatch_get_main_queue(), ^{
[(AppDelegate *)[[UIApplication sharedApplication] delegate] loginAborted];});
}
NSLog(#"%#",json);
}
} else {
NSLog(#"No Data received");
dispatch_async(dispatch_get_main_queue(), ^{
[(AppDelegate *)[[UIApplication sharedApplication] delegate] loginAborted];});
}
}] resume];
I was able to solve the problem deleting the cookies every time I make a request. It seems that these cookies are re sent every time.
+ (void)clearCookiesForURL: (NSString *)url {
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray *cookies = [cookieStorage cookiesForURL:[NSURL URLWithString:url]];
for (NSHTTPCookie *cookie in cookies) {
NSLog(#"Deleting cookie for domain: %#", [cookie domain]);
[cookieStorage deleteCookie:cookie];
}
}
Hi fairly new to iOS just trying to connect the app to an API and get my token using Authentication. I have tried a lot of different options and just can't seem to get my head around it. All i'm trying to do is obtain my token. Can anyone see where I'm going wrong? The API documentation is here: https://simplybook.me/en/api/developer-api
NSURL *url = [NSURL URLWithString:#"http://user-api.simplybook.me/login/"];
NSURLSession *session = [NSURLSession sharedSession];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:#"myloginname" forHTTPHeaderField:#"X-Company-Login"];
[request setValue:#"mytokenhere" forHTTPHeaderField:#"X-Token"];
NSURLSessionDataTask *downloadTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!error) {
id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSArray * resultDict =[json objectForKey:#"name"];
NSLog(#"%#", resultDict);
} else {
NSLog(#"%#", error);
}
}];
[downloadTask resume];
Instead of declaring NSURLSession like this:
NSURLSession *session = [NSURLSession sharedSession];
Try this instead:
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
NSURL *url = [NSURL URLWithString:#"https://user-api.simplybook.me/login/"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
And also, make sure you've declared the NSURLSessionDelegate protocol in your header file.
I will give you sample code for getting token but this is regarding to the access and refresh token.You can understand it very clearly now.
ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
}
- (IBAction)actionLogin:(id)sender {
[self loginForGettingAccessandRefreshToken];
}
-(void)loginForGettingAccessandRefreshToken
{
#try {
NSString *strUserName = [textUsername.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSString *strPassword = [textPassword.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSString *strParameters = [NSString stringWithFormat:#"client_id=roclient&client_secret=secret&grant_type=password&username=%#&password=%#&scope=dataEventRecords offline_access", strUserName, strPassword];
id param = strParameters;
NSLog(#"The param is - %#",param);
[GlobalShareClass postLoginDataToServer:BASEURL_LOGIN passParameters:param success:^(id res)
{
if([res isKindOfClass:[NSDictionary class]]){
NSDictionary *dictRes = [res copy];
NSString *strErrDesc = [NSString stringWithFormat:#"%#",[dictRes objectForKey:#"error_description"]];
if([[dictRes allKeys] containsObject:#"error_description"]){
dispatch_async(dispatch_get_main_queue(), ^{
//Dispaly UI
});
}
else{
NSString *strAccessToken = [dictRes objectForKey:#"access_token"];
NSString *strRefreshToken = [dictRes objectForKey:#"refresh_token"];
NSString *strTokenType = [dictRes objectForKey:#"token_type"];
NSString *sstrExpiresIn = [dictRes objectForKey:#"expires_in"];
[[NSUserDefaults standardUserDefaults] setObject:globalShare.strRefreshToken forKey:#"refresh_token"];
[[NSUserDefaults standardUserDefaults] setObject:globalShare.strAccessToken forKey:#"access_token"];
[[NSUserDefaults standardUserDefaults] synchronize];
NSDictionary *responseDict = [GlobalShareClass getDataFromToken:globalShare.strAccessToken];
NSString *strFetchedSub = [[NSUserDefaults standardUserDefaults] stringForKey:#"loginIdSub"];
globalShare.loginId = [responseDict objectForKey:#"sub"];
}
}
else{
NSLog(#"The res starts with array");
}
} failure:^(NSError *error) {
// error handling here ...
NSLog(#"%#", [error localizedDescription]);
dispatch_async(dispatch_get_main_queue(), ^{
//Showing Error
});
} passViewController:self];
}
#catch(NSException *exception){
NSLog(#"%#",[exception description]);
}
#finally{
}
}
}
GlobalShareClass.h
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#interface GlobalShareClass : NSObject{
NSMutableArray *arr;
}
#property (strong, nonatomic) NSMutableArray *arr;
+ (GlobalShareClass *)sharedInstance;
+ (void)postLoginDataToServer:(NSString *)url passParameters:(id)parameter success:(void (^)(id res))successPost failure:(void(^)(NSError* error))failurePost passViewController:(UIViewController *)vc;
+ (NSDictionary *)getDataFromToken:(NSString *)strToken;
+ (NSDictionary *)urlBase64Decode:(NSString *)strToParse;
#end
GlobalShareClass.m
#import "GlobalShareClass.h"
#implementation GlobalShareClass
#synthesize arr;
+ (GlobalShareClass *)sharedInstance {
static GlobalShareClass* _shareInstane = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_shareInstane = [[GlobalShareClass alloc] init];
//Incase array of initialization
_shareInstane.arr = [[NSMutableArray alloc] init];
});
return _shareInstane;
}
+ (void)postLoginDataToServer:(NSString *)url passParameters:(id)parameter success:(void (^)(id res))successPost failure:(void(^)(NSError* error))failurePost passViewController:(UIViewController *)vc{
GlobalShareClass *globalShare = [GlobalShareClass sharedInstance];
__block id jsonResp;
NSData *data = [parameter dataUsingEncoding:NSUTF8StringEncoding];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/x-www-form-urlencoded;charset=UTF-8" forHTTPHeaderField:#"content-type"];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLSessionUploadTask *dataTask = [session uploadTaskWithRequest: request
fromData:data completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if(data == nil && error){
NSLog(#"uploadTaskWithRequest error: %#", error);
failurePost(error);
}
else{
jsonResp = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSString *strStatusCode = [NSString stringWithFormat:#"%ld",(long)[((NSHTTPURLResponse *)response) statusCode]];
if([strStatusCode isEqualToString:#"400"]){
successPost(jsonResp);
}
NSString *strStatusCodeResponse = [self strGetStatusResponseCode:strStatusCode];
if([strStatusCodeResponse length] > 0){
dispatch_async(dispatch_get_main_queue(), ^{
// Showing error
});
}else{
jsonResp = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if([jsonResp objectForKey:#"error"] || jsonResp == nil){
dispatch_async(dispatch_get_main_queue(), ^{
[self showAlertController:#"Error" :[jsonResp objectForKey:#"error_description"] passViewController:vc];
});
}
else
successPost(jsonResp);
}
}
}];
[dataTask resume];
}
+ (NSDictionary *)getDataFromToken:(NSString *)strToken {
NSDictionary *data;
NSString *encoded = [strToken componentsSeparatedByString:#"."][1];
data = [GlobalShareClass urlBase64Decode:encoded];
return data;
}
+ (NSDictionary *)urlBase64Decode:(NSString *)strToParse {
#try {
NSDictionary *returnDict;
NSString *output = [strToParse stringByReplacingOccurrencesOfString:#"-" withString:#"+"];
output = [output stringByReplacingOccurrencesOfString:#"_" withString:#"/"];
switch (output.length % 4) {
case 0:
case 1:
break;
case 2:
output = [output stringByAppendingString:#"=="];
break;
case 3:
output = [output stringByAppendingString:#"="];
break;
default:
break;
}
NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:output options:0];
NSString *decodedString = [[NSString alloc] initWithData:decodedData encoding:NSUTF8StringEncoding];
NSLog(#"%#", decodedString);
NSData *data = [decodedString dataUsingEncoding:NSUTF8StringEncoding];
returnDict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
return returnDict;
}
#catch (NSException *exception) {
NSLog(#"%#", [exception description]);
}
}
#end
In Below code run so i get a response from url but when i try to get encodedPoints it give me a null value. also i update RegexKitLite but prob. not solve. Any suggestion are welcome Thank you advance.
NSString* saddr = [NSString stringWithFormat:#"%f,%f", f.latitude, f.longitude];
NSString* daddr = [NSString stringWithFormat:#"%f,%f", t.latitude, t.longitude];
NSString* apiUrlStr = [NSString stringWithFormat:#"http://maps.googleapis.com/maps/api/directions/json?origin=%#&destination=%#&sensor=false", saddr, daddr];
// http://maps.googleapis.com/maps/api/directions/json?origin=41.029598,28.972985&destination=41.033586,28.984546&sensor=false%EF%BB%BF%EF%BB%BF
NSURL *apiUrl = [NSURL URLWithString:[apiUrlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSLog(#"api url: %#", apiUrl);
NSString *apiResponse = [NSString stringWithContentsOfURL:apiUrl encoding:nil error:nil];
NSString* encodedPoints = [apiResponse stringByMatching:#"points:\\\"([^\\\"]*)\\\"" capture:1L];
NSLog(#"encodedPoints: %#", encodedPoints);
if (encodedPoints) {
return [self decodePolyLine:[encodedPoints mutableCopy]];
}
else {
return NO;
}
I think its not a good way to do API request synchronously, especially when user' phone has poor internet connection, it will slow down the responsiveness of your application. So you should do an asynchronous API request with NSURLSession.
Also, the Directions API might return more than one routes for your request. So its better to use a NSArray to store your polyline points.
Sample code:
- (void)getPolyline {
NSURL *url = [[NSURL alloc] initWithString:#"https://maps.googleapis.com/maps/api/directions/json?origin=Chicago,IL&destination=Los+Angeles,CA&key=YOUR_API_KEY"];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: url];
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithRequest:request completionHandler:
^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (!error) {
NSError *jsonError;
NSDictionary *dict = (NSDictionary*)[NSJSONSerialization JSONObjectWithData:data options:nil error:&jsonError];
if (!jsonError) {
NSArray *routesArray = (NSArray*)dict[#"routes"];
NSMutableArray *points = [NSMutableArray array];
for (NSDictionary *route in routesArray) {
NSDictionary *overviewPolyline = route[#"overview_polyline"];
[points addObject:overviewPolyline[#"points"]];
}
NSLog(#"%#", points);
}
} else {
//print error message
NSLog(#"%#", [error localizedDescription]);
}
}] resume];
}
I am trying to retrieve data data from the server. The server should return the basic information of the user when the json request is successful.
My problem is that, when i remove this code to my .plist file:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>fb***</string>
</array>
</dict>
</array>
Theres no problem aside from this warning i get from the logs:
FBSDKLog: Cannot use the Facebook app or Safari to authorize, fb**** is not registered as a URL Scheme
This presents the login view to be popup:
And all the data i need to get fetched properly. but what i want is to have the login in a browser then it goes back to the app.. so i included the code above in my .plist file.
here is my code for fetching the values:
- (void)loginViewFetchedUserInfo:(FBLoginView *)loginView user:(id<FBGraphUser>)user {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([[NSUserDefaults standardUserDefaults] valueForKey:FB_DISPLAY_NAME_TEXT] !=nil) {}else{
NSLog(#"##wew: %#", [[NSUserDefaults standardUserDefaults] valueForKey:FB_DISPLAY_NAME_TEXT]);
[webService getDataFromURL:[NSString stringWithFormat:#"%#%#", ROOT_SERVER_URL, FB_URL_LOGIN]];
}
and this is the web service file that i am using:
- (void) postRequestFromUrl: (NSString *) urlString withDictionary: (NSDictionary *) post{
NSURL *url = [NSURL URLWithString:urlString];
NSError *error;
NSData *postData = [NSJSONSerialization dataWithJSONObject:post options:0 error:&error];
NSString * postLength = [NSString stringWithFormat:#"%d", [postData length]];
NSMutableURLRequest * request = [[NSMutableURLRequest alloc] initWithURL:url];
//url where u will send data
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody: postData];
[request setValue:[NSString stringWithFormat:#"%d", [postLength length]] forHTTPHeaderField:#"Content-Length"];
NSLog(#"##REQUEST: %#", request);
NSURLConnection * conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if(conn) {
NSLog(#"Connection Successful");
} else {
NSLog(#"Connection could not be made");
}
}
This code fetch the data i needed when the login is popup. I need the user to be signed-in first so i put the request there. Is there any problem in my code?
first put openurl code in appdelegate.m file
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
return [FBSession.activeSession handleOpenURL:url];
}
then use below code for facebook login :
NSArray *permissions = [NSArray arrayWithObjects:#"email", #"public_profile", #"user_friends",nil];
[FBSession openActiveSessionWithReadPermissions:permissions
allowLoginUI:TRUE
completionHandler:^(FBSession *session, FBSessionState state, NSError *error) {
if (!error) {
hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.labelText = #"Loading...";
hud.dimBackground = NO;
[[FBRequest requestForMe] startWithCompletionHandler:
^(FBRequestConnection *connection, NSDictionary<FBGraphUser> *user, NSError *error) {
if (!error) {
[FBRequestConnection startWithGraphPath:#"me" parameters:[NSDictionary dictionaryWithObject:#"picture.type(large),id,birthday,email,name,gender,location" forKey:#"fields"] HTTPMethod:#"GET" completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
//username
if (!error) {
NSString *facebookId = user.objectID;
NSString *firstName = user.first_name;
NSString *lastName = user.last_name;
NSString *email = ([user objectForKey:#"email"] == nil) ? #"" : [user objectForKey:#"email"];
// NSString *username = (user.username == nil) ? #"" : user.username;
NSString *gender = [user objectForKey:#"gender"];
NSString *birthday = user.birthday;
NSString *userLocation = ([[user objectForKey:#"location"]objectForKey:#"name"] == nil) ? #"" : [user objectForKey:#"location"];
NSString *year = [birthday substringFromIndex:6];
NSString *monthstr = [birthday substringFromIndex:3];
NSString *month = [monthstr substringToIndex:2];
NSString *date = [birthday substringToIndex:2];
birthday = [NSString stringWithFormat:#"%#-%#-%#",date,month,year];
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[[[result objectForKey:#"picture"] objectForKey:#"data"] valueForKey:#"url"]]]];
NSData *dataImg = UIImageJPEGRepresentation(image, 0.5);
userDict = [[NSMutableDictionary alloc]init];
[userDict setObject:firstName forKey:#"firstName"];
[userDict setObject:lastName forKey:#"lastName"];
[userDict setObject:[email RemoveNull] forKey:#"email"];
[userDict setObject:email forKey:#"username"];
[userDict setObject:dataImg forKey:#"profilephoto"];
[userDict setObject:gender forKey:#"gender"];
[userDict setObject:facebookId forKey:#"facebookId"];
[userDict setObject:userLocation forKey:#"location"];
[self performSelector:#selector(signUpUser:) withObject:userDict afterDelay:0.1];
}
[MBProgressHUD hideAllHUDsForView:self.view animated:YES];
}];
}
}];
}
}];
I have a NSURLConnection (two of them), and they're running in the wrong order.
Here's my method:
- (void)loginToMistarWithPin:(NSString *)pin password:(NSString *)password {
NSURL *url = [NSURL URLWithString:#"https://mistar.oakland.k12.mi.us/novi/StudentPortal/Home/Login"];
//Create and send request
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setHTTPMethod:#"POST"];
NSString *postString = [NSString stringWithFormat:#"Pin=%#&Password=%#",
[self percentEscapeString:pin],
[self percentEscapeString:password]];
NSData * postBody = [postString dataUsingEncoding:NSUTF8StringEncoding];
[request setHTTPBody:postBody];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
// do whatever with the data...and errors
if ([data length] > 0 && error == nil) {
NSError *parseError;
NSDictionary *responseJSON = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
if (responseJSON) {
// the response was JSON and we successfully decoded it
NSLog(#"Response was = %#", responseJSON);
} else {
// the response was not JSON, so let's see what it was so we can diagnose the issue
NSString *loggedInPage = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"Response was not JSON (from login), it was = %#", loggedInPage);
}
}
else {
NSLog(#"error: %#", error);
}
}];
//Now redirect to assignments page
NSURL *homeURL = [NSURL URLWithString:#"https://mistar.oakland.k12.mi.us/novi/StudentPortal/Home/PortalMainPage"];
NSMutableURLRequest *requestHome = [[NSMutableURLRequest alloc] initWithURL:homeURL];
[request setHTTPMethod:#"POST"];
[NSURLConnection sendAsynchronousRequest:requestHome queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *homeResponse, NSData *homeData, NSError *homeError)
{
// do whatever with the data...and errors
if ([homeData length] > 0 && homeError == nil) {
NSError *parseError;
NSDictionary *responseJSON = [NSJSONSerialization JSONObjectWithData:homeData options:0 error:&parseError];
if (responseJSON) {
// the response was JSON and we successfully decoded it
NSLog(#"Response was = %#", responseJSON);
} else {
// the response was not JSON, so let's see what it was so we can diagnose the issue
NSString *homePage = [[NSString alloc] initWithData:homeData encoding:NSUTF8StringEncoding];
NSLog(#"Response was not JSON (from home), it was = %#", homePage);
}
}
else {
NSLog(#"error: %#", homeError);
}
}];
}
- (NSString *)percentEscapeString:(NSString *)string
{
NSString *result = CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
(CFStringRef)string,
(CFStringRef)#" ",
(CFStringRef)#":/?#!$&'()*+,;=",
kCFStringEncodingUTF8));
return [result stringByReplacingOccurrencesOfString:#" " withString:#"+"];
}
So, it's two NSURLConnection's that are added to the [NSOperationQueue mainQueue]. What my output is showing me is that the second NSURLConnection is running before the first one. So it tries to go to the page where I download data before I'm logged in, so it (obviously) returns a "You're not logged in" error.
How do I schedule them one after another?
The issue, as I suspect you have realized, is that you're doing asynchronous network requests (which is good; you don't want to block the main queue), so there's no assurance of the order they'll finish.
The quickest and easiest answer is to simply put the call to the second request inside the completion block of the first one, not after it. You don't want to be making that second one unless the first one succeeded anyway.
To keep your code from getting unwieldy, separate the login from the request for main page. And you can use the completion block pattern which is common with asynchronous methods. You add a parameter to loginToMistarWithPin that specifies what it should do when the request finishes. You might have one completion block handler for success, and one for failure:
- (void)loginToMistarWithPin:(NSString *)pin password:(NSString *)password success:(void (^)(void))successHandler failure:(void (^)(void))failureHandler {
NSURL *url = [NSURL URLWithString:#"https://mistar.oakland.k12.mi.us/novi/StudentPortal/Home/Login"];
//Create and send request
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setHTTPMethod:#"POST"];
NSString *postString = [NSString stringWithFormat:#"Pin=%#&Password=%#",
[self percentEscapeString:pin],
[self percentEscapeString:password]];
NSData * postBody = [postString dataUsingEncoding:NSUTF8StringEncoding];
[request setHTTPBody:postBody];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
// do whatever with the data...and errors
if ([data length] > 0 && error == nil) {
NSError *parseError;
NSDictionary *responseJSON = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
if (responseJSON) {
// the response was JSON and we successfully decoded it
NSLog(#"Response was = %#", responseJSON);
// assuming you validated that everything was successful, call the success block
if (successHandler)
successHandler();
} else {
// the response was not JSON, so let's see what it was so we can diagnose the issue
NSString *loggedInPage = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"Response was not JSON (from login), it was = %#", loggedInPage);
if (failureHandler)
failureHandler();
}
}
else {
NSLog(#"error: %#", error);
if (failureHandler)
failureHandler();
}
}];
}
- (void)requestMainPage {
//Now redirect to assignments page
NSURL *homeURL = [NSURL URLWithString:#"https://mistar.oakland.k12.mi.us/novi/StudentPortal/Home/PortalMainPage"];
NSMutableURLRequest *requestHome = [[NSMutableURLRequest alloc] initWithURL:homeURL];
[requestHome setHTTPMethod:#"GET"]; // this looks like GET request, not POST
[NSURLConnection sendAsynchronousRequest:requestHome queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *homeResponse, NSData *homeData, NSError *homeError)
{
// do whatever with the data...and errors
if ([homeData length] > 0 && homeError == nil) {
NSError *parseError;
NSDictionary *responseJSON = [NSJSONSerialization JSONObjectWithData:homeData options:0 error:&parseError];
if (responseJSON) {
// the response was JSON and we successfully decoded it
NSLog(#"Response was = %#", responseJSON);
} else {
// the response was not JSON, so let's see what it was so we can diagnose the issue
NSString *homePage = [[NSString alloc] initWithData:homeData encoding:NSUTF8StringEncoding];
NSLog(#"Response was not JSON (from home), it was = %#", homePage);
}
}
else {
NSLog(#"error: %#", homeError);
}
}];
}
Then, when you want to login, you can do something like:
[self loginToMistarWithPin:#"1234" password:#"pass" success:^{
[self requestMainPage];
} failure:^{
NSLog(#"login failed");
}];
Now, change those successHandler and failureHandler block parameters to include whatever data you need to pass back, but hopefully it illustrates the idea. Keep your methods short and tight, and use completion block parameters to specify what an asynchronous method should do when it's done.
Can you check the below link. It is about forcing one operation to wait for another.
NSOperation - Forcing an operation to wait others dynamically
Hope this helps.