Xamarin Forms, Prism and Platform Services

One question that I have had a few times now is how do I integrate platform specific implementations of services into the Prism Framework? It’s not hard, in fact, you don’t have to do anything at all! Let’s take a look at how it works. I am going to focus on the Unity container: it’s my favorite for no other reason than it was the first container I started using and it has always worked well for me across all different .NET projects. The other containers have similar capability.

Recap Abstracting Platform Services

All the platforms have very similar capabilities, especially in the case of Bluetooth, Bluetooth LE, GPS, text-to-speech etc., but each of these capabilities requires a platform specific process of consuming them. I am sure most know that an interface needs to be  created the portable/shared project and then platform specific implementations in each of the platform projects. But here is a quick recap of what it looks like.

Portable/Shared Project

Create your Xamarin Forms project and use nuget to add Prism.Unity.Forms. The latest stable release at the time of writing was 6.3.0. Once you have your project created, you would need to create your interface definition. Everyone loves to use text-to-speech for these kinds of samples, so let’s do that.


public interface ITextToSpeech
{
    void SayIt(string text);
}

For each of the platforms the parts that we are interested in for the sake of the article is pretty much the same. I won’t reproduce the actual speech code here, but if you are interested in how it is done, there is a good sample on the Xamarin developer site.

Platform Projects

Here is what the class looks like for each of the platforms.


///usings up here
[assembly: Dependency(typeof(AndroidTextToSpeech))]
namespace Sample.Droid
{
    public class AndroidTextToSpeech : ITextToSpeech
    {
        public void SayIt(string text)
        {
            /// do implementation here
        }
    }
}

/// usings up here
[assembly: Dependency(typeof(IosTextToSpeech))]
namespace Sample.iOS
{
    public class IosTextToSpeech : ITextToSpeech
    {
        public void SayIt(string text)
        {
            // do implementation here
        }
    }
}
/// usings up here
[assembly: Dependency(typeof(UwpTextToSpeech))]
namespace Sample.Uwp
{
    public class UwpTextToSpeech : ITextToSpeech
    {
        public void SayIt(string text)
        {
            // do implementation here
        }
    }
}

As promised, each of the platform implementations look the same and this is the pattern you would follow even if you weren’t using a framework like Prism. The key is the attribute just above the namespace statement that registers your implementation with the Xamarin Forms dependency service.

Consuming in ViewModel

Normally we would consume the service in the view model using the following pattern:

public class MyViewModel : BindableBase
{
    private ITextToSpeech _speechService = null;

    public MyViewModel()
    {
        _speechService = DependencyService.Get<ITextToSpeech>();
    }
}

The above looks ok, but it does tie your view model to the Xamarin Forms DependencyService. This makes things a little more painful for testing.

What Prism Does For You

What Prism does for you is remove that dependency on the Xamarin Forms dependency service and allow you to pass in the service as a constructor parameter like follows:

public class MyViewModel : BindableBase
{
    private ITextToSpeech _speechService = null;

    public MyViewModel(ITextToSpeech speechService)
    {
        _speechService = speechService;
    }
}

How does Prism do that? It creates a special extension to the Unity container (similar capabilities exist within the other containers) that basically say to the container if the requested interface isn’t registered in the container, look for it in the Xamarin Forms DependencyService. That’s pretty cool! No additional work is required and you right away have a more testable and maintainable solution. If you are interested in seeing what this looks like, you can look at the
DependencyServiceStrategy class and the ConfigureContainer method in the PrismApplication class.

 

Xamarin Forms, Autodesk Forge, iOS Deep-Linking

Alright, last time we looked at UWP, and now we will look at iOS. It is a pretty similar effort to UWP. All we need to do is declare the protocol handling and then handle the URL.

Declaring the Protocol

First we have to edit the info.plist file in the iOS project. This file needs to be modified by hand and is just a simple XML file. The plist element is the root element and contains a “dict” object. Each element of the dict object is a setting for the app and it is contained in pairs. For example:

<plist version="1.0">
<dict>
    <key>UIDeviceFamily</key>
    <array>
        <integer>1</integer>
        <integer>2</integer>
    </array>

    ... other settings
