Book Image

Xamarin Cross-Platform Development Cookbook

By : George Taskos
Book Image

Xamarin Cross-Platform Development Cookbook

By: George Taskos

Overview of this book

<p>You can create native mobile applications using the Xamarin Forms platform for the three major platforms iOS, Android, and Windows Phone. The advantage of this is sharing as much code as you can, such as the UI, business logic, data models, SQLite data access, HTTP data access, and file storage across the three major platforms.</p> <p>This book provide recipes on how to create an architecture that will be maintainable, extendable, use Xamarin Forms plugins to boost productivity, customize your views per platforms, and use platform-specific implementations at runtime.</p> <p>We start with a simple creation of a Xamarin Forms solution with the three major platforms. We will then jump to XAML recipes and you will learn how to create a tabbed application page, and customize the style and behavior of views for each platform. Moving on, you will acquire more advanced knowledge and techniques while implementing views and pages for each platform and also calling native UI screens such as the native camera page.</p> <p>Further on, we demonstrate the power of architecting a cross-platform solution and how to share code between platforms, create abstractions, and inject platform-specific implementations. Next, you will utilize and access hardware features that vary from platform to platform with cross-platform techniques. Well then show you the power of databinding offered by Xamarin Forms and how you can create bindable models and use them in XAML. You will learn how to handle user interactions with the device and take actions in particular events.</p> <p>With all the work done and your application ready, you will master the steps of getting the app ready and publishing it in the app store.</p>
Table of Contents (18 chapters)
Xamarin Cross-Platform Development Cookbook
Credits
About the Author
About the Reviewer
www.PacktPub.com
Preface
Index

Authenticating with Facebook and Google providers


One common feature that most of the applications utilize is to allow you to log in with your favorite login provider. We will close this chapter learning how you can leverage the Xamarin.Auth plugin to connect and authenticate two of the most famous login providers, Facebook and Google, to save your users from registering and using custom login steps, and to save your application from extra work in the backend and frontend tiers.

How to do it...

  1. In Xamarin Studio or Visual Studio, create a new cross-platform project; if you don't remember how to, check the Creating a cross-platform solution recipe in this chapter. Name it XamFormsAuthenticateProviders.

  2. Add the plugin in the iOS and Android projects, but don't bother with Windows Phone for now.

  3. Right-click the XamFormsAuthenticateProviders.iOS components folder, Edit Components, search for Xamarin.Auth, and add it to the project. Do the same for the XamFormsAuthenticateProviders.Droid project.

  4. To be able to authenticate your users with Facebook and Google, you will need to get your own client ID and secret from the corresponding developer consoles. For Facebook, go to https://developers.facebook.com, and for Google, got to https://console.developers.google.com.

  5. The process in every platform to get your credentials is almost the same for each OAuth 2.0 provider: you create a new project and retrieve the keys. Refer to the related platform documentation for more information. The tricky part is to not forget to add the same redirect URLs to allow the application to end somewhere after a successful login.

  6. In the XamFormsAuthenticateProviders core project, right-click and choose Add | New Folder; name it Models.

  7. In the newly created folder, add two helper classes: OAuthSettings and ProviderManager.

  8. Add the following code:

    public class OAuthSettings {
      public string ClientId {
        get;
        set;
      }
      public string ClientSecret {
        get;
        set;
      }
      public string AuthorizeUrl {
        get;
        set;
      }
      public string RedirectUrl {
        get;
        set;
      }
      public string AccessTokenUrl {
        get;
        set;
      }
      public List < string > Scopes {
        get;
        set;
      }
      public string ScopesString {
        get {
          return Scopes.Aggregate((current, next) => string.Format("{0}+{1}", current, next));
        }
      }
    
      public OAuthSettings() {
        Scopes = new List < string > ();
      }
    }
    public enum Provider {
        Unknown = 0,
        Facebook,
        Google
      }
    
    public static class ProviderManager {
      private static OAuthSettings FacebookOAuthSettings {
        get {
          return new OAuthSettings {
            ClientId = "YOUR_CLIENT_ID",
            ClientSecret = "YOUR_CLIENT_SECRET",
            AuthorizeUrl = "https://m.facebook.com/dialog/oauth/",
            RedirectUrl = "http://www.facebook.com/connect/login_success.html",
            AccessTokenUrl = "https://graph.facebook.com/v2.3/oauth/access_token",
            Scopes = new List < string > {
              ""
            }
          };
        }
      }
    
      private static OAuthSettings GoogleOAuthSettings {
        get {
          return new OAuthSettings {
            ClientId = " YOUR_CLIENT_ID ",
            ClientSecret = " YOUR_CLIENT_ID ",
            AuthorizeUrl = "https://accounts.google.com/o/oauth2/auth",
            RedirectUrl = "https://www.googleapis.com/plus/v1/people/me",
            AccessTokenUrl = "https://accounts.google.com/o/oauth2/token",
            Scopes = new List < string > {
              "openid"
            }
          };
        }
      }
    
      public static OAuthSettings GetProviderOAuthSettings(Provider provider) {
        switch (provider) {
          case Provider.Facebook:
          {
            return FacebookOAuthSettings;
          }
          case Provider.Google:
          {
            return GoogleOAuthSettings;
          }
        }
        return new OAuthSettings();
      }
    }
  9. Right-click on the core XamFormsAuthenticate project and Add | New Folder; name it Custom Pages.

  10. Right-click on the newly created folder and add two classes, LoginPage and ProvidersAuthPage, and then write or paste-replace the file's contents with the following code:

    public class LoginPage : ContentPage
    {
      public OAuthSettings ProviderOAuthSettings { get; set; }
    
      public LoginPage (Provider provider)
      {
        ProviderOAuthSettings = ProviderManager.GetProviderOAuthSettings (provider);
      }
    }
    
    public class ProvidersAuthPage : ContentPage
    {
      StackLayout stackLayout;
      Button facebookButton;
      Button googleButton;
    
      public ProvidersAuthPage ()
      {
        facebookButton = new Button {
        Text = "Facebook"
      };
      facebookButton.Clicked += async (sender, e) => 
      await Navigation.PushModalAsync(new LoginPage(Provider.Facebook));
    
      googleButton = new Button {
        Text = "Google"
      };
      googleButton.Clicked += async (sender, e) => 
      await Navigation.PushModalAsync(new LoginPage(Provider.Google));
    
      stackLayout = new StackLayout {
        VerticalOptions = LayoutOptions.Center,
        HorizontalOptions = LayoutOptions.Center,
        Orientation = StackOrientation.Vertical,
        Spacing = 10,
        Children = {
          facebookButton,
          googleButton
        }
      };
    
      this.Content = stackLayout;
      }
    }
  11. In the App constructor of the App.cs XamFormsAuthenticate project file, add the following to set the start page:

    MainPage = new ProvidersAuthPage();

