There are many ways to share code between .Net/Xamarin applications running on different platforms, but what is the best way to deal with the platform specific code? In this session you'll learn how to use shared files, dependency injection, portable class libraries, and shared projects to isolate platform specific code from the re-usable shared code.
2. What is “Cross Platform”
Android Phone
Android Tablet
Android Wear
iPhone
iPad
Apple Watch
Apple TV
Windows Phone
Windows Apps
Microsoft Band
Silverlight
XBox
.Net Core
.Net (ASP, WPF,
Winforms, Etc)
5. using Windows.Storage;
namespace XPlat
{
public class UserData
{
public List<TodoItem> GetUserTodoList()
{
var userName = this.UserName;
//Make a call to a cloud resource
}
public string UserName
{
get
{
return ApplicationData.Current.LocalSettings.Values["UserName"].ToString();
}
set
{
ApplicationData.Current.LocalSettings.Values["UserName"] = value;
}
}
}
}
6. Linked Files Shared Project* Portable Class
Conditional Compile
Partial Classes
Inheritance
Dependency Injection
Share Code
Manage
Platform
Differences
How do we share code?
16. using System.IO.IsolatedStorage;
namespace XPlat
{
public class UserData
{
public List<TodoItem> GetUserTodoList()
{
var userName = this.UserName;
//Make a call to a cloud resource
}
public string UserName
{
get
{
return IsolatedStorageSettings.ApplicationSettings["UserName"].ToString();
}
set
{
IsolatedStorageSettings.ApplicationSettings["UserName"] = value;
}
}
}
}
24. namespace XPlat
{
public partial class UserData
{
public List<TodoItem> GetUserTodoList()
{
//Get the username for local storage
var userName = this.UserName;
//Make a call to a cloud resource
}
}
}
25. using Windows.Storage;
namespace XPlat
{
public partial class UserData
{
public string UserName
{
get
{
return ApplicationData.Current.LocalSettings.Values["UserName"].ToString();
}
set
{
ApplicationData.Current.LocalSettings.Values["UserName"] = value;
}
}
}
}
26. using System.IO.IsolatedStorage;
namespace XPlat
{
public partial class UserData
{
public string UserName
{
get
{
return IsolatedStorageSettings.ApplicationSettings["UserName"].ToString();
}
set
{
IsolatedStorageSettings.ApplicationSettings["UserName"] = value;
}
}
}
}
31. namespace XPlat
{
public abstract class UserDataBase
{
public List<TodoItem> GetUserTodoList()
{
//Get the username for local storage
var userName = this.UserName;
//Make a call to a cloud resource
}
public abstract string UserName { get; set; }
}
}
32. using Android.App;
using Android.Content;
namespace XPlat
{
public class UserData : UserDataBase
{
private readonly ISharedPreferences _preferences;
public UserData()
{
var ctx = Application.Context;
_preferences = ctx.GetSharedPreferences(ctx.PackageName, FileCreationMode.Private);
}
public override string UserName
{
get { return _preferences.GetString("UserName", ""); }
set
{
using (var editor = _preferences.Edit())
{
editor.PutString("UserName", value);
editor.Commit();
}
}
}
}
}
33. What do all these have in common?
namespace XPlat
var userData = new XPlat.UserData();
var todos = userData.GetUserTodoList();
36. namespace XPlat
{
public class UserData
{
private readonly IStoredSettings _storedSettings;
public UserData(IStoredSettings storedSettings)
{
_storedSettings = storedSettings;
}
public List<TodoItem> GetUserTodoList()
{
//Get the username for local storage
var userName = _storedSettings.UserName;
//Make a call to a cloud resource
throw new NotImplementedException();
}
}
}
37. using Foundation;
namespace XPlat.iOS
{
public class AppleSettings : IStoredSettings
{
public string UserName
{
get { return NSUserDefaults.StandardUserDefaults.StringForKey("UserName"); }
set
{
var defaults = NSUserDefaults.StandardUserDefaults;
defaults.SetString(value, "UserName");
defaults.Synchronize();
}
}
}
}
38. var userData = new XPlat.UserData(new XPlat.iOS.AppleSettings());
var todos = userData.GetUserTodoList();
39. Linked Files Shared Project* Portable Class
Conditional Compile
Partial Classes
Inheritance
Dependency Injection
Share Code
Manage
Platform
Differences
How do we share code?
Most people think of Xamarin or Cordova or some other tool that allows you to target iOS, Android and Windows phones and tablets
But it can also include wearables like Apple Watch, Android Wear, MS Band Also includes Winforms, WPF and maybe a legacy Silverlight appAlso includes Asp.Net and Asp.Net Core
Have a bunch of code that is the same
Some code that is platform specific
Goal is to minimize the platform specific code
No Copy/Paste
2 Methods:GetUserTodoList() would be the same code for all platforms
Linked Files and Shared Projects are share then compilePortable Class is Compile then ShareShared Projects aren’t available from ASP.NET or ASP.NET CORE
Advantage is it can be used with any two projectsDisadvantage renaming a file breaks the link
Shared project has no references
Just a container that holds files shared between the projects
They are pulled in and compiled with each project
Reference the shared project from the other projects
Can’t use shared with all types of projects (not ASP.NET or ASP.NET CORE)
Sharing with WindowsPhone 8 means a different Namespace and Different API for local storage
Right click on a project->propertiesWindows phone 8.1 Defines NETFX_CORE and WINDOWS_PHONE_APP
Windows 8.1 and UWP both also define NETFX_CORE
I can open the linked file from both projects, changes made in one are shown in the otherFile only exists in the original project. Renaming the original file breaks the link.
If you include WP8, WP8.1, Win8.1, UWP, Android and iOS your usings look like thisWe need a better way
Splits the definition of a class over two or more files.
The UserData class defined in your shared project looks like this
Win8.1, WinPhone8.1 and UWP UserData looks like this
WindowsPhone 8 looks like thisThis looks pretty good but for both techniques all the projects must be in the same solution
Shared projects don’t work with some project types
Lowest common denominator If you choose .NET 4 or WinPhone 8.1 you don’t get Compression and Data annotationYou don’t get Dynamic with WinPhone 8.1Can’t combine Silverlight 5 and ASP.NET Core
Abstract classes contain 1+ abstract methodAbstract classes can’t be instantiated and require subclasses to provide implementations for the abstract methods
This is the definition of the UserDataBase class in the Portable Class LibraryNote the class is abstract so it can’t be instantiated
Defines UserName property as Abstract so sub-classes must implement it.
User Data Class in the Android projectNote we inherit from UserDataBase and Override the UserName property.
All these techniques so far have put the all shared code in the XPlat namespaceWe could implement inheritance in XPlat.Droid but if a calling class is completely shared through a PCL in the XPlat it can’t access the platform specific code without additional techniques to handle usingsCalling code would also all look the same
With this technique you can pass in the platform specific implementation that will be used.
Define this interface in the PCL
Calling code would look like thisYou can manually inject the dependency or use a framework like StructureMap, Unity, Ninject or something else.
Linked Files and Shared Projects are share then compilePortable Class is Compile then ShareShared Projects aren’t available from ASP.NET or ASP.NET CORE