TEF (Typed Extensibility Framework) is similar to MEF, but was designed to be flexible, customizable, and easy to implement. In this guide, we’re going to look at how to use TEF in a Xamarin Forms project. TEF is pretty easy to get started, but certain features are not available in a portable environment, which are in a Xamarin Android or Xamarin iOS project (for instance, the AppDomain is not portable, but is available in these projects). Therefore, we need to do some customization. To start, let’s go through the following steps:
Create a Project
In Visual Studio or Xamarin Studio, create a new portable or shared Xamarin.Forms project. TEF will work with either option.
Import TEF
Using Nuget, download the TEF package using Import-Package TEF via command line, or search for TEF in the Nuget Window Search Bar.
Add an AppDomain Assembly Loader
Or whatever other strategy you like to use. The following snippet uses the AppDomain to look for the loaded assemblies and find any ExportType or ExportDependency attributes. To do this, we need to create a class that implements TEF.Consumption.IAssemblyLoader. It will return a list of assemblies, and must exist in both the iOS and Android project.
Find your iOS and Android projects and create a new folder called “Library” or “Services”, or whatever you like to call it, and include the following code snippet below in each project. Note: if you are using a shared project, this may work by placing the reference there. I haven’t tried using a shared project.
[assembly:Dependency(typeof(AppDomainAssemblyLoader))] public class AppDomainAssemblyLoader : TEF.Consumption.IAssemblyLoader { public IEnumerable LoadAssemblies () { return System.AppDomain.CurrentDomain.GetAssemblies (); } }
I chose to use Xamarin’s DependencyService, which is a service locator, as the means to register and fetch this type (you’ll see the fetch later on). To register types, see my previous blog post, which explains how the assembly attributes are used.
Add a Consumer
A consumer is the component that takes the types and produces a dependency injection container. In this example, I’m using SimpleInjector PCL. Any container can be used, and you can find more about supporting containers via the Container Integration documentation. Note: it’s not complicated to create your own IConsumer class though; see the documentation for how.
Import Types
We need to add the TypeFactory code that ties all of these components together. In the App class that the Xamarin.Forms project creates, this code can exist in OnStart, or it can also be utilized with the Lazy class. I chose a different route: define it in the property of the App class:
public class App : Application { private SimpleInjector.Container _container; public SimpleInjector.Container Container { get { if (_container == null) { _container = TypeFactory.Configure ((c) => { c.AssemblyLoader ( DependencyService.Get (DependencyFetchTarget.GlobalInstance) ).Consumer ( new SimpleInjectorConsumer() ); }).Initialize () as SimpleInjector.Container; } return _container; } }
Here we use the TypeFactory to tie everything together. The configure method takes the assembly loader and consumer references, while the Initialize() uses this to construct the container. The container is returned and can be used in your app (weakly typed). In my example, to use the container in your application, use ((App)Application.Current).Container. And that’s all it takes to incorporate TEF into an assembly. The benefit is we can mark a type like:
[ExportType(typeof(ISomeType))] public class SomeType : ISomeType {
or as:
[ExportAssembly(typeof(ISomeType), typeof(SomeType))] public interface ISomeType { } public class SomeType : ISomeType { }
And these will be automatically consumed for us and put in our dependency injection container.