</dict>
</plist>

At the end of the file, we can add our protocol declaration. It will look like this:

<plist version="1.0">
<dict>
    ... other settings

    <key>CFBundleURLTypes</key>
    <array>
        <dict>
            <key>CFBundleURLName</key>
            <string>same.asyour.bundle.identifier</string>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>bbsw-fm</string>
            </array>
        </dict>
    </array>
</dict>
</plist>

Handling the URL Link

This is almost exactly like how we did it in the UWP platform project. In the AppDelegate class, we need to override the OpenUrl method and delegate back to the portable Application object.


public partial class AppDelegate : global:Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{

    public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
    {
        DeepLink.App thisApp = (DeepLink.App) Prism.Unity.PrismApplication.Current;
        thisApp.UwpIOSOnAppLinkRequestReceived(new Uri(url.ToString()));
        return true;
    }
}

All that is happening above is that when the app is activated because of the protocol that it has registered, it will look at the URL and extract the response code that was returned by Forge. It will then use that response code in a REST API call to get the authorization token and refresh token. If that is successful, we perform a navigation to the next page in our app. Obviously the code isn’t complete as it doesn’t handle any kinds of errors. For the details on how to get the authorization and refresh tokens see the previous post.

Next up and finally will be the Android implementation.

Xamarin Forms UWP Deep-Linking

Introduction

In our last post, we looked at what we needed to do to setup our shared/portable project to authenticate with Autodesk Forge. Almost all of the code the important code is in the shared area, but we do have to do somethings within each of the platform projects. Lets check out UWP.

Protocol Declaration

The first thing that I do is go into the Package.appxmanifest in the UWP project. Once you are in it, click on the “Declarations” tab. On this tab, you can declare lots of different things for your app: able to pick files, camera settings, background tasks among others. The one we are interested in is protocol.

Select protocol and add it to the list of the supported declarations. Give the protocol a name check “ExecutableOrStartPageIsRequired”. For the name, make sure that you give it something unique. Perhaps an abbreviation of your company followed by some kind of app designation.

packageappxmanifest

Override UWP Application.OnActivated

In the UWP platform project, you need to override the Application.OnActivated function to handle the deep-link. Normally Xamarin Forms will just route the URI request to OnAppLinkRequestReceived, but it doesn’t seem to be working for UWP (or iOS). So what we will do, is just call in to a new entry point which then delegates to the protected function.


/// UWP application object
sealed partial class App : Application
{
    // ... other stuff here

    protected override async void OnActivated(IActivatedEventArgs args)
    {
        base.OnActivated(args);

        if (args.Kind == ActivationKind.Protocol)
        {
            var protocolArgs = args as ProtocolActivatedEventArgs;
            DeepLink.App thisApp =
                (DeepLink.App) Prism.Unity.PrismApplication.Current;
            thisApp.UwpIOSOnAppLinkRequestReceived(protocolArgs.Uri);
        }
    }
}

Then we can go back to the application object in our portable library and fix up the OnAppLinkRequestReceived function and the manual entry point we show above that is used for UWP and iOS.


public partial class App : PrismApplication
{
    /// the rest of the class is up here ...

    protected override async void OnAppLinkRequestReceived(Uri uri)
    {
        base.OnAppLinkRequestReceived(uri);
        bool retrievedToken = await GetAccessTokenAsync(uri.ToString());
        if (retrievedToken)
            await NavigationService.NavigateAsync(Pages.Test);
    }

    public void UwpIOSOnAppLinkRequestReceived(Uri uri)
    {
        OnAppLinkRequestReceived(uri);
    }

}

All that is happening above is that when the app is activated because of the protocol that it has registered, it will look at the URL and extract the response code that was returned by Forge. It will then use that response code in a REST API call to get the authorization token and refresh token. If that is successful, we perform a navigation to the next page in our app. Obviously the code isn’t complete as it doesn’t handle any kinds of errors. For the details on how to get the authorization and refresh tokens see the previous post.

And that is it for UWP, just a little bit of code and we are back in our shared code base. Up next will be iOS.

Custom Map Renderer: iOS