Now we need to add the authentication code flow, but if you noticed when we added the Xamarin.Auth components in step 3, there are two different implementations: one for each platform. To accomplish our goal and run specific platform code for each project, we will use the PageRenderer class and an attribute that under the covers uses the Xamarin.Forms DependencyService. You can find more in-depth recipes in this book regarding these topics in Chapter 2, Declare Once, Visualize Everywhere and Chapter 4, Different Cars, Same Engine.

For the Android project

  1. Create a new folder, name it Platform Specific, and add a new class file name LoginPageRenderer. Make it a PageRenderer subclass and add the following code:

    LoginPage page;
    bool loginInProgress;
    
    protected override void OnElementChanged(ElementChangedEventArgs < Page > e) {
        base.OnElementChanged(e);
    
        if (e.OldElement != null || Element == null)
        return;
    
        page = e.NewElement as LoginPage;
    
        if (page == null || loginInProgress)
        return;
    
        loginInProgress = true;
        try {
          // your OAuth2 client id
          OAuth2Authenticator auth = new OAuth2Authenticator(page.ProviderOAuthSettings.ClientId, 
          // your OAuth2 client secret
          page.ProviderOAuthSettings.ClientSecret,
          // scopes
          page.ProviderOAuthSettings.ScopesString,
          //the scopes, delimited by the "+" symbol
          new Uri(page.ProviderOAuthSettings.AuthorizeUrl),
          // the redirect URL for the service
          new Uri(page.ProviderOAuthSettings.RedirectUrl),
          new Uri(page.ProviderOAuthSettings.AccessTokenUrl));
    
          auth.AllowCancel = true;
          auth.Completed += async(sender, args) => {
            // Do something…
            await page.Navigation.PopAsync();
            loginInProgress = false;
          };
    
          auth.Error += (sender, args) => {
            Console.WriteLine("Authentication Error: {0}", args.Exception);
          };
    
          var activity = Xamarin.Forms.Forms.Context as Activity;
          activity.StartActivity(auth.GetUI(Xamarin.Forms.Forms.Context));
        } catch (Exception ex) {
            Console.WriteLine(ex);
          }
      }
  2. We need some using statements to make all this and also a decoration attribute on the namespace.

    using XamFormsAuthenticateProviders;
    using XamFormsAuthenticateProviders.Droid;
    using Xamarin.Forms.Platform.Android;
    using Xamarin.Auth;
    using Android.App;
    
    [assembly: ExportRenderer(typeof(LoginPage), typeof(LoginPageRenderer))]
    namespace XamFormsAuthenticateProviders.Droid

