Damian Mehers' Blog Xamarin from Geneva, Switzerland.

11Jun/170

Live coding a Xamarin Forms Custom Circle Control using SkiaSharp

Supports segments of varying colors, radius and degrees, and supports data binding.

Resulting code in GitHub

Filed under: Xamarin No Comments
5Jun/170

Recording of iOS and Android and C# and Xamarin: These are a few of my favorite things

Filed under: Xamarin No Comments
20Mar/174

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.

24Jan/170

A more friendly Xamarin Forms DatePicker

This is a small, simple thing, to make a Xamarin Forms DatePicker more friendly. Instead of showing the date for yesterday, today and tomorrow, as it normally does, it instead shows "yesterday", "today", and, you guessed it, "tomorrow":

iOS: Android:

The view is totally straight-forward, except that instead of just binding the Date in the DatePicker, I also bind the Format:

<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
    xmlns:local="clr-namespace:FriendlyDatePicker" 
    x:Class="FriendlyDatePicker.FriendlyDatePickerPage"
    BindingContext="{x:Static local:ViewModel.Instance}">
    <DatePicker Date="{Binding TheDate}" Format="{Binding DateFormat}"/>
</ContentPage>

The View Model's Format property looks at the date, and returns the appropriate (escaped) string:

namespace FriendlyDatePicker {
  public class ViewModel : INotifyPropertyChanged {
    public static ViewModel Instance { get; } = new ViewModel();
    public event PropertyChangedEventHandler PropertyChanged;
    DateTime theDate = DateTime.Now;

    public DateTime TheDate {
      get {
        return theDate;
      }

      set {
        theDate = value;
        OnPropertyChanged();
        OnPropertyChanged(nameof(DateFormat));
      }
    }

    public string DateFormat {
      get {
        var date = DateTime.Now.Date;
        if (theDate.Date == date) {
          return Escape("Today");
        }
        if (theDate.Date == date.AddDays(1)) {
          return Escape("Tomorrow");
        }
        return theDate.Date == date.AddDays(-1) ? Escape("Yesterday") : "d";
      }
    }

    private string Escape(string s) {
      var result = new StringBuilder();
      foreach (var c in s) {
        result.Append('\\');
        result.Append(c);
      }
      return result.ToString();
    }

    void OnPropertyChanged([CallerMemberName] string propertyName = null) {
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
  }
}

I've noticed that this doesn't work in UWP apps because the DatePicker renders as a native component, and ignores the Format.

It is trivial, but does make for a nicer UX.

That's all folks, nothing more to see, move along now.

Filed under: Xamarin No Comments
27Sep/160

Using Styles and Data Triggers to disable Xamarin forms while waiting

It's a common scenario: You are sending data to a service, or waiting for something to happen, and you don't want the user to interact with your form while that is happening.

untitled-1

The naive approach is to bind the IsEnabled property on your containing Layout to a boolean property in your View Model, but you'll soon find that IsEnabled is not inherited. Setting it on a StackLayout doesn't set it on all the controls embedded within that layout.

Here is a solution which binds the IsRunning property of an ActivityIndicator to a View Model property, and then uses a Style and a DataTrigger to react to the ActivityIndicator's running by setting the IsEnabled properties on the Layouts contained controls:

First I overlay an ActivityIndicator over my form using an AbsoluteLayout:

  <AbsoluteLayout>
    <ActivityIndicator
        IsRunning="{Binding Loading}" HorizontalOptions="Center" VerticalOptions="Center"
        IsVisible="{Binding Loading}" AbsoluteLayout.LayoutBounds="0,0,1,1"
        AbsoluteLayout.LayoutFlags="All" x:Name="ActivityIndicator" />
    <StackLayout Orientation="Vertical"
                 AbsoluteLayout.LayoutBounds="0,0,1,1" AbsoluteLayout.LayoutFlags="All">

Next in my StackLayout I define an explicit Style with a DataTrigger which disables the targeted control and sets its Opacity to 30% when the ActivityIndicator is running:

      <StackLayout.Resources>
        <ResourceDictionary>
          <Style TargetType="View" x:Key="MyBase">
            <!-- Disable controls when the activity indicator is running -->
            <Style.Triggers>
              <DataTrigger
                  TargetType="View"
                  Binding="{Binding Source={x:Reference ActivityIndicator}, Path=IsRunning}"
                  Value="True">
                <Setter Property="Opacity" Value="0.3" />
                <Setter Property="IsEnabled" Value="False" />
              </DataTrigger>
            </Style.Triggers>
          </Style>

You might hope that using an implicit style instead of an explicit style above would affect all views contained within the StackLayout, but it doesn't work like that. There is also the tantalizing and undocumented ApplyToDerivedTypes Style property, but that has no impact that I am aware of.

So instead I create implicit styles for each specific type of control I use inside my StackPanel:

