Google Drive API downloadUrl 404 in iOS - ios

So I am trying to allow two users to swap files that each has in their Google Drive. That involves knowing the ID of the other person's file and using the API calls to retrieve it. Both files sit in folders that have been shared to anyone/public.
Trouble is when I execute the code below I am finding that each user can only use the downloadUrl corresponding to the file they own - the others return a 404. In this case either "mine" or "theirs" depending on the account I'm logged into.
// _driveService and its authorizer setup elsewhere
// Retrieve the metadata then the actual data
NSString *mine = #"0B4Pba9IBDsR3T1NVTC1XSGJTenc";
NSString *theirs = #"0B4n9OyY8tqWpNlNaN1dUc3FsNG8";
NSString *get = [NSString stringWithFormat:#"https://www.googleapis.com/drive/v2/files/%#",theirs];
[_driveService fetchObjectWithURL:[NSURL URLWithString:get] completionHandler:^
(GTLServiceTicket *ticket, GTLDriveFile *file, NSError *error)
{
if (error != nil)
NSLog(#"Error retrieving metadata: %#", error);
else
{
// Download the actual data
GTMHTTPFetcher *fetcher = [_driveService.fetcherService fetcherWithURLString:file.downloadUrl];
[fetcher beginFetchWithCompletionHandler:^
(NSData *data, NSError *error)
{
if (error != nil)
NSLog(#"Error retrieving actual data: %#", error);
else
{
NSString *content = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"Content: %#", content);
}
}];
}
}];
Error retrieving actual data: Error Domain=com.google.HTTPStatus Code=404 "The operation couldn’t be completed. (com.google.HTTPStatus error 404.)"
What am I doing wrong here? If it's a permissions thing, why am I allowed to get the metadata?
Note this is for an iOS app and both files were created and uploaded from the app using the official client API (rev 353).

Hah, so it seems the devil is in the detail I left out of the question. When creating the authorizer the scope I was providing is kGTLAuthScopeDriveFile, which was the default in an example and I forgot all about it when everything else thus far worked fine. Apparently I need to use kGTLAuthScopeDrive instead (the differences are explained here)
The logic seems a bit flawed here though, I mean I don't want access to other files that weren't created with the app, I just want access to a public file somebody else created with the app...

Related

Google Drive REST in iOS

My question is rather simple.
After integrating Google Drive REST API I could finally download files from my Google Drive.SO I have followed this example
https://developers.google.com/drive/v3/web/manage-downloads
Everything is working fine,and in console I get the information that the file is downloaded. So here is the question:where it goes,to which folder ? What is the path to where the file is saved?
And how do I set the necessary save path if I need to save a file to iOS Documents folder?
NSString *fileId = #"0BwwA4oUTeiV1UVNwOHItT0xfa2M";
GTLRQuery *query = [GTLRDriveQuery_FilesGet queryForMediaWithFileId:fileId];
[driveService executeQuery:query completionHandler:^(GTLRServiceTicket *ticket,
GTLRDataObject *file,
NSError *error) {
if (error == nil) {
NSLog(#"Downloaded %lu bytes", file.data.length);
} else {
NSLog(#"An error occurred: %#", error);
}
}];
I a developing for iOS 10 and an above and as I read the infrmation:NOTE: Because NSURLConnection is deprecated as of iOS 9 and OS X 10.11, this class has been superseded by GTMSessionFetcher.I should use either code provided by google(posted above ) or GTMSessionFetcher.I am using code from google.But Will be thankful is someone will help me solve my problem with both (google and GTMSessionFetcher) variants.
UPD: For Google API Client lib
GTLRDataObject has data property which contains raw bytes of your file. It is enough to safe your file using standard iOS methods.
There is also contentType property which is a string with MIME type of the file. You probably want to store this information somewhere along with the path to saved data, it will help you to properly display what kind of file (image/song/text) it is, as well as use it when you decode/open/read saved data to present actual information.
GTLRQuery *query = [GTLRDriveQuery_FilesGet queryForMediaWithFileId:fileId];
[driveService executeQuery:query completionHandler:^(GTLRServiceTicket *ticket,
GTLRDataObject *file,
NSError *error) {
if (error == nil) {
NSLog(#"Downloaded %lu bytes", file.data.length);
// Here is where you take these bytes and save them as file to any location you want (typically your Documents folder)
NSURL *documentsDirectoryURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
[file.data writeToURL:documentsDirectoryURL atomically:YES];
// Now you can store the url/path of the saved file somewhere along with the MIME type string (maybe new class or structure describing the file in your app)
MyFile *file = [[MyFile alloc] initWithFilePath:[documentsDirectoryURL path] mimeType:contentType];
} else {
NSLog(#"An error occurred: %#", error);
}
}];
See CocoaDocs for GTLRDataObject reference.
Generic version: using iOS SDK
If you use NSURLSession, it has delegate method which you use to move file from temporary location to anywhere you need.
Obj-C
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
// Use `location` to move your data to Documents directory or wherever else
}
Swift
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
// Use `location` to move your data to Documents directory or wherever else
}

Google Drive Api V3 is not returning Web Content link after uploading a new file to Drive?

I am working on google client Api V3 for uploading files from my IOS Application.
The Scopes I have used are
NSArray *scopes = [NSArray arrayWithObjects:kGTLAuthScopeDrive,kGTLAuthScopeDriveFile,kGTLAuthScopeDriveAppdata,kGTLAuthScopeDriveMetadata, nil];
The code I used for uploading a new file is
GTLUploadParameters *uploadParameters = [GTLUploadParameters uploadParametersWithData:data MIMEType:file.mimeType];
GTLQuery * query = [GTLQueryDrive queryForFilesCreateWithObject:metadata uploadParameters:uploadParameters];
GTLServiceTicket *uploadTicket=[self.gogService executeQuery:query completionHandler:^(GTLServiceTicket *ticket,
GTLDriveFile *updatedFile,
NSError *error) {
if (error == nil) {
NSLog(#"File WebContent Link %#", [updatedFile webViewLink]);
}
else {
NSLog(#"An error occurred: %#", [error localizedDescription]);
}
}];
[uploadTicket setUploadProgressBlock:^(GTLServiceTicket *ticket, unsigned long long totalBytesWritten, unsigned long long totalBytesExpectedToWrite)
{
progressBar.progress=(1.0*totalBytesWritten/(1.0*totalBytesExpectedToWrite)) ;
NSLog(#"%llu",totalBytesWritten);
NSLog(#"%llu",totalBytesExpectedToWrite);
}];
The problem is web content link is null and want it in response to store it in my database because my web App will use this link to open that file.
Previous version of google drive api was returning web content link.
How can I get Web Content Link?
Check this documentation: Migrate to Google Drive API v3. Be noted that full resources are no longer returned by default. Use the fields query parameter to request specific fields to be returned. If left unspecified only a subset of commonly used fields are return. You may check this sample for reference.

Uploading a file to Google drive using iOS sdk

In my current application, I am trying to upload a .text file to google drive using Google SDK for iOS. But the issue is every time I got the error message in my Log as Insufficient Permission, so If anyone have faced this or have an idea on this type of error kindly help me.
Here is my code,
GTLDriveFile *file = [GTLDriveFile object];
file.descriptionProperty = #"Uploaded from the iOS app.";
file.mimeType = #"text/*";
file.name=#"MyTextFile";
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"myTestFile" ofType:#"txt"];
GTLUploadParameters *uploadParameters = [GTLUploadParameters uploadParametersWithFileURL:[NSURL URLWithString:filePath] MIMEType:file.mimeType];
GTLQueryDrive *query = [GTLQueryDrive queryForFilesCreateWithObject:file uploadParameters:uploadParameters];
[self.service executeQuery:query completionHandler:^(GTLServiceTicket *ticket, id object, NSError *error) {
if (error) {
NSLog(#"Error: %#", error);
}else{
}
}];
Log print when this action is performed,
unexpected response data (uploading to the wrong URL?)
{"error":{"code":403,"message":"Insufficient Permission","data":[{"domain":"global","reason":"insufficientPermissions","message":"Insufficient Permission"}]},"id":"gtl_3"}
Premature failure: upload-status:"final" location:(null)
I solve this problem
First Step
change your scope to [kGTLAuthScopeDriveFile]
but after first step, i got a same 403 error
because my APP in my phone have scope [kGTLAuthScopeDriveMetadataReadonly]
Second Step
reset my application or build in another simulator
I think first auth key chain using scope readonly was existed so doesn't write file to google drive
after change scope and re-auth then it works
if you change kKeychainItemName's value to another then could re-auth again with new scope
google says in Quickstart tutorial
// If modifying these scopes, delete your previously saved credentials by
// resetting the iOS simulator or uninstall the app.
// private let scopes = [kGTLAuthScopeDriveFile]

createSession in GKGameSession is only returning nil

I'm trying to start a new GKGameSession and when I use createSession, all I can get so far is nil. Here's my code:
GKGameSession.createSession(inContainer: "test", withTitle: "MyGame", maxConnectedPlayers: 8)
{ (newGameSession, error) in
self.gameSession = newGameSession
print("\(newGameSession)")
newGameSession?.getShareURL(completionHandler: { (url, error) in
print("url: \(url) error: \(error)")
})
}
The only thing it prints is "nil". Any help is appreciated.
If you using the emulator, I suggest using a device instead. GKGameSessions do not play well with the emulator, because they depend on push notifications and an iCloud account that is logged in.
newGameSession is optional. So it seems like something has gone wrong when creating a new session.
I would say newGameSession is likely nil, in that case error will hopefully contain some useful information.
Try replacing print("\(newGameSession)") with print(newGameSession, error) to see what the error var has to say, or set a breakpoint if you know how to do that.
Try to use your app's iCloud container name instead of "test". The container name will be in the format iCloud.com.yourcompany.appname if you have selected the default container option. To ensure your app has an iCloud container you need to enable it in your app's Capabilities.
I'm a jumping in a little late, but I'd triple check the container name you're using to create the session.
I enabled all three options in xCode: key-value storage, iCloud documents and CloudKit.
I don't use iCloud drive.
The following code successfully creates a new session each time I send invites. It prints the new session, then iterates through all existing sessions for that container ID.
-(void)turnBasedMatchmakerViewController:(GKTurnBasedMatchmakerViewController *)viewController didFindMatch:(GKTurnBasedMatch *)match
{
[self dismissViewControllerAnimated:YES completion:nil];
NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
NSString *iCloudContainerName = [#"iCloud." stringByAppendingString: bundleIdentifier];
[GKGameSession createSessionInContainer:iCloudContainerName
withTitle:#"test"
maxConnectedPlayers:4
completionHandler:^(GKGameSession * _Nullable session, NSError * _Nullable error)
{
NSLog(#"(1) Session: %#, Error: %#", session.identifier, [error description]);
[GKGameSession loadSessionsInContainer:iCloudContainerName
completionHandler:^(NSArray<GKGameSession *> * _Nullable sessions, NSError * _Nullable error)
{
for (GKGameSession *session in sessions)
{
NSLog(#"(2) Session: %#, Error: %#", session.identifier, [error description]);
}
NSLog(#"-----");
}];
}];
}

Performing moments.insert when using GTMOAuth2ViewControllerTouch for authentication

I'm trying to insert a "Moment" into a user's Google+ account, using the google-api-objectivec-client library. I think have the authentication process working properly. It's primarily the same way I have YouTube authentication set up, but with the correct scope and keychain name. However, when I try to run the query to insert the moment I get the following error:
Error Domain=com.google.GTLJSONRPCErrorDomain Code=401 "The operation
couldn’t be completed. (Unauthorized)"
After looking at Google's documentation more carefully (here) I found the following comment:
When authenticating for moments.insert, you must include the data-requestvisibleactions
parameter to specify which types of App Activities your application will write.
Google has several examples of how to do this with other programming languages, but they don't have any examples for the objective-c library, and the objective-c project doesn't contain any examples of how to do this either.
I know that there's another authentication method, using the GPPSignIn button, which has a way to set the actions. However, my application is using multiple other Google API clients (YouTube, YouTube Analytics, and URL Shortener). Mixing the GoogleOpenSource.framework with the other Objective-C libraries causes a conflict. So, I need to use the GTMOAuth2ViewControllerTouch class.
My Authentication Code
GTMOAuth2ViewControllerTouch *viewController =
[GTMOAuth2ViewControllerTouch controllerWithScope:kGTLAuthScopePlusLogin
clientID:kGoogleApiClientId
clientSecret:kGoogleApiClientSecret
keychainItemName:kGooglePlusKeychainName
completionHandler:^(GTMOAuth2ViewControllerTouch *viewController, GTMOAuth2Authentication *auth, NSError *error) {
if (error)
NSLog(#"Error: %#", error.description);
else
app.googlePlusService.authorizer = auth; //this sets a property of my app delegate, to be used elsewhere in the application
}];
[self.navigationController pushViewController:viewController animated:YES];
The code I'm using to insert the "Moment"
NSString *shareUrl = "http://www.google.com"; //just for this example
GTLPlusMoment *moment = [[GTLPlusMoment alloc] init];
moment.type = #"http://schemas.google.com/AddActivity";
GTLPlusItemScope *target = [[GTLPlusItemScope alloc] init];
target.url = shareUrl;
moment.target = target;
GTLQueryPlus *query =
[GTLQueryPlus queryForMomentsInsertWithObject:moment
userId:#"me"
collection:kGTLPlusCollectionVault];
[app.googlePlusService executeQuery:query
completionHandler:^(GTLServiceTicket *ticket,
id object,
NSError *error) {
if (error) {
NSLog(#"Got bad response from plus.moments.insert: %#", error);
} else {
NSLog(#"Moment inserted: %#",moment.identifier);
}
}];
Has anyone out there successfully found the place to add the data-requestvisibleactions parameter to either the authentication call or queryForMomentsInsertWithObject method to allow them to execute the moments.insert action without receiving an error?
Thanks!
You can add data-requestvisibleactions using Java Script code in Authentication. Once you authorized with java script, it will be authorized for all types of insert moments. There is no method in Objective c for adding data-requestvisibleactions in your code. Only the option is possible through Java Script.
For More derails Refer THIS

Resources