For the iOS project

  1. The same applies here as with the Android platform: create a folder and a new class file named LoginPageRenderer, and make it a subclass of PageRenderer.

  2. This time, we need iOS-related code; you can find it in the following excerpt:

    LoginPage page;
    bool loginInProgress;
    
    protected override void OnElementChanged(VisualElementChangedEventArgs e) {
        base.OnElementChanged(e);
    
        if (e.OldElement != null || Element == null)
          return;
    
        page = e.NewElement as LoginPage;
      }
    
    public override async void ViewDidAppear(bool animated) {
      base.ViewDidAppear(animated);
    
        if (page == null || loginInProgress)
          return;
    
        loginInProgress = true;
        try {
          // your OAuth2 client id
          OAuth2Authenticator auth = new OAuth2Authenticator(page.ProviderOAuthSettings.ClientId,
          // your OAuth2 client secret
          page.ProviderOAuthSettings.ClientSecret,
          // scopes
          page.ProviderOAuthSettings.ScopesString,
          // the scopes, delimited by the "+" symbol
          new Uri(page.ProviderOAuthSettings.AuthorizeUrl),
          // the redirect URL for the service
          new Uri(page.ProviderOAuthSettings.RedirectUrl), 
          new Uri(page.ProviderOAuthSettings.AccessTokenUrl));
    
          auth.AllowCancel = true;
          auth.Completed += async(sender, args) => {
          // Do something…
          await DismissViewControllerAsync(true);
          await page.Navigation.PopModalAsync();
          loginInProgress = false;
          };
    
          auth.Error += (sender, args) => {
            Console.WriteLine("Authentication Error: {0}", args.Exception);
          };
    
          await PresentViewControllerAsync(auth.GetUI(), true);
    
        }catch (Exception ex) {
          Console.WriteLine(ex);
         }
    }
  3. Add the using statements and the ExportRenderer attribute on the namespace.

    using System;
    using Xamarin.Forms.Platform.iOS;
    using Xamarin.Forms;
    using XamFormsAuthenticateProviders;
    using XamFormsAuthenticateProviders.iOS;
    using Xamarin.Auth;
    
    [assembly: ExportRenderer(typeof(LoginPage), typeof(LoginPageRenderer))]
    namespace XamFormsAuthenticateProviders.iOS

The Windows Phone platform! Yes, the forgotten one. You must have noticed that there is no Windows Phone platform component in the Xamarin Component Store. Fear not, you have options. The magic answer is relying on the custom platform-specific PageRenderer that we add for each platform; you can create a platform-specific following the same steps and add your favorite Windows Phone OAuth library logic or you can follow the next steps to try out the experimental alpha channel Xamarin.Auth NuGet package.

  1. Right-click in the Windows Phone project and add the Xamarin.Auth NuGet package. You have to change the channel to Alpha in order to find it.

  2. Create a folder named Platform Specific and add a class file named LoginPageRenderer; subclass it to PageRenderer.

  3. Override the OnElementChanged method and add your code or Xamarin.Auth-related code.

Don't forget to check the code available with this book for the complete picture.

That was a big milestone. Of course, it scratches the surface of access tokens, but we leave that to your preferences. Normally, you would get the access token, capture the expiration date, and persist it somewhere.

How it works…

You just had a taste of the power that Xamarin.Forms can provide you to create not only a unified application with shared code but also customize per platform when necessary. With the use of PageRenderer, you can completely provide your native views and pages.

The first thing we did after we created our solution is to go to the developer console of Facebook and Google, set up a new project, enable the OAuth2 system, retrieve our keys, and add the redirect URL we need to navigate the users after a successful login. In a few words, OAuth and OAuth is an authentication system to verify that this user is actually real and exists in the provider's records. It enables the user to use the same secure credentials from a provider he trusts to give access to the application he is about to use. You can also ask for various scopes and retrieve personal information that he has provided to his trusted provider.

Then we added two helper classes: one to provide us with all the common properties, OAuthSettings, that every provider needs in order to provide you with the desired access token; and a static class, ProviderManager, with a static method accepting a provider enumeration and returning an OAuthSettings instance for the required provider.

Our cross-platform UI consists of two pages: ProvidersAuthPage, which is our main page and has two buttons where in the Clicked event of each we navigate modally to the LoginPage passing the desired provider we need to authenticate our user.

In the LoginPage constructor, we use the provider value to retrieve an instance of OAuthSettings and set to a public property. That's it, everything else is handled in our platform-specific renderers.

Every PageRenderer we added has platform-specific functionality and is essentially our Xamarin.Forms page; so essentially in iOS, a UIViewController; in Android is almost like an Activity, but the lifecycle is a little bit different; and for Windows Phone, the same applies as a PhoneApplicationPage under the hood. There are more renderers that correspond to the type of page you want.

We also decorate our renderers namespace with the ExportRenderer attribute. With this, Xamarin.Forms knows which implementation to load in runtime.

For every PageRenderer, we override OnElementChanged and get the actual Xamarin.Forms page. For iOS, we use the ViewDidAppear iOS-specific method, but for Android we don't have any specific lifecycle methods so we write all our authentication code in the OnElementChanged method.

We capture if the login is in progress in case we dismiss the modal page and OnElementChanged is invoked again to avoid showing again the authentication page.

The Xamarin.Auth plugin has an OAuth2Authenticator class that we instantiate with the information from our OAuthSettings instance that we have in our Xamarin.Forms page ProviderOAuthSettings property that we have access in our renderer. The best of both worlds!

Register the completed event that will be invoked in success and in cancel cases and the error event that will notify us if any error is occurred. With the Auth.GetUI() method, we get a platform-specific object that we can use to show our authentication view. Last but not least, in the Completed event we close the authentication view that we presented on each platform.