          <!-- Define implicit styles for each control we use. -->
          <Style TargetType="Label" BasedOn="{StaticResource MyBase}" />
          <Style TargetType="Entry" BasedOn="{StaticResource MyBase}" />
          <Style TargetType="Button" BasedOn="{StaticResource MyBase}" />
        </ResourceDictionary>
      </StackLayout.Resources>

At least I'm able to reuse my base style. So here is my final complete view (but I'm not quite done yet):

<?xml version="1.0" encoding="utf-8"?>

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:shared="clr-namespace:LoadingDemo.Shared;assembly=LoadingDemo.Shared"
             BindingContext="{x:Static shared:Locator.MyViewModel}"
             x:Class="LoadingDemo.MainPage">

  <!-- Use an absolute layout to overlay one control over another -->
  <AbsoluteLayout>
    <ActivityIndicator
        IsRunning="{Binding Loading}" HorizontalOptions="Center" VerticalOptions="Center"
        IsVisible="{Binding Loading}" AbsoluteLayout.LayoutBounds="0,0,1,1"
        AbsoluteLayout.LayoutFlags="All" x:Name="ActivityIndicator" />

    <StackLayout Orientation="Vertical"
                 AbsoluteLayout.LayoutBounds="0,0,1,1" AbsoluteLayout.LayoutFlags="All">

      <StackLayout.Resources>
        <ResourceDictionary>
          <Style TargetType="View" x:Key="MyBase">
            <!-- Disable controls when the activity indicator is running -->
            <Style.Triggers>
              <DataTrigger
                  TargetType="View"
                  Binding="{Binding Source={x:Reference ActivityIndicator}, Path=IsRunning}"
                  Value="True">
                <Setter Property="Opacity" Value="0.3" />
                <Setter Property="IsEnabled" Value="False" />
              </DataTrigger>
            </Style.Triggers>
          </Style>

          <!-- Define implicit styles for each control we use. -->
          <Style TargetType="Label" BasedOn="{StaticResource MyBase}" />
          <Style TargetType="Entry" BasedOn="{StaticResource MyBase}" />
          <Style TargetType="Button" BasedOn="{StaticResource MyBase}" />
        </ResourceDictionary>
      </StackLayout.Resources>

      <Label Text="My Label" HorizontalOptions="Center" />
      <Entry Placeholder="Enter text here" />
      <Entry Placeholder="Enter text here" />
      <Entry Placeholder="Enter text here" />
      <Button Text="Click Me" Command="{Binding StartCommand}" HorizontalOptions="Center" />
    </StackLayout>
  </AbsoluteLayout>
</ContentPage>