It Finally Happened. I picked up my first Mac computer in January. I am now the proud owner of a MacBook Pro 13, no touch bar and the regular keyboard. I went into the local Apple Store and tried out the newer keyboard and the older keyboard and have to say that I liked the feel of the old one better. I really didn’t like how loud the new keyboard was. But the old keyboard was good, trackpad was good (but not as good as I thought it was going to be) and the screen was nice. Overall a nice computer if expensive. But the important thing is that I could finally install XCode and build iOS apps!

And that is the subject of the this blog post. In the previous blog post, we looked at creating a custom renderer for Android. Now we will look at doing the same with iOS. With iOS, things are simpler in at least one regard: you don’t have to go get an access key for your app.

One thing I want to make clear after some questions from others. This information isn’t a whole lot different than what you can get from developer.xamarin.com. But it builds on those examples by making the custom map renderer updatable from your view model … if you are using data binding of course. If you aren’t using data binding, you can skip all of this and just follow the guide in the above link.

If you are still here …

I didn’t change anything in my CustomMap class in the shared project. All updates are in the iOS project. In here I have defined two classes: CustomMKAnnotationView and CustomMapRenderer. Let’s talk about CustomMKAnnotationView class first: it is the simpler.

The CustomMKAnnotationView class derives from MKAnnotationView. It adds a couple of new properties for the Id of the marker and a URL associated with the marker. Otherwise there is not a lot to be excited about. So lets move onto the CustomMapRenderer class.

The CustomMapRenderer class derives from MapRenderer. The first thing is that we have to override the OnElementChanged method. Just like Android, this either hooks up the native map to the cross platform map class in the shared project or unhooks it.


protected override OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.View> e)
{
    base.OnElementChanged(e);

    if (e.OldElement != null)
    {
        //... unhook events, native conrols etc
    }

    if (e.NewElement != null)
    {
        _customMap = (CustomMap) e.NewElement;
        _nativeMap = Control as MKMapView;
        _customPins = _customMap.CustomPins;

        _nativeMap.GetViewForAnnotation = GetViewForAnnotation;
        _nativeMap.CalloutAccessoryControlTapped +=
            OnCalloutAccessoryControlTapped;
        _nativeMap.DidSelectAnnotationView +=
            OnDidSelectAnnotationView;
        _nativeMap.DidDeselectAnnotationView +=
            OnDidDeselectAnnotationView;
    }
}

The most important things above are that you get a reference to our CustomMap object that was defined in the shared project; and also get a reference to the native map object (MKMapView).

Using the native map object from above, we can hook into events for it. We use these events to display the pins on the map and the info window when a user taps one of the pins.

Let’s take a look at the GetViewAnnotation method. This is the method that is used to show a custom marker on the map. In it we are passed in a reference to the marker. We use our GetCustomPin method to look up the information associated with the marker. In an effort to conserve memory, the map can reuse the view: and that is what is happening with the call to DequeueReusableAnnotation. If we can’t reuse, then we create a new one, specifying the image for the pin itself, and customizations for the callouts.


private MKAnnotationView GetViewForAnnotation(
    MKMapView mapView, IMKAnnotation annotation)
{
    if (annotation is MKUserLocation)
        return null;

    MKAnnotationView ret = null;

    var pointAnnotation = annotation as MKPointAnnotation;
    var customPin = GetCustomPin(pointAnnotation);
    if (customPin == null)
        throw new Exception("My pin not found");

    ret = mapView.DequeueReusableAnnotation(customPin.Id.ToString());
    if (ret == null)
    {
        ret = new CustomMKAnnotationView(
            annotation,
            customPin.Id.ToString(), customPin.Url);
        ret.Image = UIImage.FromFile("custompin2.png");
        ret.CalloutOffset = new CoreGraphics.CGPoint(0, 0);
        ret.LeftCalloutAccessoryView =
            new UIImageView(UIImage.FromFile("custompinimage.png"));
        ret.RightCalloutAccessoryView =
            UIButton.FromType(UIButtonType.DetailDisclosure);
        ((CustomMKAnnotationView) ret).Id = customPin.Id.ToString();
        ((CustomMKAnnotationView) ret).Url = customPin.Url;
    }

    ret.CanShowCallout = true;

    return ret;
}

