Say I am assuming that my Box account has a folder called "TestFolder". I want to get the folder ID of that folder so I can write files to it from within my iOS app.
Is my only option to traverse the entire root of my Box account looking for that folder name? Something like this?
__block NSString *folderID;
BOXContentClient *contentClient = [BOXContentClient defaultClient];
BOXFolderItemsRequest *listAllInRoot = [contentClient folderItemsRequestWithID:BOXAPIFolderIDRoot];
[listAllInRoot performRequestWithCompletion:^(NSArray *items, NSError *error) {
if (error != nil) {
NSLog(#"Something bad happened when listing Box contents.");
return;
}
int ii,nItems = (int) [items count];
for (ii=0; ii<nItems; ii++) {
BOXItem *currItem = [items objectAtIndex:ii];
if ([[currItem name] isEqualToString:#"TestFolder"] && [currItem isFolder]) {
folderID = [currItem modelID];
break;
}
}
}];
You can use the Box API search endpoint to query a folder by name. If the search endpoint finds the folder, the response will include the folder's id.
Here is an example that shows how to make this call with the Box iOS SDK:
BOXContentClient *contentClient = [BOXContentClient defaultClient];
BOXSearchRequest *searchRequest = [contentClient searchRequestWithQuery:#"Test Folder" inRange:NSMakeRange(0, 1000)];
[searchRequest performRequestWithCompletion:^(NSArray *items, NSUInteger totalCount, NSRange range, NSError *error) {
// If successful, items will be non-nil and contain BOXItem model objects; otherwise, error will be non-nil.
}];
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.
After looking and finding for a shared folder that matches a given name, I need to know if that folder's creator/owner is a specific google account. How could I achieve that utilizing Google Drive's Objective-C SDK.
Here is my query that finds the shared folder given its name:
GTLQueryDrive *queryDrive = [GTLQueryDrive queryForFilesList];
queryDrive.q = #"mimeType='application/vnd.google-apps.folder' and trashed=false";
[self.service executeQuery:queryDrive completionHandler:^(GTLServiceTicket *ticket,
GTLDriveFileList *files,
NSError *error) {
if (error == nil)
{
NSString *identifier = #"";
for(GTLDriveFile *file in files.files)
{
NSArray *tempArray = [NSArray arrayWithArray:file.owners];
NSLog(#"File Title: %# | ID: %#",file.name , file.identifier);
if ([file.name isEqualToString:#"GIVEN FOLDER NAME"]) {
identifier = file.identifier;
//How to know if the found folder is owned by a specified email?
break;
}
}
if ([identifier isEqualToString:#""]) {
[UISpinningWheelController removeSpinningWheel:self.view];
[self showAlertWithTitle:#"Error" andMessage:#"The required folder has not been shared with this Google account. Please contact the administrator."];
}
}
else {
[UISpinningWheelController removeSpinningWheel:self.view];
[self showAlertWithTitle:#"Error" andMessage:[error localizedDescription]];
}
}];
Thank you so much for your help.
I have the following code, which lists out the files in the root, but does not list any of the sub folders in the root:
NSString *parentId = #"root";
GTLQueryDrive *queryDrive = [GTLQueryDrive queryForFilesList];
queryDrive.q = [NSString stringWithFormat:#"'%#' in parents and trashed=false", parentId];
[self.driveService executeQuery:queryDrive completionHandler:^(GTLServiceTicket *ticket,GTLDriveFileList *files,NSError *error)
{
if (error == nil)
{
NSLog(#"Number of files: %i", [files.items count]);
NSLog(#"Have results");
// Iterate over files.items array
for(GTLDriveFile *file in files)
{
NSLog(#"File Title: %# | ID: %#",file.title, file.identifier);
}
}
else
{
NSLog(#"An error occurred: %#", error);
}
}];
This lists out the 7 files I have in the folder, but at the same level as the 7 files, I have a sub folder.
For example:
root->file1
root->file2
...etc
all come back
But root->MyFolder does not get listed in the result set.
Any ideas?
You might want to check how you've authenticated your app. If you authenticate with only your app's files as your scope, you will not see other files and folders created outside of your app.
When you authenticate like this:
self.gDriveAuthenticationViewController = GTMOAuth2ViewControllerTouch(scope:kGTLAuthScopeDrive,
clientID:clientId,
clientSecret:clientSecret,
keychainItemName:keychainName,
delegate:self,
finishedSelector:"viewController:finishedWithAuth:error:")
Use scope:kGTLAuthScopeDrive instead of kGTLAuthScopeDriveFile
In my application, I need to get file's description, however, the descriptionProperty was always nil for every file I've got. I did the same thing using google API explorer (the field was the same: items(description, title)), and could get the description successfully.
The code is the following:
GTLQueryDrive *query = [GTLQueryDrive queryForFilesList];
query.fields = #"items(description,title)";
[driveService executeQuery:query completionHandler:^(GTLServiceTicket *ticket, GTLDriveFileList *files, NSError *error) {
if (error == nil) {
NSLog(#"has %d files", [files.items count]);
GTLDriveFile *item;
for (int i = 0; i < files.items.count; i ++) {
item = [files.items objectAtIndex: i];
NSLog(#"%#", item.title);
NSLog(#"%#", item.descriptionProperty);
}
} else {
NSLog(#"An error occurred: %#", error);
}
}];
the log message I've got:
has 19 files
TestFile.txt
(null)
TestFile1.txt
(null)
...
As you can see, I got the title successfully, but the description was null for every file. I also tried not to set query.fields to let it get all fields. In this case, I could successfully get the description. But I really don't want to get all fields cause it's not necessary at all. Does anyone know what caused the problem? Thanks a lot!
Thanks for the report, this is a bug that should be fixed this week.
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