This is my View Model:

  public class ViewModel : INotifyPropertyChanged {
    private bool _loading;

    public ViewModel() {
      StartCommand = new Command(Start);
    }

    private async void Start() {
      Loading = true;
      await Task.Delay(TimeSpan.FromSeconds(5));
      Loading = false;
    }

    public bool Loading {
      get {
        return _loading;
      }
      private set {
        _loading = value;
        OnPropertyChanged();
      }
    }

    public ICommand StartCommand { get; }

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged([CallerMemberName] string propertyName = null) {
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
  }

When I test the above code in an app I created, everything works swimmingly except for the Button. It doesn't become disabled when I click it.

The reason is that I am using the Command property to execute code in my View Model, and the ICommand interface to which it is bound has its very own CanExecute mechanism to decide when the Button can be clicked.

The solution is in the View Model, and involves raising the CanExecuteChanged event:

    public ViewModel() {
      // When you click the button run the Start method.  The command is available
      // when not loading
      StartCommand = new Command(Start, canExecute: () => !Loading);
    }
...
public bool Loading {
      get { ... }
      private set {
        _loading = value;
        OnPropertyChanged();
        StartCommand.ChangeCanExecute();
      }
    }

Here is the final View Model:

  public class ViewModel : INotifyPropertyChanged {
    private bool _loading;

    public ViewModel() {
      // When you click the button run the Start method.  The command is available
      // when not loading
      StartCommand = new Command(Start, () => !Loading);
    }

    private async void Start() {
      Loading = true;
      await Task.Delay(TimeSpan.FromSeconds(5));
      Loading = false;
    }

    public bool Loading {
      get {
        Debug.WriteLine($"Returning {_loading}");
        return _loading;
      }
      private set {
        _loading = value;
        OnPropertyChanged();
        StartCommand.ChangeCanExecute();
      }
    }

    public Command StartCommand { get; }

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged([CallerMemberName] string propertyName = null) {
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
  }

I like to put my View Models in a separate class library, which has no dependencies on Xamarin Forms, but the Command class comes from Xamarin Forms. Fortunately Xamarin Forms is now open source, so I can "borrow" the Command class' source and embed it within my class library and thus remove the Xamarin Forms dependency.

The complete solution is here in GitHub.

4Sep/163

UWP OAuth in Xamarin Forms using Xamarin.Auth

tl;dr: Complete standalone example here.

I recently wanted to authenticate to Evernote via OAuth in a Xamarin Forms app I'm creating.

There is an excellent Xamarin plugin, called Xamarin.Auth which lets you do the OAuth dance for iOS and Android Xamarin Forms apps, but even in the latest branch, I couldn't get it working on the Universal Windows Platform (UWP) app.

Comments pointed to using a the WebAuthenticationBroker from Microsoft. There are plenty of examples here, unfortunately none for Evernote.

I created and published an Evernote UWP OAuth example using WebAuthenticationBroker based on the Twitter example, which was similar, but not similar enough to be able to just copy/paste.

Once I had this working I was back to Xamarin Forms,and put together a complete standalone example using Google, to log you in to Google and then display your email address and photo. Here it is running in UWP:

uwp 04

I published that example on GitHub.

In order to make Xamarin.Auth work, you create a platform specific page renderer which does the OAuth. I'd already done this for iOS and Android. For Windows I implemented it like this:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks;
using Windows.Security.Authentication.Web;
using Windows.Web.Http;
using Newtonsoft.Json;
using Xamarin.Auth;
using Xamarin.Forms.Platform.UWP;
using XamFormsUWPOAuth;
using XamFormsUWPOAuth.Shared;
using XamFormsUWPOAuth.UWP;

[assembly: ExportRenderer(typeof(AuthenticationPage), typeof(AuthenticationPageRenderer))]

namespace XamFormsUWPOAuth.UWP {
  class AuthenticationPageRenderer : PageRenderer {
    private bool _isShown;

    protected override async void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) {
      base.OnElementPropertyChanged(sender, e);
      if (_isShown) return;
      _isShown = true;

      var code = await AuthenticateUsingWebAuthenticationBroker();
      var account = await ConvertCodeToAccount(code);
      await AuthenticationHelper.FetchGoogleEmailAndPicture(account);
    }


    private async Task<string> AuthenticateUsingWebAuthenticationBroker() {
      var googleUrl = Constants.AuthorizeUrl + "?client_id=" +
                      Uri.EscapeDataString(Constants.GoogleClientId);
      googleUrl += "&redirect_uri=" + Uri.EscapeDataString(Constants.GoogleCallbackUrl);
      googleUrl += "&response_type=code";
      googleUrl += "&scope=" + Uri.EscapeDataString(Constants.Scope);

      var startUri = new Uri(googleUrl);

      var webAuthenticationResult =
        await
          WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None, startUri,
            new Uri(Constants.GoogleCallbackUrl));
      return webAuthenticationResult.ResponseStatus != WebAuthenticationStatus.Success ? null : webAuthenticationResult.ResponseData.Substring(webAuthenticationResult.ResponseData.IndexOf('=') + 1);
    }


    private static async Task<Account> ConvertCodeToAccount(string code) {
      var httpClient = new HttpClient();
      IHttpContent content = new HttpFormUrlEncodedContent(new Dictionary<string, string> {
        {"code", code},
        {"client_id", Constants.GoogleClientId},
        {"client_secret", Constants.GoogleClientSecret},
        {"redirect_uri", Constants.GoogleCallbackUrl},
        {"grant_type", "authorization_code"},
      });
      var accessTokenResponse = await httpClient.PostAsync(new Uri(Constants.AccessTokenUrl), content);
      var responseDict =
        JsonConvert.DeserializeObject<Dictionary<string, string>>(accessTokenResponse.Content.ToString());

      return new Account(null, responseDict);
    }
  }
}

You could use one of the other Microsoft OAuth examples, or your own, in order to do the OAuth.

The AccountStore stuff is a little different. I wanted to reuse the AccountStore goodness that comes with Xamarin.Auth, but I needed a UWP AccountStore implementation. I also needed to ensure my shared code, in my shared project, picked up my UWP AccountStore. I did this by creating a simple container class in the shared project:

using System;
using Xamarin.Auth;

namespace XamFormsUWPOAuth.Shared {
public static class AccountStoreFactory {
    public static Func<AccountStore> Create { get; set; } = () => AccountStore.Create();
  }
}

