Exercise 1: Use the Factory Pattern to access a dependency from shared code (XAM250)
This exercise will take an existing iOS, Android, or Windows project and pull out sharable code into a Portable Class Library (PCL), using the Factory Pattern to isolate the platform-specific code to read and write the storage file used to display quotes.

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 1 > Start folder in your copy of the cloned or downloaded course materials in either Visual Studio on Windows or Visual Studio for Mac.
Create the Portable Class Library
Your first step is to create the library to hold our shared code - you'll use a Portable Class Library (PCL) (vs. a Shared Project) as it will force you to put more thought into how you architect your shared code and provide a distinct boundary between the projects.
- Add a new PCL project to the solution. Name it GreatQuotes.Data.
- Remove the blank source file added to the project by default.
- Move the
GreatQuote
class from the Data folder in your platform project into the PCL. Make sure it's no longer in the platform-specific project and is only in the PCL project. - Add a reference to the PCL to your platform project so it has access to the model data.
- Build the solution and make sure it still works - all you've done is move a file which was already portable so it should.
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
public class CallerMemberNameAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
public class CallerFilePathAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
public class CallerLineNumberAttribute : Attribute
{
}
}
Depending on the supported targets you have selected for the Portable Class Library, it's possible that some of the attributes used in the GreateQuote.cs file are not supported. In particular, the[CallerMemberName]
attribute might be missing. There are two ways to fix this problem. * First, you can change the supported target frameworks and remove support for Silveright 5 which does not have this attribute. * Second, you can add the following code into your project. The[CallerMemberName]
feature doesn't have a runtime component, just having the specific named attribute is enough to get the compiler to support the feature. Copy the following block and paste it into a new C# source file in your project and you should be able to compile.
Create the abstraction for the Quote Loader
Next, you'll need to provide an abstraction for the Quote Loader code you're using. If you try to add it to the PCL directly, it will fail to compile because it has dependencies against specific file APIs which are not available in your current profile. In addition, each platform is slightly different in how it handles local files and where they are placed. As a result, you need a unique approach to load our quotes for each platform.
-
Open the existing QuoteLoader.cs file in your platform-specific project (iOS, Android or Windows) - this file is located in the Data folder of each project. Examine the methods and implementation presented here.
- Load is used to load quotes from a file.
- Save is used to save an existing collection of quotes back to the same file.
-
Create a new interface to represent our
QuoteLoader
independent of the platform. You'll use an interface here, but you could also use an abstract base class as well. The interface should be placed into the Portable Class Library as it will be shared across all our projects.- Name it
IQuoteLoader
. - Add definitions for the
Load
andSave
methods just as they are in the in your platform project.
public interface IQuoteLoader { IEnumerable<GreatQuote> Load(); void Save(IEnumerable<GreatQuote> quotes); }
- Name it
-
Have your platform-specific implementation of
QuoteLoader
implement this interface - you shouldn't need to make any code changes to it as the signatures forLoad
andSave
should already be present in the existing class.public class QuoteLoader : IQuoteLoader
- Build and run the application and make sure it still works properly.
Create the Factory to create the IQuoteLoader
Next, you'll utilize the Factory pattern to create the property implementation of the IQuoteLoader
that you'll use in our PCL code.
- Create a new static class named
QuoteLoaderFactory
in the Portable Class Library. This will represent the factory class you'll use to create your platform-specific implementation of anIQuoteLoader
. - Add a single static property named Create that is of type
Func<IQuoteLoader>
. This property is what you'll set in order to create a newIQuoteLoader
. - Here is what your code should look like for the factory:
public static class QuoteLoaderFactory
{
// This must be assigned to a method which creates a new quote loader.
public static Func<IQuoteLoader> Create { get; set; }
}
Refactor the code
In all three platform-specific projects, there is almost identical code to load and save the quotes. This currently utilizes the QuoteLoader
class directly, however you'd like to push this common code into your shared code (the PCL). In particular, you want to move the management of the GreatQuote
collection into shared code so that every platform locates the data the same way. Start by creating a class to manage your quotes.
- Create a new
QuoteManager
class in the PCL. -
Use the Singleton Pattern to create a static
Instance
property to expose a single copy of theQuoteManager
. You can use the built-inLazy<T>
type to implement this pattern, or just create the object the first time the property is accessed; the goal is to have a public, static property to get to a single, known instance of the object. If you need some help, you can use the below, simple example of creating a singleton in C#. You can also look at the Completed.V1 lab which uses the more efficientLazy<T>
approach.public class QuoteManager { private static readonly QuoteManager instance = new QuoteManager(); public static QuoteManager Instance { get { return instance; } } private QuoteManager() { ... } }
-
Add a public
IList<GreatQuote>
property namedQuotes
to expose the loaded quotes. - In the constructor, assign the property to a new
ObservableCollection<GreatQuote>
instance. -
Next, obtain an
IQuoteLoader
object using theQuoteLoaderFactory.Create
delegate and assign it to a field of your class. To do this, just call the delegate assigned to the property:IQuoteLoader loader; ... private QuoteManager() { ... loader = QuoteLoaderFactory.Create(); }
-
Then, populate your list of quotes using the
Load
method from theIQuoteLoader
field, this returns anIEnumerable<GreatQuote>
. - Finally, add a new public, instance method named
Save
which saves the collection of quotes using the quote Loader field'sSave
method.
Use the Quote Manager and Assign the Factory
The final step is to use the new shared QuoteManager
class and assign the factory.
- Open the application level class which was loading the quotes - this is
AppDelegate
in AppDelegate.cs for iOS,App
in App.cs for Android, andApp
in App.xaml.cs for Windows. -
Next, assign the
QuoteLoaderFactory.Create
property of your factory class to a method which creates a new platform-specificQuoteLoader
class (e.g. something that implementsIQuoteLoader
).- You can use any delegate assignment style you prefer - lambda, anonymous method, or a regular C# method defined in your class.
- Add this code into the
FinishedLaunching
override in the iOS project. - Add this code into the
OnCreate
override in the Android project. - Add this code into the
App
constructor in the Windows project.
-
Next, remove the
Quotes
collection and the quote loader code from the platform-specific code. Locate the staticList<GreatQuote>
that is used when loading and saving quotes. Here's what the line looks like:public static List<GreatQuote> Quotes { get; private set; }
-
This is the code you'd like to remove, remember that each project has it in a different file:
- iOS - AppDelegate.cs
- Android - App.cs
- Windows - App.xaml.cs
-
Try to compile your app - this will produce several errors because of our refactoring.
- Go through each error where the
Quotes
collection was being referenced and fix each spot to now use the commonQuoteManager.Instance.Quotes
property. This will reduce the coupling in the current code by using a more formalized pattern to locate the quotes. - Change the call to the original
QuoteLoader.Save
method to use the newQuoteManager.Instance.Save
method. This is in the same application-level class as the loading code. You should get a compile error which will point you at the correct spot to fix.
- Go through each error where the
- Build and run the app to verify it loads and saves quotes properly. Try putting a breakpoint into the
QuoteManager
class where it obtains the quote loader - trace through it to see it jump from the cross-platform (shared) code into each platform specific project. - If you have time, update the other platform-specific project to use your new Portable Class Library and
QuoteManager
.
Exercise summary
In this exercise, you've taken an existing set of applications and moved the data management code into a Portable Class Library (PCL), utilizing the Factory Pattern to load and save the data to a file.
You can view the completed solution in the Exercise 1 > Completed folder of your copy of the cloned or downloaded course materials.