Next we can take a quick look at the OnDidSelectAnnotationView. This is called when the user taps on the pin. You have the option of doing something custom … or just select the default that was setup in the GetAnnotationView function. In our case, we are going to see if the marker is associated with ID 1, and if it is, add in a custom image. Otherwise, we are just going to accept the default.


private void OnDidSelectAnnotationView(
    object sender,
    MKAnnotationViewEventArgs e)
{
    var customView = e.View as CustomMKAnnotationView;
    _customPinView = new UIView();

    if (customView.Id == "1")
    {
        _customPinView.Frame = new CoreGraphics.CGRect(0, 0, 200, 84);
        var image = new UIImageView(new CoreGraphics.CGRect(0, 0, 200, 84));
        image.Image = UIImage.FromFile("custommapimage.png");
        _customPinView.AddSubview(image);
        _customPinView.Center =
            new CoreGraphics.CGPoint(0, -(e.View.Frame.Height + 75));
        e.View.AddSubview(_customPinView);
    }
}

The OnDidDeselectAnnotationView method is called when the extended annotation is displayed and the user taps somewhere on the map, it will get rid of the extended information and free up the resources.


private void OnDidDeselectAnnotationView(
    object sender,
    MKAnnotationViewEventArgs e)
{
    if (!e.View.Selected)
    {
        _customPinView.RemoveFromSuperview();
        _customPinView.Dispose();
        _customPinView = null;
    }
}

Finally, let’s take a look at OnCalloutAccessoryControlTapped. After the user taps on the pin, extra data is displayed about the pin. If you tap on the extra data, this function is called. You can look at the information associated with the pin and act on it. In our case, we are going to grab the url and navigate to it.


private void OnCalloutAccessoryControlTapped(
    object sender,
    MKMapViewAccessoryTappedEventArgs e)
{
    var customView = e.View as CustomMKAnnotationView;
    if (!String.IsNullOrWhiteSpace(customView.Url))
    {
        UIApplication.SharedApplication.OpenUrl(
            new Foundation.NSUrl(customView.Url));
    }
}

And now just for a recap. If we want to make the custom map respond to our view model we have to listen for changes from the data binding system. This is almost exactly the same as the Android implementation where we override the OnElementPropertyChanged and listen for our property.


protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    base.OnElementPropertyChanged(sender, e);

    if (Element == null || Control == null)
        return;

    if (e.PropertyName == View.CustomMap.CustomPinsProperty.PropertyName)
    {
        UpdatePins();
    }
}

private void UpdatePins()
{
    if (_nativeMap == null)
        return;

    foreach (var p in _customMap.CustomPins)
    {
        MKPointAnnotation pa = new MKPointAnnotation
        {
            /// check git hub
        }

        _nativeMap.AddAnnotation(pa);
    }
}

Don’t forget, the CustomPins is bound in our XAML page.

I find it pretty amazing that Xamarin Forms is able to share so much of the code. Really all we are doing in the separate projects is the presentation: all of the business logic is shared between all. Very cool.

Find all the code in my GitHub under the XamarinPrism repository. Find it under the MapRendererIos solution.

Custom Map Renderer on Android: Part 3

This is the final post in the series about custom map renderers on Android. To recap, we added a map page to our application and then customized the appearance of the map markers and the info window that is shown when the user taps a marker.

Now we are going to make the map a bit more usable by making it possible to update the pins from the view model, and making it easy to consume services in the Android specific project.

In the previous post, we defined a CustomMap control that only had one property:


public class CustomMap : Map
{
    public List<CustomPin> CustomPins { get; set; }
}

The property above is only available from the ContentPage code behind. We are going to change this to be a bindable property that can be bound in the view model itself.


public class CustomMap : Map
{
    public static readonly BindableProperty CustomPinsProperty =
        BindableProperty.Create(
            "CustomPins",
            typeof(List<CustomPin>),
            typeof(CustomMap),
            new List<CustomPin>(),
            BindingMode.TwoWay);