I use this in order to get at the shared AccountStore throughout my code, rather than using AccountStore.Create() which you'd normally do. In my UWP startup code, I overwrite the default AccountStore (which doesn't exist on UWP anyway) in my App.xaml.cs :

    protected override void OnLaunched(LaunchActivatedEventArgs e) {
      AccountStoreFactory.Create = () => new UWPAccountStore();
      Frame rootFrame = Window.Current.Content as Frame;
              ...

My UWP specific AccountStore implementation was based on this one in the portable-bait-and-switch branch)

It makes use of the Igor Kulman's DataProtectionExtension implementation here.

uwp 01

uwp 03

uwp 04

Check out the full standalone example that works with iOS, Android and of course UWP in my GitHub repository.

Filed under: Xamarin 3 Comments
6Jul/161

Xamarin Media Plugin error: Only one operation can be active at at time

I've been getting System.InvalidOperationException: Only one operation can be active at a time in a Xamarin app I've created which uses the Media Plugin, and finally figured out why I was getting it. I was being spectacularly stupid.

I was triggering the taking of a photo in a form's Appearing event handler

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:behaviors="clr-namespace:QuickNote.Behaviors;assembly=QuickNote"
             xmlns:viewModels="clr-namespace:QuickNote.Shared.ViewModels;assembly=QuickNote"
             BindingContext="{x:Static viewModels:Locator.ExecuteQuickNote}"
             x:Class="QuickNote.ExecuteQuickNotePage" >
  <ContentPage.Behaviors>
    <behaviors:EventToCommandBehavior EventName="Appearing" Command="{Binding LoadCommand}" />
  </ContentPage.Behaviors>
  <StackLayout Orientation="Vertical">
      ...

The relevant line is the binding to the LoadCommand in the view model, which looked like this:

      LoadCommand = new Command(async () => {
        var options = new StoreCameraMediaOptions();
        using(var file = await CrossMedia.Current.TakePhotoAsync(options))
        {
          if (file == null) {
            Debug.WriteLine("No photo");
            return;
          }
          Debug.WriteLine("Got a photo");
        }
      });

The behavior I was seeing was that when the form loaded, the camera started, I took a photo, tapped the Use Photo button and then the app crashed with System.InvalidOperationException: Only one operation can be active at a time.

Can you guess why? I finally realized that after taking the photo it was re-displaying the form, causing the appearing event to be fired again, and thus causing a new photo to be taken while the old one was being taken. Hence the crash. D'oh.

My clue was that I discovered that by inserting a await Task.Yield(); at the start of the LoadCommand delegate, it stopped the crash, but started the camera again after I'd finished taking a photo.

