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}
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.
When I'm trying to perform request for inserting of a broadcast I receive error:
Error Domain=com.google.GTLRErrorObjectDomain Code=403 "The user is blocked from live streaming."
UserInfo={GTLRStructuredError=GTLRErrorObject 0x28027ad30: {code:403
errors:[1] message:"The user is blocked from live streaming."},
NSLocalizedDescription=The user is blocked from live streaming.}
I have started receiving this error today. Before, everything has been working fine. I have tested on several accounts and had not any luck.
Code:
GTLRYouTube_LiveBroadcastSnippet *broadcastSnippet= [[GTLRYouTube_LiveBroadcastSnippet alloc] init];
[broadcastSnippet setTitle:title];
[broadcastSnippet setScheduledStartTime:[GTLRDateTime dateTimeWithDate:self.beginOfStream]]; // current date + 1 minute.
[broadcastSnippet setScheduledEndTime:[GTLRDateTime dateTimeWithDate:[NSDate dateWithTimeIntervalSinceNow:80000]]];
GTLRYouTube_LiveBroadcastStatus *status = [[GTLRYouTube_LiveBroadcastStatus alloc] init];
[status setPrivacyStatus:[StreamSettings youtubeStringForPrivacyStatus:[privacyStatus intValue]]];
GTLRYouTube_LiveBroadcastContentDetails *details = [self streamDetailsWith:latency];
GTLRYouTube_LiveBroadcast *broadcast = [[GTLRYouTube_LiveBroadcast alloc] init];
[broadcast setKind:#"youtube#liveBroadcast"];
[broadcast setSnippet:broadcastSnippet];
[broadcast setStatus:status];
GTLRYouTubeQuery_LiveBroadcastsInsert *query = [GTLRYouTubeQuery_LiveBroadcastsInsert queryWithObject:broadcast
part:#"id, snippet, contentDetails,status"];
GTLRYouTubeService *service = self.youTubeService;
__strong id <YouTubeHelperDelegate> strongDelegate = self.delegate;
[service executeQuery:query completionHandler:^(GTLRServiceTicket *ticket,
GTLRYouTube_LiveBroadcast *returnedBrocast,
NSError *error) {
if (error) {
NSLog(#"%#", error); //Here is place I got an error
}
}];
If the logged in user does not have more than 1000 subscribers he can't go live using a mobile app, as mentioned in link:
https://support.google.com/youtube/answer/2853834?hl=en
I'm using the Google Cloud Storage API for obj-c for inserting data into a bucket, but I get a 400 server error back:
{reason:"keyInvalid" message:"Bad Request" domain:"usageLimits"}
I'm using the Service Account Authentication method by setting the APIKey property of GTLServiceStorage as the generated key value from the developer console and as I understand I do not need the access token as for the OAuth2 authentication?
I have tried several combinations for the various fields with no luck. The code can be seen below:
_serviceStorage = [GTLServiceStorage new];
_serviceStorage.additionalHTTPHeaders = #{#"x-goog-project-id": LTProjectID};
_serviceStorage.APIKey = ServiceAccountAPIKeyFromJsonKey;
GTLUploadParameters *uploadParam
= [GTLUploadParameters uploadParametersWithData:data MIMEType:#"audio/mp4"];
GTLStorageObject *storageObj = [GTLStorageObject object];
storageObj.name = #"file_name"
GTLStorageObjectAccessControl *objAccessControl
= [GTLStorageObjectAccessControl new];
objAccessControl.entity = #"allUsers";
objAccessControl.email = #"5942472xxxxxxx#developer.gserviceaccount.com";
objAccessControl.role = #"OWNER";
storageObj.acl = #[objAccessControl];
GTLQueryStorage *query
= [GTLQueryStorage queryForObjectsInsertWithObject:storageObj
bucket:#"my_bucket"
uploadParameters:uploadParam];
GTLServiceTicket *ticket
= [_serviceStorage executeQuery:query
completionHandler:^(GTLServiceTicket *ticket,
id object,
NSError *error) {
// error: {reason:"keyInvalid" message:"Bad Request" domain:"usageLimits"}
}
}];
ticket.uploadProgressBlock ...
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.
I'm trying to make a request for playListItems using the YouTube iOS SDK.
I'm getting back an error...
Error Domain=com.google.GTLJSONRPCErrorDomain
Code=-32602 "The operation couldn’t be completed. (No filter selected.)"
UserInfo=0x1568f1e0
{
error=No filter selected.,
GTLStructuredError=GTLErrorObject 0x1567fdf0: {message:"No filter selected." code:-32602 data:[1]},
NSLocalizedFailureReason=(No filter selected.)
}
The query I'm using is this...
GTLServiceYouTube *service = [[GTLServiceYouTube alloc] init];
service.APIKey = #"my key";
// I suspect the problem is here...
GTLQueryYouTube *query = [GTLQueryYouTube queryForPlaylistItemsListWithPart:#"contentDetails"];
query.q = #"playlistId=<The playlist ID>";
GTLServiceTicket *ticket = [service executeQuery:query
completionHandler:^(GTLServiceTicket *ticket, id object, NSError *error) {
if (!error) {
GTLYouTubePlaylistItemListResponse *playlistItems = object;
for (GTLYouTubePlaylistItem *playlistItem in playlistItems) {
GTLYouTubePlaylistItemContentDetails *details = playlistItem.contentDetails;
NSLog(#"PlaylistItem video ID = %#", details.videoId);
}
} else {
NSLog(#"%#", error);
}
}];
The problem is that the docs for the entire API are useless so there is no example of this being used. I'm having to put it together from the example for the Google Shopping apis and stuff.
Most of the guess work came from here.
Any ideas what I should put in the query.q value? Or if there's something else I'm missing?
OK, I finally found a link that shed some link on this.
The code to use is...
query.playlistId = #"the playlist ID";
Then everything worked :D