I have an iOS 11 app which currently can query any Google Sheets spreadsheet to get all of the data from any named sheet (tab), assuming that the spreadsheet is public (ie, no authentication or authorization required). Here's a simplified version of my current code that works fine:
-(void)fetchSheet
{
if (self.service == nil)
{
self.service = [[GTLRSheetsService alloc] init];
self.service.APIKey = @"MY_API_KEY";
}
// Build the query
NSString *range = @"MainSheet!A1:E";
GTLRSheetsQuery_SpreadsheetsValuesGet *query = [GTLRSheetsQuery_SpreadsheetsValuesGet queryWithSpreadsheetId:self.spreadsheetId range:range];
// Execute the query
[self.service executeQuery:query delegate:self didFinishSelector:@selector(parseSheetWithTicket:finishedWithObject:error:)];
} // fetchSheet
- (void)parseSheetWithTicket:(GTLRServiceTicket *)ticket finishedWithObject:(GTLRSheets_ValueRange *)result error:(NSError *)error
{
// Check error, process as desired
} // parseSheetWithTicket:finishedWithObject:error:
Now I'd like to make a change so that the owner of the sheet can restrict the sheet to just my app if desired, but I don't want the user to need a Google account or login to Google. My app should just be able to read the spreadsheet without any involvement of the user. Anyone could let a spreadsheet be used with my app by sharing read-only access to the MyApp@gmail.com user (which my app knows about). The best idea I could find is at How do I authorise an app (web or installed) without user intervention? which involves using the Google OAuthPlayground to make an initial authentication, and then ending up with an authorization token and refresh token. (Of course, I'm using the GTLRSheetsService service instead of Drive service shown on that page.) The idea is that my app can use the refresh token at any point in the future, and the Google client libraries will use that refresh token to get a new authorization token, and then make the request.
I realize there are security implications of this approach. Let's assume that my app will fetch the refresh token via iCloud or from a server that I host. (This would especially be handy if I ever need to change the refresh token.) For now I'm just trying to get it to work with a hard-coded refresh token.
I followed the instructions in the accepted answer on that page, and ended up with a refresh token (as well as client ID, etc). But now I don't know how to use it. After much searching I found some code that uses GTMOAuth2Authentication, but I think I should use GTMAppAuth instead (right?). Here's my new version of fetchSheet:
-(void)fetchSheet
{
if (self.service == nil)
{
self.service = [[GTLRSheetsService alloc] init];
// note that I am no longer setting service.APIKey
// Create GTMOAuth2Authentication object to hold the refresh_token
GTMOAuth2Authentication *auth = [[GTMOAuth2Authentication alloc] init];
auth.clientID = @"MY_CLIENT_ID";
auth.clientSecret = @"MY_CLIENT_SECRET";
auth.userEmail = @"MyApp@gmail.com";
auth.userID = @"myapp";
auth.refreshToken = @"MY_REFRESH_TOKEN";
// Save and use
self.googleAuth = auth;
self.service.authorizer = self.googleAuth;
}
// Build the query
NSString *range = @"MainSheet!A1:E";
GTLRSheetsQuery_SpreadsheetsValuesGet *query = [GTLRSheetsQuery_SpreadsheetsValuesGet queryWithSpreadsheetId:self.spreadsheetId range:range];
// Execute the query
[self.service executeQuery:query delegate:self didFinishSelector:@selector(parseAboutSheetWithTicket:finishedWithObject:error:)];
} // fetchSheet
When I run this code, it fails and prints "Beginning a fetch requires a request with a URL" in the debug output. What URL is it referring to, and why do I need to set one now when I didn't before?
I also tried using GTMAppAuthFetcherAuthorization (with OIDServiceConfiguration, OIDAuthorizationRequest, and OIDAuthState objects) instead of GTMOAuth2Authentication but then I got the error "Unable to open Safari" which indicates it was trying to use the standard Google user authentication and authorization stuff.
Does anyone know how to make this work? If the approach that I'm following should work, then I assume I just need to create some object that implements the GTMFetcherAuthorizationProtocol and which holds the refresh token. It seems conceptually simple, but the process on that page doesn't describe how to actually use the refresh token (and what else is or is not required to set). There seems to be a lot of other stuff about using other libraries, but I'd like to keep using GTLRSheetsService (because it lets me use specific result classes like GTLRSheets_ValueRange).