I integrate gmail into my app by using GData Obcective-C Client for authentication and obtaining therefrom contacts. For authentication I use gtm-oauth2 and this part work pretty good.
My scope for GTMOAuth2ViewControllerTouch init:
NSString *scope = [NSString stringWithFormat:#"https://www.googleapis.com/auth/plus.me %#", [GDataServiceGoogleContact authorizationScope]];
Auth init:
__keychainItemName = [infoPlist objectForKey:#"GoogleKeyChainItem"];
__auth = [GTMOAuth2ViewControllerTouch
authForGoogleFromKeychainForName:__keychainItemName
clientID:[infoPlist objectForKey:#"GoogleClientID"]
clientSecret:[infoPlist objectForKey:#"GoogleClientSecret"]];
For GData building i use this blog (with pics and stuff)
http://hoishing.wordpress.com/2011/08/23/gdata-objective-c-client-setup-in-xcode-4/
GData I get from google repository, just by running this in console
# Non-members may check out a read-only working copy anonymously over HTTP.
svn checkout http://gdata-objectivec-client.googlecode.com/svn/trunk/ gdata-objectivec-client-read-only
Problems begin when I try to get contacts:
- (GDataServiceGoogleContact *)contactService {
static GDataServiceGoogleContact* service = nil;
if (!service) {
service = [[GDataServiceGoogleContact alloc] init];
[service setShouldCacheResponseData:YES];
[service setServiceShouldFollowNextLinks:YES];
[service setAuthorizer:__auth];
}
return service;
}
- (void) methodExecute {
GDataServiceGoogleContact *service = [self contactService];
GDataServiceTicket *ticket;
const int kBuncha = 2000;
NSURL *feedURL = [GDataServiceGoogleContact contactFeedURLForUserID:kGDataServiceDefaultUser];
GDataQueryContact *query = [GDataQueryContact contactQueryWithFeedURL:feedURL];
[query setShouldShowDeleted:NO];
[query setMaxResults:kBuncha];
[ticket setAuthorizer:__auth];
ticket = [service fetchFeedWithQuery:query
delegate:self
didFinishSelector:#selector(contactsFetchTicket:finishedWithFeed:error:)];
}
- (void)contactsFetchTicket:(GDataServiceTicket *)ticket
finishedWithFeed:(GDataFeedContact *)feed
error:(NSError *)error {
if(error != nil){
NSLog(#"%#\n\n\n%#", error, feed);
}
else{
NSLog(#"%#\n\n\n%#", error, feed.entries);
}
}
And here is the point - instead of GDataEntryContact which have to be in feed, I get array of GDataEntryBase objects. There is object description example:
GDataEntryBase 0xb3b2300: {v:3.1 title:John Jackson etag:"Rn4_fjVSLit***."
categories:1 links:photo,self,edit edited:2013-03-14T17:55:57Z
id:http://www.google.com/m8/feeds/contacts/myemail%40gmail.com/base/kindofid
unparsed:<gContact:groupMembershipInfo>,<gd:name>,<gd:phoneNumber>}
I try to replace svn GData to This GData version, but everything is useless. I'm on the edge.
BTW I also turned "on" the option Contacts API at google console and added -DGDATA_INCLUDE_CONTACTS_SERVICE=1 in Other C Flags for GData.
Am I missed something or just stupid?
Great thanks for your reply!
I entered other linker flags only for project and they are, for some reason, do not applied for the whole target.
Related
I am trying to add Google Drive support to one of my apps using a private app data folder. I have sign-in working with the GIDSignIn class and the scope set to kGTLRAuthScopeDriveAppdata. Once I am signed in, I can create folders and get a file listing that shows the folders are there, then I can delete the folders and the file listing shows that they are gone. But for some reason when I try to upload a file I get a 403 error ("The user does not have sufficient permissions for this file."). This happens whether I try to put the file in the root of the app data folder or into a folder I have created.
I have set up a project in the Google Developer Console. I have an API key configured to work with my app's bundle ID and given it unrestricted API access. The Google Drive API is enabled.
My code is adapted from Google's own samples so a lot of this may look quite familiar. I've trimmed down the sign-in handling since that appears to be working fine.
- (instancetype) init
{
self = [super init];
if (!self) return nil;
[GIDSignIn sharedInstance].clientID = (NSString *)kGoogleClientId;
//kGoogleClientId is the ID from the developer console.
[GIDSignIn sharedInstance].delegate = self;
[GIDSignIn sharedInstance].scopes = #[kGTLRAuthScopeDriveAppdata];
return self;
}
//GIDSignInDelegate method
- (void) signIn:(GIDSignIn *)signIn didSignInForUser:(GIDGoogleUser *)user withError:(NSError *)error
{
authenticatedUser = user; //authenticatedUser is an instance variable
NSLog(#"Signed in to Google Drive with user %#", user.profile.name);
[delegate GoogleDriveDidSignIn:self];
}
- (GTLRDriveService *) driveService
{
static GTLRDriveService *service;
static dispatch_once_t onceToken;
dispatch_once(&onceToken,
^{
service = [[GTLRDriveService alloc] init];
service.APIKey = (NSString *)kGoogleApiKey;
//kGoogleApiKey matches the developer console too. It has unrestricted API access and is tied to my bundle ID
service.APIKeyRestrictionBundleID = [[NSBundle mainBundle] bundleIdentifier];
service.shouldFetchNextPages = YES;
service.retryEnabled = YES;
});
service.authorizer = authenticatedUser.authentication.fetcherAuthorizer;
//authenticatedUser is an instance variable which stores the user information returned by
//GIDSignIn when the user signs in
return service;
}
- (void) createFolderNamed:(NSString *)folderName completionHandler:(void(^)(NSString *foldername, NSString *newFolderId))completionHandler
{
GoogleDriveHandler * __weak weakself = self;
GTLRDriveService *service = [self driveService];
GTLRDrive_File *folder = [GTLRDrive_File object];
folder.name = folderName;
folder.mimeType = (NSString *)kMimeType_GoogleDriveFolder;
folder.parents = #[#"appDataFolder"];
GTLRDriveQuery_FilesCreate *query = [GTLRDriveQuery_FilesCreate queryWithObject:folder uploadParameters:nil];
[service executeQuery:query completionHandler:^(GTLRServiceTicket * _Nonnull callbackTicket, id _Nullable object, NSError * _Nullable callbackError)
{
if (callbackError)
{
NSLog(#"-createFolderNamed: callbackError: %#", callbackError.localizedDescription);
}
else
{
GTLRDrive_File *createdFolder = (GTLRDrive_File *)object;
if ( [createdFolder.mimeType isEqualToString:(NSString *)kMimeType_GoogleDriveFolder] )
{
NSLog(#"Google Drive created folder named \"%#\" with identifier \"%#\" and mime-type \"%#\"", createdFolder.name, createdFolder.identifier, createdFolder.mimeType);
}
else
{
NSLog(#"Error : Attempted to create folder, but Google Drive created item named \"%#\" with identifier \"%#\" and mime-type \"%#\"", createdFolder.name, createdFolder.identifier, createdFolder.mimeType);
}
}
}];
}
- (void) writeFileAtUrl:(NSURL *)source toFolderWithId:(NSString *)folderId completionHandler:(void(^)(NSString *filename, NSString *newFileId))completionHandler
{
GoogleDriveHandler * __weak weakself = self;
GTLRDriveService *service = [self driveService];
GTLRDrive_File *file = [GTLRDrive_File object];
file.name = source.lastPathComponent;
file.mimeType = #"binary/octet-stream";
file.parents = #[folderId];
file.spaces = #[#"appDataFolder"];
GTLRUploadParameters *parameters = [GTLRUploadParameters uploadParametersWithFileURL:source MIMEType:#"binary/octet-stream"];
parameters.shouldUploadWithSingleRequest = YES;
GTLRDriveQuery_FilesCreate *query = [GTLRDriveQuery_FilesCreate queryWithObject:file uploadParameters:parameters];
query.fields = #"id";
[service executeQuery:query completionHandler:^(GTLRServiceTicket * _Nonnull callbackTicket, id _Nullable object, NSError * _Nullable callbackError)
{
if (callbackTicket.statusCode == 200)
{
GTLRDrive_File *createdFile = (GTLRDrive_File *)object;
NSLog(#"Wrote file %# in Google Drive folder %#", createdFile.name, folderId);
if (completionHandler) completionHandler(createdFile.name, createdFile.identifier);
}
else
{
NSLog(#"-writeFileAtUrl:toFolderWithId:completionHandler: status code = %li : callbackError: %#", callbackTicket.statusCode, callbackError.localizedDescription);
}
}];
}
As an example, I've tried doing this after GIDSignIn logs in:
NSURL *sampleFile = [[NSBundle mainBundle] URLForResource:#"AValidTestFile" withExtension:#"png"];
if (sampleFile)
{
[self writeFileAtUrl:sampleFile toFolderWithId:#"appDataFolder" completionHandler:^(NSString *filename, NSString *newFileId)
{
NSLog(#"Uploaded file %# with ID %#", filename, newFileId);
}];
}
I still just get a 403 error.
At this point, I've read a huge number of tutorials, blog posts and forum threads in several different programming languages. I've gone over my own code several times and added an insane number of logging statements to double check everything, but I can't work out how I can have permission to create folders, but not to put files in them.
Some time later...
If you go through the credential wizard in the Google Console (rather than just selecting an iOS credential because you're creating an iOS app), you get a message which says "Application data cannot be accessed securely from iOS. Please consider selecting another platform" and it refuses to create a credential for you. Is it possible that this just doesn't work, despite the SDK having the necessary constants?
For those who follow after me, I think I've concluded that using the appDataFolder in iOS just doesn't work.
Having switched to using a folder in the Drive space, I've also found that the -uploadParametersWithFileURL:MIMEType: method of GTLRUploadParameters doesn't work. When I use that I get a file called 'Untitled' (containing the file metadata I set in my GTLRDrive_File object) in the root of the drive. As soon as I switched to -uploadParametersWithData:MIMEType: I got the correct file in the correct place.
I suppose the lesson so far is that if something isn't working, assume it’s the SDK.
I am using GTMAppAuth SignIn for authentication in one of my iOS application. With that I am defining all possible scopes for Google Calendar use but still getting the following error.Google Calendar API & CalDAV API is enable on Google Console but still I do not know why I am getting this error.i also add Scopes for Google APIs shown in below image.
my code is given below:
-(void) fetchCalendarList
{
self.calendarList = nil;
self.calendarListFetchError = nil;
id<GTMFetcherAuthorizationProtocol> authorization =
[GTMAppAuthFetcherAuthorization authorizationFromKeychainForName:kGTMAppAuthKeychainItemName];
self.calendarService.authorizer = authorization;
GTLRCalendarService *service = self.calendarService;
GTLRCalendarQuery_CalendarListList *query = [GTLRCalendarQuery_CalendarListList query];
// query.minAccessRole = kGTLRCalendarMinAccessRoleOwner;
// query.minAccessRole = kGTLRCalendarOrderByStartTime;
self.calendarListTicket = [service executeQuery:query
completionHandler:^(GTLRServiceTicket *callbackTicket,
id calendarList,
NSError *callbackError) {
// Callback
self.calendarList = calendarList;
self.calendarListFetchError = callbackError;
self.calendarListTicket = nil;
[self fetchEvents];
}];
}
i am getting this error:
Printing description of callbackError:
Error Domain=com.google.GTLRErrorObjectDomain Code=403 "Insufficient Permission" UserInfo={GTLRStructuredError=GTLRErrorObject 0x1c065adc0: {message:"Insufficient Permission" errors:[1] code:403}, NSLocalizedDescription=Insufficient Permission}
I have a question related with Amazon iOS Mobile SDK v2. I have a task to create an empty folder inside provided bucket. To create an empty folder I am using method:
- (void)createDirectory:(AWSS3Object *)directory inBucket:(AWSS3Bucket *)bucket success:(void (^)())success failure:(void (^)(NSError *error))failure
{
NSString *configurationKey = [NSString string];
if (self.configurationType == AmazonServiceConfigurationTypeDefault)
{
configurationKey = S3ConfigurationKey;
}
else if (self.configurationType == AmazonServiceConfigurationTypeHUB)
{
configurationKey = S3HUBConfigurationKey;
}
else if (self.configurationType == AmazonServiceConfigurationTypeTemporary)
{
configurationKey = S3TemporaryConfigurationKey;
}
AWSS3 *s3 = [AWSS3 S3ForKey:configurationKey];
AWSS3PutObjectRequest *putObjectRequest = [AWSS3PutObjectRequest new];
putObjectRequest.key = directory.key;
putObjectRequest.bucket = bucket.name;
putObjectRequest.body = [NSString string];
[[s3 putObject:putObjectRequest] continueWithBlock:^id(AWSTask *task)
{
if (task.error)
{
NSError *error = task.error;
failure(error);
}
else
{
success();
}
return nil;
}];
}
With this method I am always getting the error: The request signature we calculated does not match the signature you provided.
My access and secure keys are correct.
From the previous experience, I was used to get this error because of incorrect parameters.
My key: AWS iOS SDK v2 Test/test/
My bucket: abc-test
Previously, when I was using AWS iOS Mobile SDK v1, practically the same code has been working like a charm.
Does anyone know, where is the problem?
I am able to reproduce this problem, The cause of problem is the trailing slash of the key value has been stripped by NSURL.path ,lead to an incorrect request signature error. We will fix the AWS Mobile SDK in future release. Thanks for bringing it to our attention.
I have added the announcement app and added few items to it, now i want to fetch the items from my announcement list so I have used the below code
token_Obtained_During_first_time_Login: This is the token that i get when i login for the first time using the acquireTokenWithResource method of ADAuthenticationContext class
- (void)getClientList {
NSString *appToken = [[NSUserDefaults standardUserDefaults]
valueForKey:#"token_Obtained_During_first_time_Login"];
NSString* hostName = #"https://myTenant.sharepoint.com/sites/myApp";
OAuthentication *credentials = [[OAuthentication alloc] initWith:appToken];
ListClient *client = [[ListClient alloc]
initWithUrl:hostName
credentials:credentials];
NSURLSessionTask* task = [client getListItems:#"MyAnnouncements"
callback:^(NSMutableArray *listItems, NSError *error) {
if (error==nil) {
NSLog(#"%#",listItems);
}
}];
[task resume];
}
I have even debugged the 365 code and it provides me the below URL for getListItems: callback method
https://myTenant.sharepoint.com/sites/myApp/_api/lists/GetByTitle('MyAnnouncements')/Items
I have even tried the same using getTokenWith method which comes with the sample code
- (void)getAnnouncementList:(void (^)(ListClient *))callback{
NSString* hostName = #"https://myTenant.sharepoint.com";
[self getTokenWith:hostName :true completionHandler:^(NSString *token) {
OAuthentication *credentials = [[OAuthentication alloc] initWith:token];
callback([[ListClient alloc]initWithUrl:hostName credentials:credentials]);
}];
}
But still no luck i get the list as nil
Please guide on how this can be resolved, I have even verified the rights in the Azure Directory everything seems fine am able to fetch data of one drive, mails and calendar but list is a place where i am stuck.
Every time i call the above code i get the response nil not sure what am passing wrong, my guess is the token.
I resolved this issue by making a change in the apiUrl present in the ListClient.m file of Office 365.
All i did was changed it to
const NSString *apiUrl = #"/sites/mobileApp/_api/web/Lists";
Making the above change did the trick and now i can access all the list data.
i'm trying to get data from Google Analytics, using the Core Reporting API sdk.
https://developers.google.com/analytics/devguides/reporting/core/v3/gdataLibraries
But even the samples for IOS are not compiling on Xcode 5, and i cannot find any source code or exemple.
Sorry to ask for that, but is there anyone have a link to a demo, or tutorial to get this done?
There is a bug reported under analytics-issues
a linker error when building with Xcode 5
it has fixed status as for 20 Sep 2013. And project manager has also given a link to IOS SDK where is should work with version > 3.01.
But after this fixed status - there are three users as for today who have the problem.
After a lot of try, i put a small sample of how to get data, if this can help someone:
NSString *const kKeychainItemName = #"eKit: gan";
NSString *const kMyClientID = #"149329999999-heob9dk0o7d9999nkufaehrk99iaf9na.apps.googleusercontent.com"; // pre-assigned by service
NSString *const kClientSecret = #"pAzA-4AAAAAcAAAmAAAAAjbA"; // pre-assigned by service
#interface sFirstViewController ()
#end
#implementation sFirstViewController {
APPaginalTableView *_paginalTableView;
GTLObject *myGTL;
}
- (void)viewDidLoad{
[super viewDidLoad];
}
- (void)viewController:(GTMOAuth2ViewControllerTouch *)viewController finishedWithAuth:(GTMOAuth2Authentication *)auth
error:(NSError *)error {
if (error != nil) {
// Authentication failed
} else {
// Authentication succeeded
self.auth=auth;
[self analytics_Query];
[viewController dismissViewControllerAnimated:TRUE completion:nil];
}
}
- (void)analytics_Query{
GTLServiceAnalytics *service = [[GTLServiceAnalytics alloc] init];
service.authorizer=self.auth;
GTLQueryAnalytics *query = [GTLQueryAnalytics queryForDataGaGetWithIds:#"ga:79891549" startDate:#"2006-01-01" endDate:#"today" metrics:#"ga:totalEvents"];
//GTLQueryAnalytics *query = [GTLQueryAnalytics queryForManagementAccountsList];
GTLServiceTicket *ticket = [service executeQuery:query
completionHandler:^(GTLServiceTicket *ticket, id object, NSError *error) {
if (error == nil) {
myGTL=object;
}
}];
}