The solution was to add a flag which I checked to ensure I didn't run the command more than once:

      LoadCommand = new Command(async () => {
        if(_loaded) return;
        _loaded = true;
            ...

The error was perfectly correct, I was causing more than one "take photo" operation to be active at the same time, I just didn't realize why.

10Apr/163

Getting Xamarin Xaml Intellisense when the binding context is set in code

I'm working on an app where I navigate from one page to another, passing data by setting the new page's binding context:

Navigation.PushAsync(new QuickNotePage() { BindingContext = quickNote});

When designing the Xaml for QuickNotePage I was pained to see that Intellisense wasn't working, because I wasn't setting the bindingContext for the page in Xaml.

A quick search led me to this page which pre-dates the current version of Xamarin, but nevertheless reminded me of the old design-time namespaces that were auto-generated when I worked on WPF and Silverlight.

This is the Xaml I'm using now to get Intellisense auto-completion and the ability to navigate to properties:

Before:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="QuickNoteForms.QuickNotePage">
    <Label Text="{Binding Title}"/>
</ContentPage>

After:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="QuickNoteForms.QuickNotePage"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             xmlns:quickNoteViewModels="clr-namespace:QuickNote.ViewModels;assembly=QuickNoteForms"
             d:DataContext="{d:DesignInstance quickNoteViewModels:QuickNoteViewModel}">
    <Label Text="{Binding Title}"/>
</ContentPage>

Where QuickNoteViewModel is the ViewModel class, and instance of which I set above when instantiating the page.

Filed under: Xamarin 3 Comments
8Apr/166

Visual Studio missing “Forms Xaml Page” from “Add|New Item” menu using Xamarin

Not sure why this is happening, but its been happening on all my installations of Xamarin with Visual Studio 2015.

All the tutorials and web pages talk about using Project|Add New Item and adding a new "Forms Xaml Page". But whenever I install Xamarin and Visual Studio 2015, I just get the "Forms ContentPage" and "Forms ContentView" which just generate a C# file, no Xaml.

To fix this, I copied XamlPage.zip from

C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\Extensions\Xamarin\Xamarin\4.0.3.214\T\IT\Cross-Platform\Code

to

C:\Users\your name here\Documents\Visual Studio 2015\Templates\ItemTemplates\Visual C#

Finally, it is there:
Screenshot 2016-04-08 11.10.19

Filed under: Xamarin 6 Comments
24Jul/127

Porting a Windows Phone app to iOS

Yes, I know.  It’s not the most common direction.  Creating an app first on Windows Phone, and then porting it to iOS?

In my spare time I recently created and released a Windows Phone app that synchronizes your Google Chrome environment to Windows Phone, to access your Chrome bookmarks, passwords, recently viewed web pages, and (experimentally) open tabs from Windows Phone.

 

It does this by talking the Chrome sync protocol directly to Google’s servers just like Chrome itself does.

I created the app by downloading the Chromium source code, and then building and running Chrome on my PC (Chrome is written in C), working out how it did the synchronization, and then I did the same thing from C# in my Windows Phone app.

I released the app and it was well received.

The next step in my master-plan was to release a similar app for the iPhone and iPad, by porting my app using MonoTouch to iOS.  I got to the point where it was working, and then, well … Google released Chrome for iOS … at which point the potential audience for my iOS product shrank to approximately zero.

Nevertheless I did get to port an app from Windows Phone to iOS using MonoTouch;  I thought I’d share my experience.

I’m going to:

  1. Explain how I set up my development environment to run Visual Studio on my Mac, with just a three-finger swipe to go between Visual Studio and the real-device debugger;
  2. Describe how I structured my project to share code between the two apps;
  3. Explain how I implemented different database access code, hidden behind a common interface;
  4. Look at a significant hurdle I hit where my code ran fine in the iPhone Simulator, but crashed and burned on a real device, and what I did to resolve this;
  5. Reflect on the overall experience.

About MonoTouch

First a word about MonoTouch.  If you are like me, you hate the idea of a porting framework because you want to create an app that has a native look and feel … not some generic bland UI that looks the same on all platforms, and is thus horrible on all platforms.

Here is what you need to know about MonoTouch: it provides C# bindings to the native iOS frameworks.  It does not provide any UI compatibility layer to let you run Silverlight on iOS.  You still design your UI using NIB files, create outlets, ViewControllers etc.  You use MonoTouch to create a native app, that is indistinguishable from an app coded in Objective C.

So if you can’t port the UI, what is the point?  It turned out that most of the challenging code in my app was the backend stuff – authenticating, syncing, storing in the DB, etc.  The UI was pretty straightforward.  I wanted to port the backend code, but put an authentic iOS UI on it.

Learning iOS and MonoTouch

A few years ago Red Gate software acquired a product I created and consequently am a Friend of Red Gate.  One of the perks was a free years subscription to the online video course company, Pluralsight.

Before doing anything with MonoTouch I watched the available Pluralsight courses on iOS and MonoTouch.  On most devices, such as the iPad you can watch them at 1.5x or even 2x the normal speed.  I found these courses to be excellent, and I now pay out of my own pocket to subscribe.

The half-life of the information gleaned through watching these videos is very short in my brain, so I needed to get my hands dirty very quickly after watching the videos.

Setting up the development environment

Although I did not know much about MonoTouch development, I did know that I wanted to continue using Visual Studio, and more specifically the Resharper development/refactoring tool from Jetbrains: .NET development without Resharper is unthinkable for me.

One other thing I knew was that I didn’t want to fork over US$200 for a MonoTouch license without being sure that what I wanted to do would work.  Fortunately you can download and use MonoTouch for free, but you can only deploy apps to the iOS Simulator – not to real devices.  This seemed good enough to me. I thought that if it worked on the emulator, it was very likely to work on a real device.

Little did I know how naïve I was.

Windows on Mac

I already had a MacBook Air running OS X, and Parallels hosting Windows 7.  I also already had Visual Studio installed within Windows 7 and Resharper installed.

MonoTouch

I downloaded and installed MonoTouch on the OS X environment, and made sure I could build and run a simple project.  Then I followed the instructions in this email to set up my Windows and Visual Studio environment to be able to edit MonoTouch projects.

Visual Studio and MonoTouch together

I’ve read a lot of stuff about people using Dropbox to automatically synchronize their PC based Visual Studio with their Mac based MonoTouch.  Instead what I did was simply to open the MonoTouch solution from within Visual Studio running in the virtual machine on the same PC, using the ability to open the host OS’s files within the VM.

I set up the Mac’s file system to be available inside Windows:

image

I created a new solution using MonoDevelop on MacOS:

image

Then I opened that solution using Visual Studio running in the parallels Virtual Machine, via the Mac’s drive mounted in the Windows Virtual Machine (notice the drive on the left hand side):

image

I ended up being able to edit and build using Visual Studio, then use a four-finger swipe on the mousepad to switch back to MonoTouch to run and debug the app.  Here is a quick video of the complete edit, debug run cycle using MonoDevelop to run the app under an iPhone simulator on iOS, and Visual Studio to develop:

 

Using Visual Studio to develop, and then MonoTouch to deploy and debug was almost totally painless.  I still needed to learn MonoTouch's debugger shortcuts, but that was the only pain-point.

Porting the code

The Windows Phone project structure

The original version of the Windows Phone project was not designed with the idea of porting it to iOS, however I did use the standard MVVM pattern, which meant that my sync logic and database code was totally decoupled from my UI code.

I used two different Visual Studio solutions, however the iOS solution references the same source control folders as the Windows Phone Solution for the shared classes.  These are the classes that are shared between the solutions:

image

The Engine namespace contains the classes used to talk the Chrome sync protocol to Google’s servers.  The Models namespace contains the classes used to represent entities written to, and read from the database.  The proto folder contains protocol-buffer definitions and generated classes, and the ProtocolBuffers folder contains the engine used to talk the protocol buffers protocol.  All of these classes are shared between the Windows Phone and iOS versions of the app.

Almost all my non-UI code could be reused between Windows Phone 7, and iOS, however there were a couple of areas where I needed to re-write code, namely storage of Settings, and Database code, which I hide behind interfaces (see IDatabase, IDatabaseFactory, and ISyncOptions in the picture above).

Database access across platforms

Although I love using LINQ, Microsoft’s recent announcement that Windows Phone 8 will support SQLite was very welcome, since if I’d used SQLite on Windows Phone, my database code would have remained unchanged.  For this app, I ended up re-writing the database read/write code, with different implementations of an IDatabase interface used by the sync engine.

I use LINQ to SQL as my database implementation on Windows Phone, and I wanted to re-use the same database entities on iOS, even if they were stored using a different technology, namely SQLite.  I ended up using #IFs to allow me to use the same classes between both iOS and Windows Phone.

I’m not going to go into all the details of what I did, but I thought I’d give you a flavour by looking at the class used to represent encryption keys exchanged during synchronization.  I’ll show an extract of the class itself, and then the two different IDatabase implementations which read/write instances of these classes.

Shared database entity class

This is an example of the NigoriModel class, used to represent encryption keys. Note the #IFs used for Windows Phone specific classes. You’ll also see that I have not commented out the use of the Table and Column attributes – I simply defined my own TableAttribute class, #IFd to be only visible when building for iOS.

I used the Windows Phone ProtectedData class to encrypt sensitive information prior to committing it to the database.

using System;
using System.ComponentModel;
#if WINDOWS_PHONE
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Security.Cryptography;
#endif
namespace Chromarks.Models {
    [Table]
    public class NigoriModel : INotifyPropertyChanged
#if WINDOWS_PHONE
        , INotifyPropertyChanging
#endif
    {
        private int _id;
 
        [Column(IsPrimaryKey = true, IsDbGenerated = true, DbType = "INT NOT NULL Identity", 
            CanBeNull = false, AutoSync = AutoSync.OnInsert)]
        public int Id
        {
            get
            {
                return _id;
            }
            set
            {
                if (_id != value)
                {
                    NotifyPropertyChanging("Id");
                    _id = value;
                    NotifyPropertyChanged("Id");
                }
            }
        }
 
        private byte[] _userKeyEncrypted;
 
        [Column]
        public byte[] UserKeyEncrypted
        {
            get { return _userKeyEncrypted; }
            set
            {
                if (_userKeyEncrypted != value)
                {
                    NotifyPropertyChanging("UserKeyEncrypted");
                    _userKeyEncrypted = value;
                    NotifyPropertyChanged("UserKeyEncrypted");
                }
            }
        }
 
        private static byte[] Encrypt(byte[]  plain) {
            byte[] bytes = null;
#if WINDOWS_PHONE
            bytes = ProtectedData.Protect(plain, null);
#else
            bytes = plain; // TODO: implement for iOS
#endif
            return bytes;
        }
 
        public byte[] UserKey
        {
            get { return Decrypt(UserKeyEncrypted); }
 
            set {
                UserKeyEncrypted = Encrypt(value);
            }
        }
 
                ...
        
        // Version column aids update performance.
#if WINDOWS_PHONE
        [Column(IsVersion = true)]
        private Binary _sqlVersion;
#endif
 
        #region INotifyPropertyChanged Members
 
        public event PropertyChangedEventHandler PropertyChanged;
 
        // Used to notify the page that a data context property changed
        protected void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
 
        #endregion
 
        #region INotifyPropertyChanging Members
 
#if WINDOWS_PHONE
        public event PropertyChangingEventHandler PropertyChanging;
#endif
        // Used to notify the data context that a data context property is about to change
        protected void NotifyPropertyChanging(string propertyName)
        {
#if WINDOWS_PHONE
            if (PropertyChanging != null)
            {
                PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
            }
#endif
        }
 
        #endregion
    }
}

 

In this way I was able to use the same classes in my synchronization engine, whether running on iOS or Windows Phone.  Since all database access was hidden behind the IDatabase interface, all I needed to do was provide the sync engine with different IDatabase implementations depending on the platform:

Windows Phone 7 IDatabase implementation (LINQ to SQL)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Chromarks.Engine;
using Chromarks.Models;
 
namespace Chromarks.ViewModels
{
 
    class DatabaseImpl : IDatabase {
        private const String Tag = "DatabaseImpl";
        private readonly ChromarksDataContext _dataContext;
        
        public DatabaseImpl(ChromarksDataContext dataContext)
        {
            _dataContext = dataContext;
        }
 
        public void Dispose()
        {
            _dataContext.Dispose();
        }
 
        public void SubmitChanges()
        {
            _dataContext.SubmitChanges();
        }
 
        public bool AnySyncProgress()
        {
            return _dataContext.SyncProgress.Any();
        }
 
        public NigoriModel GetNigoriWithName(string keyName)
        {
            try
            {
                return _dataContext.Nigoris.SingleOrDefault(n => n.KeyName == keyName);
            }
            catch (Exception ex)
            {
                Log.Error(Tag, "Error invoking GetNigoriWithName with " + keyName, ex);
                return null;
            }
        }
 
        public void InsertNigori(NigoriModel nigori)
        {
            _dataContext.Nigoris.InsertOnSubmit(nigori);
        }
                ...
iOS IDatabase implementation (SQLite)

I replicated the Windows Phone behaviour in the iOS implementation, using equivalent mechanisms from SQLite.

using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Text;
using Chromarks.Engine;
using Chromarks.Models;
using Mono.Data.Sqlite;
using sync_pb;
 
// ReSharper disable CheckNamespace
namespace Chromarks {
// ReSharper restore CheckNamespace
    internal class Database : IDatabase {
        private readonly SqliteConnection _connection;
 
        private SqliteTransaction _transaction;
        private bool _disposed;
 
        internal Database()
        {
            _connection = GetConnection();
            _connection.Open();
        }
        
        public void Dispose()
        {
            Debug.Assert(!_disposed);
            _disposed = true;
            if(_transaction != null) {
                _transaction.Rollback();
                _transaction = null;
            }
            _connection.Dispose();
        }
 
 
        public void SubmitChanges()
        {
            Debug.Assert(!_disposed);
            if (_transaction != null) {
                _transaction.Commit();
                _transaction = null;
            }
        }
 
        public bool AnySyncProgress()
        {
            Debug.Assert(!_disposed);
            using (var cmd = _connection.CreateCommand())
            {
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = "select COUNT(*) FROM SyncProgressModel;";
                var count = (long)cmd.ExecuteScalar();
                return count > 0;
            }
        }
        
        public NigoriModel GetNigoriWithName(string keyName)
        {
            Debug.Assert(!_disposed);
            using (var cmd = _connection.CreateCommand())
            {
                cmd.CommandType = CommandType.Text;
                cmd.CommandText =
@"SELECT [UserKeyEncrypted], [MacKeyEncrypted], [EncryptionKeyEncrypted] FROM [NigoriModel] WHERE " +
                    "[KeyName] = @KeyName";
                Log.Debug("Database", cmd.CommandText);
                AddParameter(cmd, "@KeyName", keyName);
                using (var reader = cmd.ExecuteReader())
                {
                    if (!reader.Read())
                    {
                        return null;
                    }
                    var result = new NigoriModel
                    {
                        UserKeyEncrypted = (byte[])reader["UserKeyEncrypted"],
                        MacKeyEncrypted = (byte[])reader["MacKeyEncrypted"],
                        EncryptionKeyEncrypted = (byte[])reader["EncryptionKeyEncrypted"],
                    };
                    return result;
                }
            }
        }
 
        public void InsertNigori(NigoriModel nigori)
        {
            Debug.Assert(!_disposed);
            using (var cmd = _connection.CreateCommand())
            {
                cmd.CommandType = CommandType.Text;
                cmd.CommandText =
@"INSERT INTO [NigoriModel] ([KeyName],[UserKeyEncrypted],[MacKeyEncrypted],[EncryptionKeyEncrypted])" +
                   "VALUES (@KeyName, @UserKeyEncrypted, @MacKeyEncrypted, @EncryptionKeyEncrypted);";
                Log.Debug("Database", cmd.CommandText);
                AddParameter(cmd, "@KeyName", nigori.KeyName);
                AddParameter(cmd, "@UserKeyEncrypted", nigori.UserKeyEncrypted);
                AddParameter(cmd, "@MacKeyEncrypted", nigori.MacKeyEncrypted);
                AddParameter(cmd, "@EncryptionKeyEncrypted", nigori.EncryptionKeyEncrypted);
                cmd.ExecuteNonQuery();
            }
        }
 
                ...

Running the app

The iOS Simulator

I was amazed and delighted to find that all the networking code just compiled and ran using MonoTouch.

Once I got the database implementation working on iOS, I ran a simple iOS app using my Chrome sync email address, password and application-specific password (I have that option turned on for my account).  It worked – I was able to communicate to Google’s servers and dump out my synchronized bookmarks.

A real device

So far this was all done using the iOS emulator, but I was on a high – I took out my credit card and paid to buy a license to use MonoTouch on physical devices instead of just virtual devices.  I also paid to become a registered Apple iOS developer. There are very thorough instructions on how to set up your real-world iPhone as a developer device.

I rushed through the setup instructions, deployed my app to the iPhone, ran it and … it crashed.  The same code that had run fine on the emulator failed on the real device.

Generic Functions – the problem

Turns out I should have read those warnings and release notes, rather than just diving in.  One of the restrictions that MonoTouch faces is that it can not dynamically generate code at runtime, and one of the C# constructs that requires this generic functions.  And guess what, the protocol buffers code made liberal use of generic functions, such as this:

        /// <summary>
        /// Reads an enum field value from the stream. If the enum is valid for type T,
        /// then the ref value is set and it returns true.  Otherwise the unkown output
        /// value is set and this method returns false.
        /// </summary>   
        [CLSCompliant(false)]
        public bool ReadEnum<T>(ref T value, out object unknown)
            where T : struct, IComparable, IFormattable, IConvertible
        {
            int number = (int)ReadRawVarint32();
            if (Enum.IsDefined(typeof(T), number))
            {
                unknown = null;
                value = (T)(object)number;
                return true;
            }
            unknown = number;
            return false;
        }
Generic Functions – the solution

I wrote new functions to be non-generic:

        public bool ReadEnumNonGeneric(Func<object, bool> isEnum, Action<int> setEnum, 
                                       Action<object> setUnknown)
        {
            int number = (int)ReadRawVarint32();
            if (isEnum(number)) {
                setUnknown(null);
                setEnum(number);
                return true;
            }
            setUnknown(number);
            return false;
        }

… and changed the calling code to invoke my non-generic functions:

// if(input.ReadEnum(ref result.deviceType_, out unknown)) {
if (input.ReadEnumNonGeneric(n => Enum.IsDefined(typeof(global::sync_pb.SessionHeader.Types.DeviceType), n), 
                             n => result.deviceType_ = (global::sync_pb.SessionHeader.Types.DeviceType)n, 
                             u => unknown = u))

Now my code not only compiled, but it also ran!

MonoTouch compiler crash – not a problem

One issue that I never got to the bottom of is that the MonoTouch compiler crashed when compiling my code.  My solution was to always compile under Windows, and then let MonoTouch transform the compiled code into an iOS app, and run it.  I suspect that it is the fact that I left the generic functions there that causes the MonoTouch compiler to crash.

Conclusion

Although Google cold-heartedly destroyed my ambitions to release a Chrome-syncing app for iOS when they released Chrome, I still got a lot out of the experience of porting my app from Windows Phone, and I’m ready now for the next one.

Here are some final thoughts.

  • Being able to program in C#, and having a lot of the .NET framework library available is fantastic if you are an experienced .NET programmer
  • You’ll still need to invest significant effort into familiarizing yourself with the iOS programming frameworks, especially the UI to provide a truly native experience
  • There are restrictions to the magic that MonoTouch can do - When your app works on the Simulator but not on the real device, don’t despair – read the FM and re-write your code to work around the restrictions.  Better yet, read about the restrictions before you code.
  • Its worth investing in getting your development environment set up properly – it was a joy to be able to edit, refactor, build in Visual Studio and then just swipe Visual Studio out of the way and run and debug the app, all on the same MacBook Air
Filed under: Chrync, iOS, WP7, Xamarin 7 Comments