Damian Mehers' Blog Xamarin from Geneva, Switzerland.

20Mar/173

Using Google Sign-in for iOS in Xamarin Forms to access Google APIs

This is another of those posts where I am essentially writing a message to my future self to remind myself how to do something, and in the process perhaps help out someone else.

I wanted to use the Google Sign-in for iOS Xamarin Component from Xamarin Forms to let a user sign-in to Google, and then use the resulting access token to invoke one of the Google APIs, in my case the Google Tasks API.

There are several hurdles to overcome:

  • How to use the Google Sign-in for iOS Xamarin Component from Xamarin Forms, since the examples are for iOS apps;
  • How to use that component to request access to the Google Tasks API;
  • How to use the resulting access token to actually invoke the API.

Google Sign-in for iOS Xamarin Component from Xamarin Forms

The Getting Started Guide for the Google Sign-in for iOS Xamarin Component explains how to set up the component for a native Xamarin iOS app.

I followed its instructions with regards to registering on the Google API Console, downloading the GoogleService-Info.plist file, and setting up my AppDelegate:

    public override bool OpenUrl(UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)
    {
      return Google.SignIn.SignIn.SharedInstance.HandleUrl(url, sourceApplication, annotation);
    }

    public override bool FinishedLaunching(UIApplication app, NSDictionary options)
    {

      NSError configureError;
      Google.Core.Context.SharedInstance.Configure(out configureError);
      if (configureError != null)
      {
        // If something went wrong, assign the clientID manually
        Debug.WriteLine("Error configuring the Google context: {0}", configureError);
        Google.SignIn.SignIn.SharedInstance.ClientID = "....apps.googleusercontent.com";
      }

          ...

The instructions with regards to Signing In were trickier though, since they assume access to iOS View Controller.

Xamarin Forms hides such platform-specifics, however this post on Using Custom UIViewControllers in Xamarin.Forms on iOS by Xamarin's Mike Bluestein explains how to get hold of the ViewController by creating a custom renderer for a page.

Assuming your Xamarin Forms main page is called "MainPage" (inspired, I know), I followed Mike's instructions and ended up with a renderer like this:

using System.Diagnostics;
using System.Threading.Tasks;
using Foundation;
using Google.SignIn;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(enfiler.Views.MainPage), typeof(enfiler.iOS.IOSMainPage))]
namespace enfiler.iOS
{
  public class IOSMainPage : PageRenderer, ISignInUIDelegate, ISignInDelegate
  {
    TaskCompletionSource<string> _taskCompletionSource;

    public override void ViewDidLoad()
    {
      Services.GoogleTasks.Instance.GetAccessToken = GetAccessToken;
      base.ViewDidLoad();
    }


    public Task<string> GetAccessToken()
    {
      _taskCompletionSource = new TaskCompletionSource<string>();
      SignIn.SharedInstance.UIDelegate = this;
      SignIn.SharedInstance.Delegate = this;
      SignIn.SharedInstance.Scopes = new string[] { Google.Apis.Tasks.v1.TasksService.Scope.Tasks };
      SignIn.SharedInstance.SignInUser();
      return _taskCompletionSource.Task;
    }

    public void DidSignIn(SignIn signIn, GoogleUser user, NSError error)
    {
      if (error != null)
      {
        _taskCompletionSource.SetException(new NSErrorException(error));
      }
      else
      {
        _taskCompletionSource.SetResult(user.Authentication.AccessToken);
      }
    }
  }
}

When the Xamarin Forms page called MainPage loads, this renderer gets invoked to actually render it on iOS. Since it derives from the builtin PageRender class, it doesn't have to do any of the heavy lifting of rendering, but instead simply registers itself in the Services.GoogleTasks.Instance class in my Xamarin Forms PCL, which we will see later.

Notice how the GetAccessToken does the Sign In work described in the Getting Started guide. It provides for asynchronous invocation and thus uses the TaskCompletionSource class since the sign-in completes via the DidSignIn callback.

One difference from the Getting Started guide is that I'm specifying the Google Tasks OAuth Scope in GetAccessToken. In order to do this I needed to add the Google APIs Client Library nuget package. I also needed to activate the Google Tasks API for my app in the Google API Console.

Notice also that in the DidSignIn I'm completing the task returned from GetAccessToken either with an exception, or with the OAUTH access token resulting from logging in.

Invoking the Google Tasks API with the token returned from the Google Sign-In component

This is the GoogleTasks class with which the IOSMainPage class registered itself by setting the GetAccessToken callback:

using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Google.Apis.Tasks.v1;

namespace enfiler.Services
{
  public class GoogleTasks
  {

    public static GoogleTasks Instance { get; } = new GoogleTasks();
    public async Task<Google.Apis.Tasks.v1.Data.Task> CreateTask(string title, string notes)
    {
      var taskService = new TasksService();
      var task = new Google.Apis.Tasks.v1.Data.Task
      {
        Title = title,
        Notes = notes
      };
      var request = taskService.Tasks.Insert(task, "@default");
      request.OauthToken = await GetAccessToken.Invoke();
      return await request.ExecuteAsync();
    }

    public Func<Task<string>> GetAccessToken { get; set; }
  }
}

I defined this in the Xamarin Forms PCL for my project, and added the Google APIs Client Library nuget package to my PCL too.

The key thing here is the assigning of the OauthToken on the request.

Inside my Xamarin Forms app whenever I want to create a new Google Task I await the invocation of CreateTask which calls back into the custom renderer:

      var googleTask = await Services.GoogleTasks.Instance.CreateTask("Hello To", "Jason Isaacs")

Summary

Google have deprecated the use of Web Views to authenticate with their services and are instead requiring the use of their own libraries, such as the Google Sign-In for iOS library.

By combining the use of the Google Sign-In for iOS Xamarin Component with a custom page renderer, and requesting a custom OAUTH scope I was able to request access to a user's Google Tasks, and then create a task.

I've not yet explored the same thing on Android, but I'd hope to be able to register a callback from my Android code just as on iOS to do the OAUTH dance.

Comments (3) Trackbacks (0)
  1. Great solution! Thank you very much! Just one question, every time I log in to the google account it asks for permission to manage my tasks, its supposed to ask only the first time, but it keeps asking over and over, did you come into that behaviour? Thanks a lot

  2. Thanks for this. Helped me out

  3. Damian, thank you very much for this post, I’m trying to follow your steps, I’ve managed to install the Google Sign in component properly, but when trying to install the Google.Tasks.Apis plugin to my PCL project I’m getting an error:

    Could not install package ‘Microsoft.NETCore.Jit 1.0.2’. You are trying to install this package into a project that targets ‘.NETPortable,Version=v4.5,Profile=Profile111’, but the package does not contain any assembly references or content files that are compatible with that framework. For more information, contact the package author.

    Did you get this problem? And if you did, could you please share how you’ve managed to fix this.

    Thank you!


Leave a comment

No trackbacks yet.