Confused with logic of login flow in AWS Developer Authenticated Identities - authentication

I believe that I have an incorrect flow for implementing Developer Authenticated Identities and I keep hearing and doing different things on the web. So I thought I'd present my entire flow and hear what the correct way of doing this was and present some questions and errors at the bottom.
Initially, I have a user login with a password and username (I'm using nsuserdefaults only temporarily, I will use KeyChain later).
Note: I also have a callback that goes all the way down to see if I properly authenticated a user.
Login Method:
-(void)loginBusyTimeUser:(void(^)(BOOL))callBack{
//initialize nsuserdefualts should be keychain later
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *post = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
[defaults objectForKey:#"username"], #"username",
[defaults objectForKey:#"password"], #"password",
nil];
NSError *error;
NSData *postData = [NSJSONSerialization dataWithJSONObject:post options:0 error:&error];
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[postData length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:#"SOMELOGINURL"]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:postData];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
[[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *newJSON = [NSJSONSerialization JSONObjectWithData:data
options:0
error:&error];
if(!newJSON || [newJSON objectForKey:#"errorMessage"]){
NSLog(#"%#",newJSON);
callBack(false);
NSLog(#"DID NOT AUTHENTICATE");
}else{
NSLog(#"%#",newJSON);
[defaults setValue:[newJSON objectForKey:#"Token"] forKey:#"Token"];
[defaults setValue:[newJSON objectForKey:#"IdentityId"] forKey:#"IdentityId"];
[self authenticateUser:^(BOOL call){
callBack(call);
}];
}
}] resume];
}
If Everything is successful, I then authenticate my user with AWS Cognito with the developer authenticated flow:
-(void)authenticateUser:(void(^)(BOOL))callBack{
//Now after making sure that your user's credentials are sound, then initialize the IdentityProvider, in this case
//BusytimeAuthenticated
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
id<AWSCognitoIdentityProvider> identityProvider = [[BusytimeAuthenticated alloc] initWithRegionType:AWSRegionUSEast1
identityId:nil
identityPoolId:#"SOMEPOOLID"
logins:#{#"cognito-identity.amazonaws.com": [defaults objectForKey:#"Token"]}
providerName:#"cognito-identity.amazonaws.com"
];
credentialsProvider = [[AWSCognitoCredentialsProvider alloc] initWithRegionType:AWSRegionUSEast1
identityProvider:identityProvider
unauthRoleArn:nil
authRoleArn:nil];
configuration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSEast1
credentialsProvider:self.credentialsProvider];
AWSServiceManager.defaultServiceManager.defaultServiceConfiguration = configuration;
[[credentialsProvider refresh] continueWithBlock:^id(AWSTask *task){
if([task isFaulted]){
callBack(false);
}else{
callBack(true);
}
return [defaults objectForKey:#"Token"];
}];
}
Then the refresh method causes some errors so I'll show my "BusytimeAuthenticated" class (.m)
//
// BusytimeAuthenticated.m
// BusyTime
//
// Created by akash kakumani on 10/14/15.
// Copyright (c) 2015 BusyTime. All rights reserved.
//
#import "BusytimeAuthenticated.h"
#interface BusytimeAuthenticated()
#property (strong, atomic) NSString *providerName;
#property (strong, atomic) NSString *token;
#end
#implementation BusytimeAuthenticated
#synthesize providerName=_providerName;
#synthesize token=_token;
- (instancetype)initWithRegionType:(AWSRegionType)regionType
identityId:(NSString *)identityId
identityPoolId:(NSString *)identityPoolId
logins:(NSDictionary *)logins
providerName:(NSString *)providerName{
if (self = [super initWithRegionType:regionType identityId:identityId accountId:nil identityPoolId:identityPoolId logins:logins]) {
self.providerName = providerName;
}
return self;
}
// Return the developer provider name which you choose while setting up the
// identity pool in the Amazon Cognito Console
- (BOOL)authenticatedWithProvider {
return [self.logins objectForKey:self.providerName] != nil;
}
// If the app has a valid identityId return it, otherwise get a valid
// identityId from your backend.
- (AWSTask *)getIdentityId {
// already cached the identity id, return it
if (self.identityId) {
return [AWSTask taskWithResult:nil];
}
// not authenticated with our developer provider
else if (![self authenticatedWithProvider]) {
return [super getIdentityId];
}
// authenticated with our developer provider, use refresh logic to get id/token pair
else {
return [[AWSTask taskWithResult:nil] continueWithBlock:^id(AWSTask *task) {
if (!self.identityId) {
return [self refresh];
}
return [AWSTask taskWithResult:self.identityId];
}];
}
}
// Use the refresh method to communicate with your backend to get an
// identityId and token.
- (AWSTask *)refresh {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if (![self authenticatedWithProvider]) {
return [super getIdentityId];
}else{
NSDictionary *post = [[NSDictionary alloc] initWithObjectsAndKeys:
[defaults objectForKey:#"username"], #"username",
[defaults objectForKey:#"password"], #"password",
nil];
NSError *error;
NSData *postData = [NSJSONSerialization dataWithJSONObject:post options:0 error:&error];
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[postData length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:#"SOMELOGINURL"]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:postData];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
[[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *newJSON = [NSJSONSerialization JSONObjectWithData:data
options:0
error:&error];
if(!newJSON){
NSLog(#"Failure in refresh: %#",newJSON);
}else{
NSLog(#"The IdentityID in the refresh method: %#",[newJSON objectForKey:#"IdentityId" ]);
NSLog(#"The token in the refresh method: %#",[newJSON objectForKey:#"Token" ]);
self.identityId = [newJSON objectForKey:#"IdentityId" ];
self.token = [newJSON objectForKey:#"Token" ];
}
}] resume];
return [AWSTask taskWithResult:self.identityId];
}
return [AWSTask taskWithResult:self.identityId];
}
#end
Some Questions I had:
Is the DeveloperAuthenticationClient necessary to solve my problems? I saw the sample app using them but I found them too confusing.
Should I be using my ProviderName or should I be using "cognito-identity.amazonaws.com"
I sometimes face a timeout error and found out that it could be the fact that my implementation of login (using API Gateway and a lambda method) could have some cold-start issues. The way I solved this is by increasing the timeout time to 20 seconds. Is this the correct way to solve this?
I saw that in the sample app they use GetToken and Login as two separate things. I thought it would be easier if my login could also serve as my GetToken. Is this appropriate?
Finally, please address any problems that you see with my code if time permits.
Error:
[Error] AWSCredentialsProvider.m line:527 |
__40-[AWSCognitoCredentialsProvider refresh]_block_invoke352 | Unable to refresh. Error is [Error
Domain=com.amazonaws.AWSCognitoCredentialsProviderErrorDomain Code=1
"identityId shouldn't be nil"
UserInfo={NSLocalizedDescription=identityId shouldn't be nil}]
(I also found out the error above is related to the fact that self.identityId didn't get set because the request was in a block and other parts executed first and the solution is:
- (AWSTask *)refresh {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if (![self authenticatedWithProvider]) {
return [super getIdentityId];
}else{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *string = [defaults objectForKey:#"IdentityId"];
self.identityId = string;
return [AWSTask taskWithResult:[defaults objectForKey:#"IdentityId"]];
}
NSString *string = [defaults objectForKey:#"IdentityId"];
return [AWSTask taskWithResult:[defaults objectForKey:#"IdentityId"]];
}
But I believe that this isn't the correct implementation. )
I believe my code was working at a certain point but stopped working after I upgraded to the new SDK. However, it might just be the fact that I hadn't noticed the error initially.

Answers to your questions:
Yes you need to have some entity(client) which communicates with your backend system.
You are using cognito-identity.amazonaws.com in the logins map, but using the IdentityProvider pattern for refresh. This is why the first authentication succeeds but attempts to refresh fail. The logic in your refresh will never fire. Please look at our end to end sample on how to implement developer authenticated identities.
From my limited knowledge yes this is one way, but you may face performance issues. Please contact AWS Lambda via their forums for more guidance on this.
We highly recommend following the flow in the sample. The getToken doesn't need your authentication credentials if you have established trust, whereas login will always need the authentication credentials, so better not to mix these.
Thanks..

Related

Why is my AFNetworking API call result in an error?

I come from a Swift background; therefore, am used to Alamofire. I am trying to learn Objective-C and have been plying with AFNetworking.
The following is a simple method that calls the Yelp search API to find business in the vicinity.
- (void) getBusinesses: (NSString *) usingToken{
NSString *starbucks = #"Starbucks";
NSString *latitude = #""; //enter latitude
NSString *longitude = #""; //enter longitude
NSString *URLString = [NSString stringWithFormat:#"https://api.yelp.com/v3/businesses/search?term=%#&latitude=%#&longitude=%#", starbucks, latitude, longitude];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
AFHTTPRequestSerializer *request = [[AFHTTPRequestSerializer alloc] requestWithMethod:#"GET" URLString:URLString parameters:nil error:nil];
NSString *bearerToken = [NSString stringWithFormat: #"Bearer %#", usingToken];
[request setValue: bearerToken forHTTPHeaderField:#"Authorization"];
NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error){
if (error){
NSLog(#"*** Failed!");
} else {
NSLog(#"*** Succeeded!");
}
}];
dataTask.resume;
}
When running this code I get the following error:
Incorrect NSStringEncoding value 0x0000 detected. Assuming NSASCIIStringEncoding. Will stop this compatibility mapping behavior in the near future.
I have tested the above Yelp URL with my valid access token on Postman and it works perfectly.
If someone could point me in the right direction to figure out what is going on I'd appreciate it!
P.S. this is the documentation for the Yelp search endpoint

Activity indicator with SBJson framework

I'm currently working on an app that uses a basic login page to check if the user has access to the app. I've made the login part with help of this tutorial which uses this frame work to use username and password from a simple web script.
I'm hoping that someone else maybe have worked with it or can help me with my issue. I wan't to show an activity indicator, I'm using MBProgressHUD as an activity indicator.
So I've experimented with it but can't get it to show the activity indicator when the app is connecting to the URL. I've done some bad connection simulating with the login process but the activity indicator won't show up when the app is connecting to the URL. It only shows on errors and the only thing that shows any kind of loading on success is that the login buttons pressed state is "active" (blue highlighted) until the loading is done.
So here's my code that runs when the user has typed in username and password and clicks on the login button:
// Login button
- (IBAction)loginBtnClicked:(id)sender
{
// Show the activity indicator
[HUD showUIBlockingIndicatorWithText:#"Loggar in..."];
#try {
if([[userNameTxtField text] isEqualToString:#""] || [[passwordTxtField text] isEqualToString:#""] ) {
// No username or password entered
[self alertStatus:#"Du måste ange användarnamn och lösenord" :#"Något gick fel!"];
// Hide activity indicator
[HUD hideUIBlockingIndicator];
} else {
NSString *post =[[NSString alloc] initWithFormat:#"username=%#&password=%#",[userNameTxtField text],[passwordTxtField text]];
NSLog(#"PostData: %#",post);
NSURL *url=[NSURL URLWithString:#"http://www.nebulon.se/json/sendus/jsonlogin.php"];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
//[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[url host]];
NSError *error = [[NSError alloc] init];
NSHTTPURLResponse *response = nil;
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSLog(#"Response code: %d", [response statusCode]);
if ([response statusCode] >=200 && [response statusCode] <300){
NSString *responseData = [[NSString alloc]initWithData:urlData encoding:NSUTF8StringEncoding];
NSLog(#"Response ==> %#", responseData);
SBJsonParser *jsonParser = [SBJsonParser new];
NSDictionary *jsonData = (NSDictionary *) [jsonParser objectWithString:responseData error:nil];
NSLog(#"%#",jsonData);
NSInteger success = [(NSNumber *) [jsonData objectForKey:#"success"] integerValue];
NSLog(#"%d",success);
if(success == 1){
// Login success, grant user access to app
NSLog(#"Login SUCCESS");
[self loginSuccess];
// Hide activity indicator
[HUD hideUIBlockingIndicator];
// Store username
NSString *userName = [userNameTxtField text];
NSUserDefaults *UserDefaults = [NSUserDefaults standardUserDefaults];
[UserDefaults setObject:userName forKey:#"userName"];
[UserDefaults synchronize];
[self dismissViewControllerAnimated:NO completion:nil];
} else {
// Login error
NSString *error_msg = (NSString *) [jsonData objectForKey:#"error_message"];
[self alertStatus:error_msg :#"Inloggningen misslyckades"];
[self loginFailed];
// Hide activity indicator
[HUD hideUIBlockingIndicator];
}
} else {
// Login error
if (error) NSLog(#"Error: %#", error);
[self alertStatus:#"Ingen nätverksuppkoppling hittades." :#"Ett fel har inträffat!"];
[self loginFailed];
// Hide activity indicator
[HUD hideUIBlockingIndicator];
}
}
}
#catch (NSException * e) {
// Login error
NSLog(#"Exception: %#", e);
[self alertStatus:#"Inloggningen misslyckades." :#"Ett fel har inträffat!"];
[self loginFailed];
// Hide activity indicator
[HUD hideUIBlockingIndicator];
}
}
I believe the issue is due to the use of sendSynchronousRequest:returningResponse:error:
This method will be blocking the main/UI thread so the HUD never actually gets a chance to show until the method has returned, at which point the code continues and the HUD is hidden.
I think you should be looking at using an asynchronous request. and implementing the NSURLConnection delegate methods.
EDIT: Added code sample.
Assuming you're targeting iOS 5 and higher you can use the following code snippet which takes advantage of blocks with sendAsynchronousRequest:queue:completionHandler: and GCD.
NSOperationQueue *backgroundQueue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:request
queue:backgroundQueue
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
// NOTE: This block is called on the background queue.
// Use GCD to get back onto the main thread
dispatch_async(dispatch_get_main_queue(), ^{
// This block will process the response and data on the main thread
});
}];
It really is very little work to port your existing code to use this mechanism. If you don't know how blocks work you should read the documentation as they are a very powerful language feature and are being used in an increasing amount of Apple and third-party frameworks.
I would also recommend staying AWAY from third-party networking libraries for now until you understand the nuances that can cause issues such as this.

Use a NSURLconnection for multiple posts to API

I having a situation here where I have some templates (objects), which I want to push to my backend. Now When the users presses the synchronise button, there is a connection to the backend form which I get all the templates. The templates on the IPAD app are compared to those that were transferred from the backend. If a template on the IPAD app has the ID(0), then there is a post to the backend. the backend then returns the saved template with his ID (to add to the local stored template).
Now I think my problem here is that I'm using the same connection (templateupdateconnection => bad name...) for all of those posts to the backend. The actual problem is that in the connectiondidfinishloading method, I only get a response on the last template that was posted to the backend.
Anyone who knows how I can solve this?
Thanks in advance!!
THE CODE
-(void)syncRegistrations:(NSArray *)arrayOfRegistrations{
NSFetchRequest *request = [[NSFetchRequest alloc]init];
[request setEntity:[NSEntityDescription entityForName:#"Registration" inManagedObjectContext:self.managedObjectContext]];
NSError *error;
NSArray *Data = [self.managedObjectContext executeFetchRequest:request error:&error];
BOOL old=FALSE;
for(int registrationCounter = 0; registrationCounter < arrayOfRegistrations.count; registrationCounter ++){
NSDictionary *dictRegistration = [arrayOfRegistrations objectAtIndex:registrationCounter];
for(Registration *registration in Data){
if([dictRegistration objectForKey:#"id"] == registration.id){
old = TRUE;
}
else if ([registration.id intValue]==0){
NSString *jsonRequest = [NSString stringWithFormat:#"{\"form\":%#}",registration.form.id];
NSLog(#" de jsonrequest: %#",jsonRequest);
NSURL *url = [NSURL URLWithString:#"http://mybackend/registrations"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
NSData *requestData = [NSData dataWithBytes:[jsonRequest UTF8String] length:[jsonRequest length]];
[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"];
[request setHTTPBody: requestData];
self.registrationtoupdate = registration;
self.registrationUpdateConnection = NULL;
self.registrationUpdateConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[self.registrationUpdateConnection start];
registration.id=[NSNumber numberWithInteger:-1];
[self.managedObjectContext save:&error];
old=TRUE;
}
}
if(old==FALSE){
//nieuwe template toevoegen
Registration *registration = [NSEntityDescription insertNewObjectForEntityForName:#"Registration" inManagedObjectContext:self.managedObjectContext];
registration.id = [dictRegistration objectForKey:#"id"];
registration.form = [self getFormByID:[dictRegistration objectForKey:#"form"]];
[self.managedObjectContext save:&error];
}
old=FALSE;
}
//[self getRC];
[self performSelector:#selector(getRC) withObject:nil afterDelay:3];
}
CONNECTIONDIDFINISHLOADING METHOD
else if([connection isEqual:self.registrationUpdateConnection]){
NSDictionary *dictRegistration = [NSJSONSerialization JSONObjectWithData:self.registrationdata options:kNilOptions error:&error];
NSLog(#"de data van de registratie is: %#",dictRegistration);
NSLog(#"de registration to update is: %#",self.registrationtoupdate);
self.registrationtoupdate.id = [dictRegistration objectForKey:#"id"];
[self.managedObjectContext save:&error];
}
You should encapsulate the NSURLConnection and all relevant state data into a class. Let that class have a start method which asynchronously starts the connection and a completion block which has a parameter result representing the eventual result of the request. That way, you don't mismatch response data with other connections.
Then, in the completion block, reference the corresponding context (registration to update), get the JSON representation and update the context on the correct thread or queue appropriately for the given managed object context (see below "Thread Confinement"):
So, basically:
else if ([registration.id intValue]==0) {
// setup the request
NSMutableURLRequest *request = ...
MyHTTPRequestOperation* op =
[[MyHTTPRequestOperation alloc] initWithRequest:request
completion:^(void)(id result)
{
if (![result isKindOfClass:[NSError class]]) {
assert(result != nil && [result isKindOfClass:[NSData class]]);
NSDictionary* dictRegistration =
[NSJSONSerializationJSONObjectWithData:result
options:kNilOptions
error:&error];
// Todo: check if dictRegistration has the required type
id newRegistrationID = [dictRegistration objectForKey:#"id"];
// Update the model on the thread/queue you defined for the
// NSManagedObjectContext:
[self updateRegistration:registration withID: newRegistrationID];
}
else {
// an error occurred
}
}];
[op start];
registration.id=[NSNumber numberWithInteger:-1];
}
You should be familiar with NSModelObjectContext and "Thread Confinement".
see also: -(id)initWithConcurrencyType:(NSManagedObjectContextConcurrencyType)ct
Core Data Release Notes for OS X v10.7 and iOS 5.0
Core Data Best Practices at WWDC/2012 (requires dev account)
#CouchDeveloper, thanks for putting me on the right track!!
The final solution, wasn't to create a subclass of the NSURLConnection, but just using the completion block in combination with a simple NSURLConnection. check the following code:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if ([data length] > 0 && error == nil){
NSDictionary *dictrc = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
NSLog(#"de data van de rc is: %#",dictrc);
rc.id = [dictrc objectForKey:#"id"];
[self.managedObjectContext save:&error];
}
}];

Objective-C Twitter API 150 Requests

I am trying to fetch tweets from twitter using an NSURLConnection, and I keep hitting the maximum number of requests per hour (150). I switched to using the authenticated way of fetching tweets, like below, but still hit the maximum API calls - 150. How am I able to make more than 150 twitter requests per device?
ACAccountStore *store = [[ACAccountStore alloc] init];
ACAccountType *twitterAccountType =
[store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
// Request permission from the user to access the available Twitter accounts
[store requestAccessToAccountsWithType:twitterAccountType
withCompletionHandler:^(BOOL granted, NSError *error) {
if (!granted) {
// The user rejected your request
NSLog(#"User rejected access to the account.");
}
else {
// Grab the available accounts
NSArray *twitterAccounts =
[store accountsWithAccountType:twitterAccountType];
if ([twitterAccounts count] > 0) {
// Use the first account for simplicity
ACAccount *account = [twitterAccounts objectAtIndex:0];
// Now make an authenticated request to our endpoint
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
[params setObject:#"1" forKey:#"include_entities"];
// The endpoint that we wish to call
NSURL *url =
[NSURL
URLWithString:#"https://api.twitter.com/1/statuses/user_timeline.json?include_entities=true&include_rts=true&screen_name=%#&count=5",someTwitterUserName];
// Build the request with our parameter
TWRequest *request =
[[TWRequest alloc] initWithURL:url
parameters:params
requestMethod:TWRequestMethodGET];
// Attach the account object to this request
[request setAccount:account];
[request performRequestWithHandler:
^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if (!responseData) {
// inspect the contents of error
NSLog(#"%#", error);
}
else {
NSError *jsonError;
NSArray *timeline =
[NSJSONSerialization
JSONObjectWithData:responseData
options:NSJSONReadingMutableLeaves
error:&jsonError];
if (timeline) {
// at this point, we have an object that we can parse and grab tweet information from
NSLog(#"%#", timeline);
}
else {
// inspect the contents of jsonError
NSLog(#"%#", jsonError);
}
}
}];
} // if ([twitterAccounts count] > 0)
} // if (granted)
}];
Try see at this post
solution
Problem in your twitter app, who have a limit at default

Request timeout with Facebook API on iOS

I have a bunch of URL requests that are sent out in batch and then a 'catcher' class that recieves the results. I'm doing this through facebook's graph API SDK so I don't have control of the actual NSURL. My problem is that I don't trigger the next result protocol until all requests have come back, but sometimes, especially over 3G, they don't always come back (especially when you have a few hundred). What's a good way to 'give up' on a given request? The way the actual request works, for those who aren't familiar with the FB api, is you initialize a facebook object and then request data from it, and you specify a return delegate. So, for each individual request I have a returnDelegate (the catcher), which I can initialize specific to each request.
UPDATE: Some code from FB-API
- (FBRequest*)requestWithMethodName:(NSString *)methodName
andParams:(NSMutableDictionary *)params
andHttpMethod:(NSString *)httpMethod
andDelegate:(id <FBRequestDelegate>)delegate {
NSString * fullURL = [kRestserverBaseURL stringByAppendingString:methodName];
return [self openUrl:fullURL
params:params
httpMethod:httpMethod
delegate:delegate];
}
- (FBRequest*)openUrl:(NSString *)url
params:(NSMutableDictionary *)params
httpMethod:(NSString *)httpMethod
delegate:(id<FBRequestDelegate>)delegate {
[params setValue:#"json" forKey:#"format"];
[params setValue:kSDK forKey:#"sdk"];
[params setValue:kSDKVersion forKey:#"sdk_version"];
if ([self isSessionValid]) {
[params setValue:self.accessToken forKey:#"access_token"];
}
[self extendAccessTokenIfNeeded];
FBRequest* _request = [FBRequest getRequestWithParams:params
httpMethod:httpMethod
delegate:delegate
requestURL:url];
[_requests addObject:_request];
[_request addObserver:self forKeyPath:requestFinishedKeyPath options:0 context:finishedContext];
[_request connect];
return _request;
}
- (void)connect {
if ([_delegate respondsToSelector:#selector(requestLoading:)]) {
[_delegate requestLoading:self];
}
NSString* url = [[self class] serializeURL:_url params:_params httpMethod:_httpMethod];
NSMutableURLRequest* request =
[NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:kTimeoutInterval];
[request setValue:kUserAgent forHTTPHeaderField:#"User-Agent"];
[request setHTTPMethod:self.httpMethod];
if ([self.httpMethod isEqualToString: #"POST"]) {
NSString* contentType = [NSString
stringWithFormat:#"multipart/form-data; boundary=%#", kStringBoundary];
[request setValue:contentType forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:[self generatePostBody]];
}
_connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
self.state = kFBRequestStateLoading;
self.sessionDidExpire = NO;
}
As discussed above, you can implement your own timeout by instantiating an NSTimer which will signal a delegate object when a certain amount of time elapses.