Trouble with OAuth in Temboo - ios

I am trying to authorize Twitter For a user in iOS and Temboo. Here is how I am attempting to do it.
In my SetttingsViewController class I have a button where the user can tap to begin the TwitterAuthentication. I have the InitializeOauth methods in a class called TwitterClient, and the FinalizeOauth methods in a class called TwitterFClient. Here are my classes.
TwitterClient.m
-(void)runInitializeOAuthChoreo {
// Instantiate the Choreo, using a previously instantiated TembooSession object, eg:
TMBTembooSession *session = [[TMBTembooSession alloc] initWithAccount:#"prnk28" appKeyName:#"Floadt" andAppKeyValue:#"9b9031d182d7441da05f8214ba2c7170"];
// Create the choreo object using your Temboo session
TMBTwitter_OAuth_InitializeOAuth *initializeOAuthChoreo = [[TMBTwitter_OAuth_InitializeOAuth alloc] initWithSession:session];
// Get Inputs object for the choreo
TMBTwitter_OAuth_InitializeOAuth_Inputs *initializeOAuthInputs = [initializeOAuthChoreo newInputSet];
// Set credential to use for execution
[initializeOAuthInputs setCredential:#"Twitter"];
// Set inputs
[initializeOAuthInputs setForwardingURL:#"floadt://success"];
// Execute choreo specifying this class as the choreo delegate
[initializeOAuthChoreo executeWithInputs:initializeOAuthInputs delegate:self];
}
// TMBChoreographyDelegate method implementation - handle choreo errors
-(void)choreographyDidFailWithError:(NSError*)error {
// Log error to the console
NSLog(#"Error - %#", error);
}
// TMBChoreographyDelegate method implementation - choreo executed successfully
-(void)choreographyDidFinishExecuting:(TMBTwitter_OAuth_InitializeOAuth_ResultSet*)result {
// Log results to the console
// NSLog(#"%#", [result getAuthorizationURL]);
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:[result getAuthorizationURL]]];
NSUserDefaults *user = [NSUserDefaults standardUserDefaults];
NSString *callbackid = [result getCallbackID];
[user setObject:callbackid forKey:#"TwitterCallbackID"];
[user synchronize];
NSString *oauthtokensecret = [result getOAuthTokenSecret];
[user setObject:oauthtokensecret forKey:#"TwitterTemporaryOAuth"];
[user synchronize];
}
TwitterFClient.m
-(void)runFinalizeOAuthChoreo {
// Instantiate the Choreo, using a previously instantiated TembooSession object, eg:
TMBTembooSession *session = [[TMBTembooSession alloc] initWithAccount:#"prnk28" appKeyName:#"Floadt" andAppKeyValue:#"9b9031d182d7441da05f8214ba2c7170"];
// Create the choreo object using your Temboo session
TMBTwitter_OAuth_FinalizeOAuth *finalizeOAuthChoreo = [[TMBTwitter_OAuth_FinalizeOAuth alloc] initWithSession:session];
// Get Inputs object for the choreo
TMBTwitter_OAuth_FinalizeOAuth_Inputs *finalizeOAuthInputs = [finalizeOAuthChoreo newInputSet];
// Set credential to use for execution
[finalizeOAuthInputs setCredential:#"Twitter"];
// Set inputs
NSUserDefaults *user = [NSUserDefaults standardUserDefaults];
NSString *tCall = [user stringForKey:#"TwitterCallbackID"];
NSString *tTAuth = [user stringForKey:#"TwitterTemporaryOAuth"];
[finalizeOAuthInputs setOAuthTokenSecret:tCall];
[finalizeOAuthInputs setCallbackID:tTAuth];
[finalizeOAuthInputs setConsumerKey:TWITTER_CONSUMER_KEY];
[finalizeOAuthInputs setConsumerSecret:TWITTER_CONSUMER_SECRET];
// Execute choreo specifying this class as the choreo delegate
[finalizeOAuthChoreo executeWithInputs:finalizeOAuthInputs delegate:self];
}
// TMBChoreographyDelegate method implementation - handle choreo errors
-(void)choreographyDidFailWithError:(NSError*)error {
// Log error to the console
NSLog(#"Error - %#", error);
}
// TMBChoreographyDelegate method implementation - choreo executed successfully
-(void)choreographyDidFinishExecuting:(TMBTwitter_OAuth_FinalizeOAuth_ResultSet*)result {
// Log results to the console
NSLog(#"%#", [result getAccessTokenSecret]);
NSLog(#"%#", [result getAccessToken]);
NSLog(#"%#", [result getScreenName]);
NSLog(#"%#", [result getUserID]);
}
Those are the two client classes. I believe that in the Facebook example they are both combined would that be the suggested practice?
Anyway this is how the methods are called from within the SettingsViewController Class:
SettingsViewController.m
TwitterClient *twitter = [[TwitterClient alloc] init];
[twitter runInitializeOAuthChoreo];
When the user returns from the Web Browser is when I call the FinalizeOAuth Method. Here is how it is stated in the AppDelegate.
AppDelegate.m
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation
{
NSString *daURL = [url absoluteString];
// NSString *instagram;
NSString *twitter;
twitter = [daURL substringWithRange: NSMakeRange (0, 16)];
// instagram = [daURL substringWithRange:NSMakeRange(0, 27)];
if ([daURL isEqual: #"floadt://success"]) {
TwitterFClient *fo = [[TwitterFClient alloc] init];
[fo runFinalizeOAuthChoreo];
}else{
[[InstagramClient sharedClient] handleOAuthCallbackWithURL:url];
}
return YES;
}
Im under the assumption that there is a much simpler way to do this and I would be open to drastic code change!

It looks like you have these values reversed:
[finalizeOAuthInputs setOAuthTokenSecret:tCall];
[finalizeOAuthInputs setCallbackID:tTAuth];
Also, in the finalize step you're specifying your Twitter credential, but then you're also doing this:
[finalizeOAuthInputs setConsumerKey:TWITTER_CONSUMER_KEY];
[finalizeOAuthInputs setConsumerSecret:TWITTER_CONSUMER_SECRET];
That's unnecessary, since those values are stored in your credential (as evidenced by your use of it in the initialize-oauth flow). You can remove those two lines altogether. If they for some reason didn't match, your choreo execution would fail.

Related

Error 403 on Youtube Video Upload IOS

In my project wanna have a flow like this:
Users record short videos -> they upload the videos on my channel -> end
To achive this result i'm trying to work with the new Google APIs Client Library for Objective-C for REST. It has a poor documentation and the examples are for mac only. Anyway after many errors this is my code:
- (void)doAuthWithoutCodeExchange:(OIDServiceConfiguration *)configuration
clientID:(NSString *)clientID
clientSecret:(NSString *)clientSecret {
NSURL *redirectURI = [NSURL URLWithString:kRedirectURI];
// builds authentication request
OIDAuthorizationRequest *request =
[[OIDAuthorizationRequest alloc] initWithConfiguration:configuration
clientId:clientID
clientSecret:clientSecret
scopes:#[ OIDScopeOpenID, OIDScopeProfile ]
redirectURL:redirectURI
responseType:OIDResponseTypeCode
additionalParameters:nil];
// performs authentication request
AppDelegate *appDelegate = (AppDelegate *) [UIApplication sharedApplication].delegate;
[self logMessage:#"Initiating authorization request %#", request];
appDelegate.currentAuthorizationFlow =
[OIDAuthorizationService presentAuthorizationRequest:request
presentingViewController:self
callback:^(OIDAuthorizationResponse *_Nullable authorizationResponse,
NSError *_Nullable error) {
if (authorizationResponse) {
OIDAuthState *authState =
[[OIDAuthState alloc] initWithAuthorizationResponse:authorizationResponse];
[self setAuthState:authState];
[self logMessage:#"Authorization response with code: %#",
authorizationResponse.authorizationCode];
// could just call [self tokenExchange:nil] directly, but will let the user initiate it.
OIDTokenRequest *tokenExchangeRequest =
[_authState.lastAuthorizationResponse tokenExchangeRequest];
[self logMessage:#"Performing authorization code exchange with request [%#]",
tokenExchangeRequest];
[OIDAuthorizationService performTokenRequest:tokenExchangeRequest
callback:^(OIDTokenResponse *_Nullable tokenResponse,
NSError *_Nullable error) {
if (!tokenResponse) {
[self logMessage:#"Token exchange error: %#", [error localizedDescription]];
} else {
[self logMessage:#"Received token response with accessToken: %#", tokenResponse.accessToken];
}
[_authState updateWithTokenResponse:tokenResponse error:error];
GTMAppAuthFetcherAuthorization *gtmAuthorization =
[[GTMAppAuthFetcherAuthorization alloc] initWithAuthState:authState];
// Sets the authorizer on the GTLRYouTubeService object so API calls will be authenticated.
self.youTubeService.authorizer = gtmAuthorization;
// Serializes authorization to keychain in GTMAppAuth format.
[GTMAppAuthFetcherAuthorization saveAuthorization:gtmAuthorization
toKeychainForName:kGTMAppAuthKeychainItemName];
[self uploadVideoFile];
}];
} else {
[self logMessage:#"Authorization error: %#", [error localizedDescription]];
}
}];
}
This method cause this flow:
app send user to google login page in safari -> user log with his credentials -> after login, user is redirect back to my app -> the block success call the method UploadVideo.
This part of the flow seems to work correctly, i obtain a valid token as the log says. The second part is the video upload that consist in two main methods:
- (void)uploadVideoFile {
// Collect the metadata for the upload from the user interface.
// Status.
GTLRYouTube_VideoStatus *status = [GTLRYouTube_VideoStatus object];
status.privacyStatus = #"public";
// Snippet.
GTLRYouTube_VideoSnippet *snippet = [GTLRYouTube_VideoSnippet object];
snippet.title = #"title";
NSString *desc = #"description";
if (desc.length > 0) {
snippet.descriptionProperty = desc;
}
NSString *tagsStr = #"tags";
if (tagsStr.length > 0) {
snippet.tags = [tagsStr componentsSeparatedByString:#","];
}
GTLRYouTube_Video *video = [GTLRYouTube_Video object];
video.status = status;
video.snippet = snippet;
[self uploadVideoWithVideoObject:video
resumeUploadLocationURL:nil];
}
- (void)uploadVideoWithVideoObject:(GTLRYouTube_Video *)video
resumeUploadLocationURL:(NSURL *)locationURL {
NSURL *fileToUploadURL = [NSURL fileURLWithPath:self.VideoUrlCri.path];
NSError *fileError;
NSLog(#"step");
if (![fileToUploadURL checkPromisedItemIsReachableAndReturnError:&fileError]) {
NSLog(#"exit");
return;
}
// Get a file handle for the upload data.
NSString *filename = [fileToUploadURL lastPathComponent];
NSString *mimeType = [self MIMETypeForFilename:filename
defaultMIMEType:#"video/mp4"];
GTLRUploadParameters *uploadParameters =
[GTLRUploadParameters uploadParametersWithFileURL:fileToUploadURL
MIMEType:mimeType];
uploadParameters.uploadLocationURL = locationURL;
GTLRYouTubeQuery_VideosInsert *query =
[GTLRYouTubeQuery_VideosInsert queryWithObject:video
part:#"snippet,status"
uploadParameters:uploadParameters];
query.executionParameters.uploadProgressBlock = ^(GTLRServiceTicket *ticket,
unsigned long long numberOfBytesRead,
unsigned long long dataLength) {
NSLog(#"upload progress");
};
GTLRYouTubeService *service = self.youTubeService;
_uploadFileTicket = [service executeQuery:query
completionHandler:^(GTLRServiceTicket *callbackTicket,
GTLRYouTube_Video *uploadedVideo,
NSError *callbackError) {
if (callbackError == nil) {
NSLog(#"uploaded");
} else {
NSLog(#"error %#",callbackError);
}
}];
}
- (NSString *)MIMETypeForFilename:(NSString *)filename
defaultMIMEType:(NSString *)defaultType {
NSString *result = defaultType;
NSString *extension = [filename pathExtension];
CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension,
(__bridge CFStringRef)extension, NULL);
if (uti) {
CFStringRef cfMIMEType = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType);
if (cfMIMEType) {
result = CFBridgingRelease(cfMIMEType);
}
CFRelease(uti);
}
return result;
}
I obtain a 403 error in NSLog(#"error %#",callbackError); and i can't see error details because the are something like :
data=<7b226572 726f7222 3a7b2265 72726f72 73223a5b 7b22646f 6d61696e 223a2267 6c6f6261 6c222c22 72656173 6f6e223a 22696e73 75666669 ... 61676522 3a22496e 73756666 69636965 6e742050 65726d69 7373696f 6e227d7d>}
In google api console i have created a Client Oauth for my application bundle and an API key, i use those values for my connection, it seems they works correctly because i obtain a valid token. Anyway, there someone who can help me or point me in the right direction about this error? Or there someone who knows a working example about a video upload for IOS and not for MAC ? There something weird in my code? I can't find any help in documentation or google

iOS Authenticate Azure Active Directory & get calendar events from office 365 exchange

Trying to Authenticate with Azure Active Directory and fetch mail, calendar data, accessToken is returned successfully:
authority = #"https://login.windows.net/common/oauth2/authorize";
redirectUriString = #"http://xxxxxx.xxxxxxx.com/oauth";
resourceId = #"https://outlook.office365.com";
clientId = #"xxxxxxx-xxxxx-xxx";
-(void) getToken : (BOOL) clearCache completionHandler:(void (^) (NSString*))completionBlock;
{
ADAuthenticationError *error;
authContext = [ADAuthenticationContext authenticationContextWithAuthority:authority
error:&error];
[authContext setValidateAuthority:YES];
NSURL *redirectUri = [NSURL URLWithString:redirectUriString];
if(clearCache){
[authContext.tokenCacheStore removeAllWithError:&error];
if (error) {
NSLog(#"Error: %#", error);
}
}
[authContext acquireTokenWithResource:resourceId
clientId:clientId
redirectUri:redirectUri
completionBlock:^(ADAuthenticationResult *result) {
if (AD_SUCCEEDED != result.status){
// display error on the screen
[self showError:result.error.errorDetails];
}
else{
completionBlock(result.accessToken);
}
}];
}
-(NSArray*)getEventsList
{
__block NSMutableArray * todoList;
[self getToken:YES completionHandler:^(NSString* accessToken){
NSURL *todoRestApiURL = [[NSURL alloc]initWithString:#"https://outlook.office365.com/api/v1.0/me/folders/inbox/messages?$top=2"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL:todoRestApiURL];
NSString *authHeader = [NSString stringWithFormat:#"Bearer %#", #""];
[request addValue:authHeader forHTTPHeaderField:#"Authorization"];
[request addValue:#"application/json; odata.metadata=none" forHTTPHeaderField:#"accept"];
[request addValue:#"fbbadfe-9211-1234-9654-fe435986a1d6" forHTTPHeaderField:#"client-request-id"];
[request addValue:#"Presence-Propelics/1.0" forHTTPHeaderField:#"User-Agent"];
//[request addValue:#"true" forHTTPHeaderField:#"return-client-request-id"];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (error == nil){
NSArray *scenarios = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
todoList = [[NSMutableArray alloc]initWithArray:scenarios];
//each object is a key value pair
NSDictionary *keyVauePairs;
for(int i =0; i < todoList.count; i++)
{
keyVauePairs = [todoList objectAtIndex:i];
NSLog(#"%#", keyVauePairs);
}
}
NSLog(#"Finished");
//[delegate updateTodoList:TodoList];
}];
}];
return nil; }
Error is returned in response object:
{
error = {
code = ErrorAccessDenied;
message = "Access is denied. Check credentials and try again.";
};
}
I know its late to answer this but it might be helpful for someone like me who was struggling to get the same thing done
I have done this using the office 365 SDK for iOS which has all the inbuilt classes to do your work.
If you download their sample code it will provide you all the details you require to do certain operations (mail, calendar, contacts, one drive).
Before using the SDK make sure you login to Azure AD and register your application and add permissions so that you do not get 403 error code or any access denied message.
I am using the below code to fetch my events details from outlook calendar
[self getClientEvents:^(MSOutlookClient *client) {
NSURLSessionDataTask *task = [[[client getMe] getEvents] read:^(NSArray<MSOutlookEvent> *events, MSODataException *error) {
if (error==nil) {
if (events.count!=0) {
dispatch_async(dispatch_get_main_queue(), ^{
for(MSOutlookEvent *calendarEvent in events){
NSLog(#"name = %#",calendarEvent.Subject);
}
});
}else{
NSLog(#"No events found for today");
}
}
}];
[task resume];
}];
getClientEvents is a method which gives call to the Office 365 SDK and fetches the event details of the user but it first fetches the token for the resource and then makes the call with the acquired token
-(void)getClientEvents : (void (^) (MSOutlookClient* ))callback{
[self getTokenWith : #"https://outlook.office365.com" :true completionHandler:^(NSString *token) {
MSODataDefaultDependencyResolver* resolver = [MSODataDefaultDependencyResolver alloc];
MSODataOAuthCredentials* credentials = [MSODataOAuthCredentials alloc];
[credentials addToken:token];
MSODataCredentialsImpl* credentialsImpl = [MSODataCredentialsImpl alloc];
[credentialsImpl setCredentials:credentials];
[resolver setCredentialsFactory:credentialsImpl];
[[resolver getLogger] log:#"Going to call client API" :(MSODataLogLevel *)INFO];
callback([[MSOutlookClient alloc] initWithUrl:#"https://outlook.office365.com/api/v1.0" dependencyResolver:resolver]);
}];
}
getTokenWith method fetches the token for a resource first and then with the acquired token makes the necessary calls to fetch the events, but before fetching the token it checks in the cache to see if there are any tokens available for the same resource.
// fetch tokens for resources
- (void) getTokenWith :(NSString *)resourceId : (BOOL) clearCache completionHandler:(void (^) (NSString *))completionBlock;
{
// first check if the token for the resource is present or not
if([self getCacheToken : resourceId completionHandler:completionBlock]) return;
ADAuthenticationError *error;
authContext = [ADAuthenticationContext authenticationContextWithAuthority:[[NSUserDefaults standardUserDefaults] objectForKey:#"authority"] error:&error];
NSURL *redirectUri = [NSURL URLWithString:#"YOUR_REDIRECT_URI"];
[authContext acquireTokenWithResource:resourceId
clientId:[[NSUserDefaults standardUserDefaults] objectForKey:#"clientID"]
redirectUri:redirectUri
completionBlock:^(ADAuthenticationResult *result) {
if (AD_SUCCEEDED != result.status){
[self showError:result.error.errorDetails];
}
else{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:result.tokenCacheStoreItem.userInformation.userId forKey:#"LogInUser"];
[userDefaults synchronize];
completionBlock(result.accessToken);
}
}];
}
getCacheToken method: Checks if there are any reusable token for any resources.
-(BOOL)getCacheToken : (NSString *)resourceId completionHandler:(void (^) (NSString *))completionBlock {
ADAuthenticationError * error;
id<ADTokenCacheStoring> cache = [ADAuthenticationSettings sharedInstance].defaultTokenCacheStore;
NSArray *array = [cache allItemsWithError:&error];
if([array count] == 0) return false;
ADTokenCacheStoreItem *cacheItem;
for (ADTokenCacheStoreItem *item in array) {
if([item.resource isEqualToString:resourceId]){
cacheItem = item;
break;
}
}
ADUserInformation *user = cacheItem.userInformation;
if(user == nil) return false;
if([cacheItem isExpired]){
return [self refreshToken:resourceId completionHandler:completionBlock];
}
else
{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:user.userId forKey:#"LogInUser"];
[userDefaults synchronize];
completionBlock(cacheItem.accessToken);
return true;
}
}
Using this code and Office 365 SDK in place you can get the outlook events for a particular user, before that make sure you have full permissions in the Azure AD else you may get 0 events as response.
Please note all the methods are from the SDK example apart from the first method to view how to fetch the events i would recommend to download the exchange example from the github.
You can also use MSGraph SDK to fetch calendars and events:
Check this link: Configuration process is same, only fetching events is different(see given code for fetching events):
How to Fetch/Create calender by O365-iOS-Connect?
Note: Above link is used to fetch calendars from outlook the process is same for this but you should use this code after authentication and completed get events action look like this:
- (IBAction)getCalendarsEvents:(id)sender {
[NXOAuth2AuthenticationProvider setClientId:clientId
scopes:#[#"https://graph.microsoft.com/Files.ReadWrite",
#"https://graph.microsoft.com/Calendars.ReadWrite"]];
[[NXOAuth2AuthenticationProvider sharedAuthProvider] loginWithViewController:nil completion:^(NSError *error) {
if (!error) {
[MSGraphClient setAuthenticationProvider:[NXOAuth2AuthenticationProvider sharedAuthProvider]];
self.client = [MSGraphClient client];
// Authentication done
[[[[_client me] events] request] getWithCompletion:^(MSCollection *response, MSGraphUserEventsCollectionRequest *nextRequest, NSError *error){
NSArray *arr = response.value;
MSGraphEvent *event = arr.firstObject;
// Here you will getting outlook events
}];
}
}];
}

Show activity indicator when iOS app is loading data?

My iPhone app has to load 3 data sets when it is first opened. I have 3 view controllers, one for each data set. I notice that when I am on my real iPhone and first open the app and touch a view controller there may be a very long pause, I am assuming while the data is being loaded, but I am not sure.
Here is the relevant code in my AppDelegate:
#import "AppDelegate.h"
#import "MasterViewController.h"
#implementation AppDelegate
- (void)application:(UIApplication *)application performFetchWithCompletionHandler: (void (^)(UIBackgroundFetchResult))completionHandler
{
// Background Fetch for Employees
EmployeeDatabase *tmpEmployeeDatabase = [[EmployeeDatabase alloc] init];
[tmpEmployeeDatabase updateEmployeeData];
completionHandler(UIBackgroundFetchResultNewData);
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Set Background Fetch Interval
[application setMinimumBackgroundFetchInterval: UIApplicationBackgroundFetchIntervalMinimum];
// Take Database Name and get DatabasePath for later use
self.databaseName = #"employees.db";
self.databaseNameLocations = #"locations.db";
self.databaseNameContacts = #"contacts.db";
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDir = [documentPaths objectAtIndex:0];
self.databasePath =[documentDir stringByAppendingPathComponent:self.databaseName];
self.databasePathLocations =[documentDir stringByAppendingPathComponent:self.databaseNameLocations];
self.databasePathContacts =[documentDir stringByAppendingPathComponent:self.databaseNameContacts];
// See if we need to initialize the employee db
EmployeeDatabase *tmpEmployeeDatabase = [[EmployeeDatabase alloc] init];
if (![tmpEmployeeDatabase checkIfDatabaseExists]) {
[tmpEmployeeDatabase updateEmployeeData];
}
// See if we need to initialize the contact db
ContactsDatabase *tmpContactsDatabase = [[ContactsDatabase alloc] init];
if (![tmpContactsDatabase checkIfDatabaseExists]) {
[tmpContactsDatabase updateContactsData];
}
// See if we need to initialize the Locations db
LocationDatabase *tmpLocationDatabase = [[LocationDatabase alloc] init];
if (![tmpLocationDatabase checkIfDatabaseExists]) {
[tmpLocationDatabase updateLocationData];
}
return YES;
}
#pragma mark - Application's Documents directory
// Returns the URL to the application's Documents directory.
- (NSURL *)applicationDocumentsDirectory {
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
#end
And here is where I call one of the web services and load the data:
#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
- (void)callWebService {
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
[manager.requestSerializer setAuthorizationHeaderFieldWithUsername:#"xxxxxx" password:#"xxxxxxxxxxxx"];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
AFHTTPRequestOperation *operation = [manager GET: #"https://xxxx/mobile/mobilede.nsf/restPeople.xsp/People"
parameters: [self jsonDict]
success: ^(AFHTTPRequestOperation *operation, id responseObject)
{
NSMutableArray *employees = (NSMutableArray *)responseObject;
FMDatabase *db = [FMDatabase databaseWithPath:self.employeeDatabasePath];
[db open];
for (NSDictionary *dict in employees) {
BOOL success = [db
executeUpdate:
#"INSERT INTO employees "
"(id,fstNme,midNme,lstNme,fulNme,locNbr,supID,"
"mrkSeg,geoLoc,lvl,vp,ema,ofcPhn,mobPhn) VALUES "
"(?,?,?,?,?,?,?,?,?,?,?,?,?,?);",
[dict objectForKey:#"id"], [dict objectForKey:#"fstNme"],
[dict objectForKey:#"midNme"], [dict objectForKey:#"lstNme"],
[dict objectForKey:#"fulNme"], [dict objectForKey:#"locNbr"],
[dict objectForKey:#"supId"], [dict objectForKey:#"mrkSeg"],
[dict objectForKey:#"geoLoc"], [dict objectForKey:#"lvl"],
[dict objectForKey:#"vp"], [dict objectForKey:#"ema"],
[dict objectForKey:#"ofcPhn"],[dict objectForKey:#"mobPhn"], nil];
if (success) {
} // Only to remove success error
}
}
failure:
^(AFHTTPRequestOperation * operation, NSError * error) {
NSLog(#"Error: %#", error);
}
];
[operation start];
}
[EDIT]
I forgot the part of the code where I called the
[EDIT]
One possible mistake is that I am using the same background queue for each of the three processes? See the #define kBgQueue at the top of this bit of code.
What is the best practice to handle this? Should I NOT put this on a background queue and alert the user to wait?
[Thank you. I changed this and recompiled. The first time the app starts the interface will freeze at some point, and you cannot do anything for 12 seconds or so, and then it comes out of it. Subsequently there are no pauses. As an example, when I first open the app I can get to my first view, Employees by Name, and if I touch it to list them it might go into the the list but be blank. So I will touch the navigation backward and then it stops for 12 seconds (or so) and then it will return to the main menu, and when I go back in there are the employees. And it never stops from then on out. I cannot figure out why if this is on the background que that it is holding up the interface. What could I run to try to determine when this is happening?]
If your app could not work without data put data processing in background and show some activity indicator to user. If your app could work without data let user do whatever he wants to do and after data is being loaded just reload UI with new data.

Accessing NSDictionary object outside of ASYNC callback

I am using UNIRest to make a call and return a JSON object to my app. I have it returning the proper data as a NSDictionary and it logs our perfect. I am now trying to take that data and display it inside of my view. I cannot use my dictionary outside of the callback.
I have been digging around here on StackOverflow for similar results and posts related to variables. I feel it is a scope issue with it being limited to inside of the callback block.
My header file: (UIViewController)
#property (nonatomic, strong) NSDictionary *tideData;
My implementation:
#interface TideDetailViewController ()
#end
#implementation TideDetailViewController
#synthesize tideData;
- (void)viewDidLoad {
[super viewDidLoad];
// tideData = [[NSDictionary alloc] init];
// location is working, I removed it for testing to call a static string for now
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.distanceFilter = kCLDistanceFilterNone; // whenever we move
self.locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters; // 100 m
[self.locationManager startUpdatingLocation];
NSString *locationQueryURL = #"http://api.wunderground.com/api/XXXXXXXXXXXXX/tide/geolookup/q/43.5263,-70.4975.json";
NSLog(#"%#", locationQueryURL);
[[UNIRest get:^(UNISimpleRequest *request) {
[request setUrl: locationQueryURL];
}] asJsonAsync:^(UNIHTTPJsonResponse *response, NSError *error) {
// This is the asyncronous callback block
self.code = [response code];
NSDictionary *responseHeaders = [response headers];
UNIJsonNode *body = [response body];
self.rawResults = [response rawBody];
// I tried this as self as well
tideData = [NSJSONSerialization JSONObjectWithData:self.rawResults options: 0 error: &error];
// this logs perfectly.
NSLog(#"tideData %#", tideData);
// tried setting it to the instance
//self.tideData = tideData;
}];
// returns null
NSLog(#"tideData outside of call back %#", self.tideData);
// this is where I will be setting label text for now, will refactor once I get it working
// rest of file contents........
I have tried a good amount of items related to scoping, clearly just missing the mark. Any ideas? I have searched setting global variables, etc. Been stuck on this for a bit now.
Thanks,
Ryan
The reason you see nil is because you are logging it too soon: when you call
NSLog(#"tideData outside of call back %#", self.tideData);
the get:asJsonAsync: method has not received the results yet.
You can fix this problem by adding a setter for your property, and adding some special handling to it, like this:
-(void)setTideData:(NSDictionary*)dict {
_tideData = dict;
NSLog(#"tideData outside of call back %#", _tideData);
}
This method will be called from the asynchronous code when you do the tideData = ... assignment.
Try setting the object on main thread:
[self performSelectorOnMainThread:#selector(setTideData:) withObject:[NSJSONSerialization JSONObjectWithData:self.rawResults options: 0 error: &error] waitUntilDone:NO];
- (void)setTideData:(NSDictionary*)dict {
self.tideData = dict;
}

iOS facebook integration - sending and receiving requests

I use a facebook api to connect to facebook and send request via native dialogs provided by the api.
I followed the sample posted in the docs on developers.facebook.com
But I have following problem sending requests :
1. The requests are not shown in notifications - only in application center - In this case i think that it is a problem of that the app is in sandbox and not posted to APPSTORE
I succesfully send request to facebook server with right fbUser id. But when I want to receive the notification in app here comes the problem :
Following the docs as an authorized user I should se
this in open url method:
fb[APP_ID]://authorize#expires_in=[ACCESS_TOKEN_EXPIRATION]
&access_token=[USER_ACCESS_TOKEN]
&target_url=https://apps.facebook.com/[APP_NAME_SPACE]/?request_ids=
[COMMA_SEPARATED_REQUESTIDs]&ref=notif&app_request_type=user_to_user
But i can see only plain login without targer url .... I can see session expiration date, fb app id, access token and so on. But no target url?
So basically what the target_url is?
How it should be set?
What i have to include when sending request?
In addition :
application handle open url method is called properly.
checkRequests method is also called properly after app becomes active.
Please do not link me to the docs. I have read it moreless 50 times and didn't find any reasonable solution...
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
// attempt to extract a token from the url
self.openedURL = url;
NSLog(#"%#",url);
return [FBSession.activeSession handleOpenURL:url];
}
- (void)sendRequest {
FBSBJSON *jsonWriter = [FBSBJSON new];
NSDictionary *gift = [NSDictionary dictionaryWithObjectsAndKeys:
#"5", #"points",
#"1", #"badge",
nil];
NSString *giftStr = [jsonWriter stringWithObject:gift];
NSMutableDictionary* params =
[NSMutableDictionary dictionaryWithObjectsAndKeys:
#"Hi from test app", #"message",
giftStr, #"data",
nil];
[self.facebook dialog:#"apprequests"
andParams:params
andDelegate:self];
}
// Handle the request call back
- (void)dialogCompleteWithUrl:(NSURL *)url {
NSDictionary *params = [self parseURLParams:[url query]];
NSString *requestID = [params valueForKey:#"request"];
NSLog(#"Request ID: %#", requestID);
}
-(FBSession*)returnSession{
return self.session;
}
/*
* Helper function to get the request data
*/
- (void) notificationGet:(NSString *)requestid {
[FBRequestConnection startWithGraphPath:requestid
completionHandler:^(FBRequestConnection *connection,
id result,
NSError *error) {
if (!error) {
NSString *title;
NSString *message;
if ([result objectForKey:#"data"]) {
title = [NSString
stringWithFormat:#"%# sent you a gift",
[[result objectForKey:#"from"]
objectForKey:#"name"]];
FBSBJSON *jsonParser = [FBSBJSON new];
NSDictionary *requestData =
[jsonParser
objectWithString:[result objectForKey:#"data"]];
message =
[NSString stringWithFormat:#"Badge: %#, Karma: %#",
[requestData objectForKey:#"badge"],
[requestData objectForKey:#"points"]];
} else {
title = [NSString
stringWithFormat:#"%# sent you a request",
[[result objectForKey:#"from"] objectForKey:#"name"]];
message = [NSString stringWithString:
[result objectForKey:#"message"]];
}
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:title
message:message
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil,
nil];
[alert show];
// Delete the request notification
[self notificationClear:[result objectForKey:#"id"]];
}
}];
}
/*
* Helper function to check incoming URL
*/
- (void) checkIncomingNotification {
if (self.openedURL) {
NSString *query = [self.openedURL fragment];
if (!query) {
query = [self.openedURL query];
}
NSDictionary *params = [self parseURLParams:query];
for (NSString * str in [params allKeys]) {
NSLog(#"key %#", str);
}
// Check target URL exists
NSString *targetURLString = [params valueForKey:#"target_url"];
if (targetURLString) {
NSURL *targetURL = [NSURL URLWithString:targetURLString];
NSDictionary *targetParams = [self parseURLParams:[targetURL query]];
NSString *ref = [targetParams valueForKey:#"ref"];
// Check for the ref parameter to check if this is one of
// our incoming news feed link, otherwise it can be an
// an attribution link
if ([ref isEqualToString:#"notif"]) {
// Get the request id
NSString *requestIDParam = [targetParams
objectForKey:#"request_ids"];
NSArray *requestIDs = [requestIDParam
componentsSeparatedByString:#","];
// Get the request data from a Graph API call to the
// request id endpoint
[self notificationGet:[requestIDs objectAtIndex:0]];
}
}
// Clean out to avoid duplicate calls
self.openedURL = nil;
}
}
Is there any way that these problems are caused by the way that the app is not published on Appstore (Appstore id is not set neither for iPhone nor iPad)?
Here are code snippets showing using of the fb api:
Thank you very much for the time.
Enable deep linking in Facebook app settings
Facebook sdk 3.5 requests not working
I think this link will help you,configure App on Facebook as well

Resources