    public List<CustomPin> CustomPins
    {
        get { return (List<CustomPin>) GetValue(CustomPinsProperty); }
        set { SetValue(CustomPinsProperty, value);
    }
}

Now that CustomPins is a bindable property it can participate in the data binding system. We can setup a property in our view model and attach it in our XAML.


    <view:CustomMap x:Name="cusmap"
IsShowingUser="True" MapType="Street"
CustomPins="{Binding CustomPins, Mode=TwoWay}" />

And here is the property in the view model:


/// other properties
private List<CustomPin> _customPins = new List<CustomPin>();
public List<CustomPin> CustomPins
{
    get { return _customPins; }
    set { SetProperty<List<CustomPin>>(ref _customPins, value); }
}

Finally, we need to get our CustomMapRenderer to react to these changes. The way we do that is to override the OnElementPropertyChangedEvent to list for changes in the CustomPins property.


public class CustomMapRenderer : MapRenderer, GoogleMap.IInfowindowAdapter, IOnMapReadyCallback
{
    // .... all the other code

    protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        base.OnElementPropertyChanged(sender, e);

        if (Element == null || Control == null)
            return;

        if (e.PropertyName == CustomMap.CustomPinsProperty.PropertyName)
        {
            UpdatePins();
        }
    }
}

Finally, I want to show how you can use that data binding technique to pass some services through to your Android project. For example, when a user taps a marker on the map, the info window will show. We can listen for when the user taps on that info window and use our Prism navigation service to pull up a new page. Let’s add the new bindable property to the CustomMap class.


public class CustomMap : Map
{
    public static readonly BindableProperty CustomPinsProperty =
        BindableProperty.Create(
            "CustomPins",
            typeof(List<CustomPin>),
            typeof(CustomMap),
            new List<CustomPin>(),
            BindingMode.TwoWay);

    public List<CustomPin> CustomPins
    {
        get { return (List<CustomPin>) GetValue(CustomPinsProperty); }
        set { SetValue(CustomPinsProperty, value); }
    }

    public static readonly BindableProperty NavigationServiceProperty =
        BindableProperty.Create(
            "NavigationService",
            typeof(Prism.Navigation.INavigationService),
            typeof(CustomMap),
            null);

    public Prism.Navigation.INavigationService NavigationService
    {
        get { return (Prism.Navigation.INavigationService) GetValue(NavigationServiceProperty); }
        set { SetValue(NavigationServiceProperty, value); }
    }
}

Let’s make sure the navigation service is exposed in our view model and then bind it in XAML. Here’s the view model:


public class MapViewModel : BaseViewModel
{
    public MapViewModel(INavigationService navigationService)
        : base()
    {
        NavService = navigationService;
    }

    /// other properties, such as CustomPins

    private INavigationService _navService = null;
    public INavigationService NavService
    {
        get { return _navService; }
        set { SetProperty<INavigationService>(ref _navService, value); }
    }

    /// other code is here
}

And now the XAML:


    <view:CustomMap x:Name="cusmap"
IsShowingUser="True" MapType="Street"
CustomPins="{Binding CustomPins, Mode=TwoWay}"
NavigationService="{Binding NavService}" />

And now we can get use the navigation service from our CustomMapRenderer like this by adding in our InfoWindowClick handler. Remember that we wired it up in the OnMapReady function. Now let’s put some functionality in the handler:


private void OnMapInfoWindowClick(object sender, GoogleMap.InfoWindowClickEventArgs e)
{
    if (_customMap.NavigationService != null)
    {
        _customMap.NavigationService.NavigateAsync(DevDaysSpeakers.PageKeys.Speakers);
    }
}

You can find all of this code in my GitHub repo in the MapRenderer solution. If you run the project, you can tap on the “Map” button on the main page. It will bring up the map and display a marker. If you tap the test button, it will update the marker to a new location. Tapping on the marker will show a custom info window, and tapping that window will use the Prism navigation service to navigate back to the main page.

Sounds like I am finally going to pick up a Mac laptop, so I should be able to expand these posts to the iOS platform. Looking forward to that.

Lots of stuff! Hope you find some of it useful.