XAM250 Patterns for Cross Platform Mobile Development

Exercise 2: Add an abstraction and Service Locator into our project to find and use a platform-specific feature. (XAM250)

This exercise will define a service for performing text-to-speech and then create a Service Locator to find the implementation of that service which is platform-specific. This is intended to be done as part of the online class, if you are working through this on your own you can follow the step-by-step instructions shown below.

To complete the exercise, you will need Visual Studio for Windows or macOS with the Xamarin development tools installed. You will also need either an emulator/simulator or a device to run the exercise on. Please see the setup page if you need help installing the Xamarin development environment.

Open the starter solution

Open the starter solution from the Exercise 2 > Start folder in your copy of the cloned or downloaded course materials in either Visual Studio on Windows or Visual Studio for Mac.


Define the abstraction

Let's start by defining our abstraction - you'll have each platform use it's Text-To-Speech engine to read stored quotes to the user. The first step is to define an interface called ITextToSpeech with a single method called Speak. This needs to go in your shared code. You'll place it in the GreatQuotes.Data PCL.

  1. Open the starter project GreatQuotes.sln or continue with the previous exercise.
  2. Create a new interface source file in the PCL project, name it ITextToSpeech.cs.
  3. Make the interface public so it can be used outside the PCL.
  4. Add a single method to the interface named Speak that returns void and takes a single string parameter.
namespace GreatQuotes
{
    public interface ITextToSpeech
    {
        void Speak(string text);
    }
}

Add the service locator

Next, you're going to add a service locator - you could write this from scratch, but to save time you can add an existing implementation that has already been written.

  1. The code is in the Exercise 2 > Assets folder - add the file into the GreatQuotes.Data PCL's Assets folder. Make sure to copy the file into the project and not move it.
  2. Examine the new source file you have added. It associates a contract Type with an implementation by saving the registration off in a dictionary.
    • There is a static Instance property to retrieve the singleton locator object.
    • It has a Add method to register a type with an implementation.
    • You use the Resolve<T> method to locate an implementation based on the abstraction.

Add the Text-to-Speech implementations

Register your implementations for the Text-to-Speech functionality in each of the platform-specific projects.

  1. Add implementations of the TTS engine for each platform. You've got three of them in your assets folder in the Exercise 2 folder, one for each of our platforms.
  2. Add each one into the appropriate platform-specific project based on the name
    • TextToSpeechService.Android.cs goes into the Android project.
    • TextToSpeechService.iOS.cs goes into the iOS project.
    • TextToSpeechService.Win.cs goes into the UWP project.
  3. Feel free to examine the source for each implementation - make sure the interface is correctly defined and implemented and build the projects.

Register the implementations with the abstraction

Use your Service Locator and register each implementation in the platform code against the ITextToSpeech interface.

  1. Use the Add method on the service locator in the platform-specific project to tie the TextToSpeechService class to the ITextToSpeech interface.
    • Register the iOS version in the AppDelegate.cs file as part of FinishedLaunching.
    • Register the Android version in the App.cs file as part of the App.OnCreate override.
    • Register the Windows version in the App.xaml.cs file as part of the App constructor.
ServiceLocator.Instance.Add<ITextToSpeech, TextToSpeechService>();

Use the TTS service

Finally, add support to call the Text-to-Speech service in your shared code and invoke it from each platform.

  1. Add a method to the shared QuoteManager.cs class which will invoke the TTS service.
  2. Name the method SayQuote and have it take a GreateQuote as the parameter.
  3. Lookup the ITextToSpeech abstraction using the service locator Resolve method.
  4. Make sure the returning value is not null and if not, use the Speak method to say the quote and author.

    public class QuoteManager
    {
        ...
        public void SayQuote(GreatQuote quote)
        {
            if (quote == null)
                throw new ArgumentNullException("quote");
    
            ITextToSpeech tts = ServiceLocator.Instance.Resolve<ITextToSpeech>();
    
            var text = quote.QuoteText;
    
            if (!string.IsNullOrWhiteSpace(quote.Author))
               text += $" by {quote.Author}";
    
            tts.Speak(text);
        }
    }
    
  5. You need to call this new method for each platform on the Details screen. This will be in the platform specific code, which is using this quote object. Use the following code to invoke the logic.
  6. In iOS, add the code to the QuoteDetailViewController.cs ViewDidLoad method.

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();
        ...
        NavigationItem.RightBarButtonItem = editButton;
        Quote.AddGestureRecognizer(new UITapGestureRecognizer(() => QuoteManager.Instance.SayQuote(quote)));
    }
    
  7. In the Android project, open QuoteDetailActivity.cs.
  8. In the OnCreate method, subscribe to the quoteText Touch event, create a handler method and add the following code.

    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);
        ...
        quoteText.Touch += QuoteText_Touch;
    }
    
    void QuoteText_Touch(object sender, View.TouchEventArgs e)
    {
        var qm = QuoteManager.Instance;
        qm.SayQuote(qm.Quotes[quoteIndex]);
    }
    
  9. Override OnDestroy and unsubscribe the Touch event handler.
  10. Finally, in Windows, it will be in the QuoteDetailsPage. Add a PointerPressed event handler onto the first TextBlock which is displaying the quote text and in the event handler implementation use the following code:

    private void OnSayQuote(object sender, PointerRoutedEventArgs e)
    {
        QuoteManager.Instance.SayQuote((GreatQuote)DataContext);
    }
    
  11. Build and run the application on as many implementations as you can.
  12. Set some breakpoints and trace through the service locator to see how it finds the implementation and then invokes the service.

Exercise summary

In this lab exercise, you utilized a Service Locator pattern to abstract away a platform-specific feature and then invoke it from shared code.

You can view the completed solution in the Exercise 2 > Completed folder of your copy of the cloned or downloaded course materials.

Go Back