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.
Related
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
I have an in-home app that will be supported by IT team.
When they support desktop apps, they may read logs to find any troubles (like server returned 404 on sync request).
I can use NSLog for iOS, but how can user access them with out of Xcode?
I can't ask any user "please give me your phone to investigate what has happened".
Does there is some tool any IT person with out of Xcode and Mac may use to read iOS logs?
Does there is some tool any IT person with out of Xcode and Mac may use to read iOS logs?
Unfortunately not. It used to be that you could run an app on your device that would read the Console log, but Apple took that ability away; I guess they saw it as a security breach.
If your user can get to a Mac running Xcode, they can view the console log directly in Xcode.
Otherwise, as others have suggested, you will have to build into your app the capacity to keep a log in a place you can get to. For example you can write to a file and then offer (within the app) to email that file to yourself. Many apps have an interface to a facility like this in their Settings bundle.
I've been using the combination of CocoaLumberjack, Antenna & DDAntennalogger at work for remote logging. Basically, you have to set up an end-point at your server and Antenna will be used to send the logs remotely.
Here's the reference that when configuring it on my project:
Remote logging using CocoaLumberjack, Antenna & DDAntennaLogger
This is how you can do it:
Step 1: Redirect your NSLog statements to a text file in file system. This you can do on specific user action or always enable it and delete it periodically.
Step 2: Have a web service which will allow you to upload the saved logs in the file system. You could trigger this on user action or may be a timer based job.
Step 3: Delete the logs from file system once upload is successful.
Here is a example of such a custom logger:
#import "MyCustomLogging.h"
#define kMyCustomLoggingFile #"NSLogging.txt"
static NSString *const kMyDeviceLogUploadURL = #"uploadDeviceLogURL";
#interface MyCustomLogging ()
#property (nonatomic, strong) MyRequestHandler *requestHandler;
#property (nonatomic, assign, getter = isNsLogRedirected) BOOL nsLogRedirected;
#property (nonatomic, assign) BOOL shouldStopLogging;
#property (nonatomic, strong) NSString *pathForLogging;
#end
#implementation MyCustomLogging
static int savedStdErr = 0;
static MyCustomLogging *sharedMyCustomLogging = nil;
+ (MyCustomLogging *)sharedMyCustomLogging {
static dispatch_once_t pred = 0;
dispatch_once(&pred, ^{
sharedMyCustomLogging = [[self alloc] init];
});
return sharedMyCustomLogging;
}
#pragma mark -
#pragma mark Start Method
- (void)startLogging {
// Starting the Redirection of the Logs
if (!self.isNsLogRedirected) {
[self nsLogRedirectedToFile];
}
}
#pragma mark -
#pragma mark Stop Method
- (void)stopLogging {
NSLog(#"Stopping the logging");
NSString *aLoggingPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
self.pathForLogging = [aLoggingPath stringByAppendingPathComponent:kMyCustomLoggingFile];
// If file already exists & logging was not redirected then directly upload the logs::A Possible case of app quit/crash without uploading previous logs
if ([self isLogFilePresent] && !self.nsLogRedirected) {
[self uploadLogs];
} else if (self.isNsLogRedirected) { //Check for Successive Stop Notifications
self.shouldStopLogging = YES;
[self restoreNSLog];
} else {
NSDictionary *anUserInfo = #{kMyDeviceLogUplodStatusKey: kMyValueOne};
[[NSNotificationCenter defaultCenter] postNotificationName:kMyDeviceLogsUploadNotification object:nil userInfo:anUserInfo];
}
}
#pragma mark -
#pragma mark Private Method
- (void)nsLogRedirectedToFile {
if (!self.isNsLogRedirected) {
NSLog(#"Redirecting NSLogs to a file.....");
self.nsLogRedirected = YES;
savedStdErr = dup(STDERR_FILENO);
NSString *aLoggingPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
self.pathForLogging = [aLoggingPath stringByAppendingPathComponent:kMyCustomLoggingFile];
NSLog(#"Logging Path: %#", self.pathForLogging);
freopen([self.pathForLogging cStringUsingEncoding:NSASCIIStringEncoding],"a+",stderr);
NSLog(#"NSLog Redirected to a file Succesfully");
[MySessionObject setLoggingOn:YES];
}
}
- (void)restoreNSLog {
if (self.isNsLogRedirected) {
[MySessionObject setLoggingOn:NO];
NSLog(#"NSLog Will be Restored now....");
self.nsLogRedirected = NO;
fflush(stderr);
dup2(savedStdErr, STDERR_FILENO);
close(savedStdErr);
savedStdErr = 0;
}
[self uploadLogs];
NSLog(#"NSLog Restored Successfully");
}
- (void)uploadLogs {
NSLog(#"Now uploading files");
// Disable logging before files are uploading
MySessionObject.enableLogging = NO;
NSError *anError = nil;
NSData *aLogData = [NSData dataWithContentsOfFile:self.pathForLogging options:NSDataReadingUncached error:&anError];
//Converting to String
NSString *aLogString = [[NSString alloc] initWithData:aLogData encoding:NSUTF8StringEncoding];
NSMutableDictionary *aPostBody = [[NSMutableDictionary alloc] initWithCapacity:3];
[aPostBody setValue:aLogString forKey:#"logData"];
[aPostBody setValue:MySessionObject.wifiMACAddress forKey:#"deviceMACAddress"];
[aPostBody setValue:MySessionObject.deviceToken forKey:#"deviceID"];
__weak MyCustomLogging *aBlockSelf = self;
self.requestHandler = [[MyRequestHandler alloc] initWithEndPoint:#"/uploadLogs" body:aPostBody container:nil loadingOverlayTitle:nil successHandler:^(NSDictionary *iResponse) {
if (iResponse) {
//Remove the File From the Path
NSError *aFileError = nil;
BOOL aFileRemoveSuccess = [[NSFileManager defaultManager] removeItemAtPath:self.pathForLogging error:&aFileError];
if (!aFileRemoveSuccess) {
//Tracking the Event
NSString *aDescription = [NSString stringWithFormat:#"Error Code:%ld Error Description:%#", (long)[aFileError code], [aFileError localizedDescription]];
NSLog(#"Error occured while deleting log file:%#", aDescription);
}
// Clearing all
aBlockSelf.pathForLogging = nil;
NSDictionary *anUserInfo = #{kMyDeviceLogUplodStatusKey: kMyValueOne};
[[NSNotificationCenter defaultCenter] postNotificationName:kMyDeviceLogsUploadNotification object:nil userInfo:anUserInfo];
}
} andErrorHandler:^(NSString *iMessage, NSString *iKey, NSInteger iErrorCode, BOOL iIsNetworkError) {
NSDictionary *anUserInfo = #{kMyDeviceLogUplodStatusKey: kMyValueZero};
[[NSNotificationCenter defaultCenter] postNotificationName:kMyDeviceLogsUploadNotification object:nil userInfo:anUserInfo];
}];
[self.requestHandler executeRequest];
}
- (BOOL)isLogFilePresent {
NSFileManager *aFileManager = [[NSFileManager alloc] init];
BOOL aFilePresent = [aFileManager fileExistsAtPath:self.pathForLogging];
return aFilePresent;
}
#end
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.
Using the Google Drive iOS API, how can I create a folder AND share it?
GTLDriveFile * folder = [GTLDriveFile object];
folder.title = #"Test";
folder.mimeType = #"application/vnd.google-apps.folder";
// ???
May be this one helps you. This code is for creating folder with specific name.
Code 1 :
-(void)createFolderWithName:(NSString *)folderName {
GTLDriveFile *file = [GTLDriveFile object];
file.title = #"FileUpload";
file.mimeType = #"application/vnd.google-apps.folder";
// To create a folder in a specific parent folder, specify the identifier
// of the parent:
// _resourceId is the identifier from the parent folder. Here i am using root as the parent folder.
NSString *resourceId = #"root";
if (resourceId.length && ![resourceId isEqualToString:#"0"]) {
GTLDriveParentReference *parentRef = [GTLDriveParentReference object];
parentRef.identifier = resourceId;
file.parents = [NSArray arrayWithObject:parentRef];
}
GTLQueryDrive *query1 = [GTLQueryDrive queryForFilesInsertWithObject:file uploadParameters:nil];
[[self appDelegate].driveService executeQuery:query1 completionHandler:^(GTLServiceTicket *ticket, id object, NSError *error) {
if (!error) {
//I see this in the console...
//No Errors in Folder Query:
//GTLDriveFileList 0x108f3430: {kind:"drive#fileList"
//etag:""Q5ElJByAJoL0etObruYVPRipH1k/vyGp6PvFo4RvsFtPoIWeCReyIC8""}
NSLog(#"No Errors in Folder Query: %#",object);
GTLDriveFileList *list = (GTLDriveFileList *)object;
//So, none of this for() loop happens
for (GTLDriveFile *file in list.items) {
NSLog(#"Folder: \n \n %# \n \n",file);
//NSLog(#"\n file extension value is %#",file.fileExtension);
//NSLog(#"\n file size value is %#",file.fileSize);
}
} else {
NSLog(#"Folder Query Error: %#",error);
}
}];
}
In the above code resourceId is the parent folder id, under which u want to create the folder.
I don't know how to share the full folder, but i think it is same like sharing the file.
If u want to know about particular file sharing please refer this link. In the link you can find the example for objective-c code.
https://developers.google.com/drive/v2/reference/permissions/insert
first let me say that I am new ios/xcode as well as AWS.
I am creating an app that writes data to an AWS S3 bucket. The app works when creating a bucket and putting objects to the US Standard Region. However, when I change the region to Singapore, the app creates the bucket successfully - but, I cannot put objects into the bucket and AWS does not produce an error or exception of any kind.
Here is the code in question. The commented code in the createBucket method successfully creates a bucket in Singapore. The processGrandCentralDispatchUpload method is works for the US Standard region, but does not put objects to my Singapore bucket.
- (void)createBucket
{
// Create the bucket.
#try {
//S3Region *region = [[S3Region alloc] initWithStringValue:kS3RegionAPSoutheast1];
//S3CreateBucketRequest *createBucketRequest = [[S3CreateBucketRequest alloc] initWithName:[Constants S3Bucket] andRegion:region];
S3CreateBucketRequest *createBucketRequest = [[S3CreateBucketRequest alloc] initWithName:[Constants S3Bucket]];
S3CreateBucketResponse *createBucketResponse = [self.s3 createBucket:createBucketRequest];
NSLog(#"create bucket response: %#", createBucketResponse.error);
if(createBucketResponse.error != nil)
{
NSLog(#"Error: %#", createBucketResponse.error);
}
}
#catch (AmazonServiceException* asex) {
NSLog(#"putObject - AmazonServiceException - %#", asex);
}
#catch (AmazonClientException* acex) {
NSLog(#"putObject - AmazonClientException - %#", acex);
}
}
- (void)processGrandCentralDispatchUpload:(NSData *)jsonData withTimestamp:(int)timestamp
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
UserData * user = [[[DataStore defaultStore] user] objectAtIndex:0];
NSString * dateKeyComponent = [self putRequestDateComponent:timestamp];
objectName = [NSString stringWithFormat:#"%#/%#/%#", user.email, user.uniqueIdentifier, dateKeyComponent];
S3PutObjectRequest *putObjectRequest = [[S3PutObjectRequest alloc] initWithKey:objectName
inBucket:[Constants S3Bucket]];
putObjectRequest.contentType = #"data/json";
putObjectRequest.data = jsonData;
// Put the image data into the specified s3 bucket and object.
#try {
S3PutObjectResponse *putObjectResponse = [self.s3 putObject:putObjectRequest];
dispatch_async(dispatch_get_main_queue(), ^{
if(putObjectResponse.error != nil)
{
NSLog(#"Error: %#", putObjectResponse.error);
[self showAlertMessage:[putObjectResponse.error.userInfo objectForKey:#"message"] withTitle:#"Upload Error"];
}
else
{
//[self showAlertMessage:#"The data was successfully uploaded." withTitle:#"Upload Completed"];
}
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
});
}
#catch (AmazonServiceException* asex) {
NSLog(#"putObject - AmazonServiceException - %#", asex);
}
#catch (AmazonClientException* acex) {
NSLog(#"putObject - AmazonClientException - %#", acex);
}
});
}
I am one of the maintainers of the AWS SDK for iOS. I'm sorry you're encountering difficulties.
If your bucket name contains non alpha numeric characters you may need to set the endpoint of the Amazon S3 client to the region of your bucket.
AmazonS3Client *s3 = [[AmazonS3Client alloc] initWithCredentialsProvider:provider];
s3.endpoint = [AmazonEndpoints s3Endpoint:AP_SOUTHEAST_1];
You can also turn on verbose logging in your application to see the raw responses from the service while using the SDK. If you do see errors coming back that are not being captured, please let us know.
[AmazonLogger verboseLogging];
A couple things I will note about your code that you may also want to consider:
Once the bucket is created, you no longer need to call create bucket. You may want to consider removing the S3CreateBucketRequest/S3CreateBucketResponse from your application.
S3 bucket naming is unique across all regions. If the bucket was created in US Standard then you cannot create it in Singapore without first deleting the bucket in US Standard.
You seem to be mixing both exception and error handling in your code. Please see our blog post on how to control exception handling in the SDK.