Learn how to use the Flux design pattern with Xamarin.
Source code: https://github.com/SuavePirate/Xamarin.Flux
Learn how to use the Flux design pattern with Xamarin.
Source code: https://github.com/SuavePirate/Xamarin.Flux
Learn how to use 4 methods to call platform specific code from shared code in Xamarin. Make calls to the HockeyApp iOS SDK from a Portable Class Library.
Source Code: https://github.com/SuavePirate/Xamarin.HockeyApp.Portable
Using all 4 methods to call platform specific code from shared code in Xamarin. Make calls to the HockeyApp iOS SDK from a Portable Class Library.
Source Code: https://github.com/SuavePirate/Xamarin.HockeyApp.Portable
Using Dependency Injection to call platform specific code from shared code in Xamarin. Make calls to the HockeyApp iOS SDK from a Portable Class Library.
Source Code: https://github.com/SuavePirate/Xamarin.HockeyApp.Portable
Using the Service Locator anti-pattern to call platform specific code from shared code in Xamarin. Make calls to the HockeyApp iOS SDK from a Portable Class Library.
Source Code: https://github.com/SuavePirate/Xamarin.HockeyApp.Portable
Using the Xamarin.Forms DependencyService class to call platform specific code from shared code in Xamarin. Make calls to the HockeyApp iOS SDK from a Portable Class Library.
Source Code: https://github.com/SuavePirate/Xamarin.HockeyApp.Portable
Does your app have sensitive information that belongs to your user? If so, you’re probably taking some action to protect it. Storing it with encryption, locking it behind a passcode, using TouchID, clearing their session when they leave the app, etc.
One thing you might not have considered is a vulnerability when using the app switcher. Could someone take your user’s phone and view the sensitive information by just double tapping the home button?
Let’s protect that data. We’re going to put a blurred view over the app whenever the user leaves (or even just hits the app switcher right away), plus it can look pretty cool!
In our AppDelegate.cs
, override the OnResignActivation
method:
public override void OnResignActivation(UIApplication uiApplication) { var window = UIApplication.SharedApplication.KeyWindow; var blurView = UIBlurEffect.FromStyle(UIBlurEffectStyle.Light); var blurEffectView = new UIVisualEffectView(blurView); blurEffectView.Frame = window.Frame; blurEffectView.Tag = 808080; window?.AddSubview(blurEffectView); base.OnResignActivation(uiApplication); }
This will add our blurred view whenever they leave. Now to remove it when they come back, override the OnActivated
method:
public override void OnActivated(UIApplication uiApplication) { var window = UIApplication.SharedApplication.KeyWindow; window?.ViewWithTag(808080)?.RemoveFromSuperview(); base.OnActivated(uiApplication); }
And that’s it!
Bonus swift version: Override applicationWillResignActive
and applicationDidBecomeActive
in your AppDelegate.swift
.
func applicationWillResignActive(application: UIApplication) { let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.Dark) let blurEffectView = UIVisualEffectView(effect: blurEffect) blurEffectView.frame = window!.frame blurEffectView.tag = 808080 self.window?.addSubview(blurEffectView) } func applicationDidBecomeActive (application: UIApplication) { self.window?.viewWithTag(808080)?.removeFromSuperview() }
For those who just want code: https://github.com/SuavePirate/Xamarin.Onion
Don’t forget:
A strong and scale-able architecture is important in applications, especially in Mobile Apps. APIs and SDKs are constantly changing, new technology is constantly released, and team sizes are always changing. A solid Onion Architecture can save a development team a lot of time by making it simple to change service implementations, restrict access to certain areas, making logic flow easy to follow, and making testing isolated blocks of code easier.
Some of the important topics this will cover:
In this section, we will talk briefly about building useful tests for our solution, and why the Onion pattern makes it easy to break tests out into individual layers.
In this example, we will add a test project whose purpose it to just test the Business layer within our Infrastructure.
Let’s start with by adding a nUnit project to our solution, or by adding the nuget package to a class library. Xamarin has great documentation on this: https://developer.xamarin.com/guides/cross-platform/application_fundamentals/installing-nunit-using-nuget/
In our project, we also want to install MvvmLight, just like in our Client and Platform layers. We will also need to add references to our Domain.Models, Domain.Interfaces, Application.Models, Application.Interfaces, and Infrastructure.Business projects.
In order to test our Infrastructure.Business project, we will need to create mock versions of our Data project. In our test project, we can create Repository implementations with mock data for each set that we need. For example:
MockGenericRepository.cs
public class MockGenericRepository : IGenericRepository { private List _data; public MockGenericRepository() { _data = new List(); } public void Add(T entity) { _data.Add(entity); } public void AddRange(IEnumerable entities) { _data.AddRange(entities); } public Task CommitAsync() { return Task.FromResult(false); // we don't need to explicitly save changes } public Task FindAsync(Func<T, bool> predicate) { var entity =_data.Where(predicate).FirstOrDefault(); return Task.FromResult(entity); } public Task<IEnumerable> GetAsync(Func<T, bool> predicate) { var entities =_data?.Where(predicate); return Task.FromResult(entities); } public void Remove(T entity) { _data.Remove(entity); } }
and MockUserRepository.cs
public class MockUserRepository : MockGenericRepository, IUserRepository { public MockUserRepository() : base() { } }
Now that we have some mock implementations, we can set up our tests against our Business logic.
UserBusinessTests.cs
public class UserBusinessTest { private IUserService _userService; [SetUp] public void StartUpIoC () { ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default); SimpleIoC.Default.Register<IUserService, UserService>(); SimpleIoC.Default.Register<IUserRepository, MockUserRepository>(); _userService = SimpleIoC.Default.GetInstance(); } [Test ()] public async void AddUserTest() { var result = await _userService.CreateUserAsync(new NewUser { Email = "test@test.com", FullName = "Testy McTest" }); Assert.IsNotNull(result.Data); } }
Now we can test against any of the business logic in our application with a mock layer. The same practice can be applied to test any other layer in the solution as well. The data layer can be tested by mocking the business layer, and so on.
Looking back at all of the components of our Onion Architecture, one might think, “Wow, that’s a lot of code to do a simple task”. It’s important to remember that this architecture is not for every project. It’s focus is on scalability and testability. If your project has the potential to grow into something quite complicated, with many developers involved, this type of solution might work best for you. However, if you’re working on something quick to get out the door, maybe getting right to the point is easier and best for you.
The best parts about the Onion Architecture are its abilities to make drastic changes to tools or services used, without having to rewrite anything but that components implementation as well as making it easy to test individual layers without affecting the others or using real data. It also allows for closer monitoring and management of the codebase; keeping people from making calls directly from one layer to another. The only thing you have to emphasize is, “Are you adding a reference to another project to get something done? If so, you might be doing it wrong”.
For those who just want code: https://github.com/SuavePirate/Xamarin.Onion
Don’t forget:
A strong and scale-able architecture is important in applications, especially in Mobile Apps. APIs and SDKs are constantly changing, new technology is constantly released, and team sizes are always changing. A solid Onion Architecture can save a development team a lot of time by making it simple to change service implementations, restrict access to certain areas, making logic flow easy to follow, and making testing isolated blocks of code easier.
Some of the important topics this will cover:
In this section, we will look at how to expand our Inversion of Control container with platform specific code. Specifically, we will implement some pieces of the HockeyApp SDK so that we can make calls to it from our Client or Infrastructure layers.
Our example will focus on just Android, but the same principles can be applied to any of the unique platform projects.
First thing we need to do is make sure we also install the MvvmLight nuget package in your Android project, as well as the HockeyApp Xamarin package.
From here, we can go back to our Application.Interface layer and create a new service:
ICrashAnalyticsService.cs
public interface ICrashAnalyticsService { void Initialize(); void GetFeedback(); }
Setting it up generic like this allows us to switch providers from HockeyApp to some other service should that be a need in the future.
Back in our Android project, let’s implement the ICrashAnalyticsService
with our HockeyApp logic.
HockeyAppService.cs
public class HockeyAppService : ICrashAnalyticsService { private const string HOCKEYAPP_KEY = "YOUR_HOCKEYAPP_KEY"; private readonly Android.App.Application _androidApp; private readonly Activity _context; public HockeyAppService(Activity context, Android.App.Application androidApp) { _context = context; _androidApp = androidApp; } public void GetFeedback() { FeedbackManager.ShowFeedbackActivity(_context.ApplicationContext); } public void Initialize() { CrashManager.Register(_context, HOCKEYAPP_KEY); MetricsManager.Register(_androidApp, HOCKEYAPP_KEY); UpdateManager.Register(_context, HOCKEYAPP_KEY); FeedbackManager.Register(_context, HOCKEYAPP_KEY); } }
Now we can create an IoCConfig
class specific to our Android project. Because SimpleIoC
uses a singleton for its container, we can register classes in our platform specific classes before our registrations in the Client layer.
AndroidIoCConfig.cs
public class AndroidIoCConfig { public void RegisterAndroidServices(Android.App.Application application, Activity activity) { var hockeyService = new HockeyAppService(activity, application); hockeyService.Initialize(); SimpleIoc.Default.Register<ICrashAnalyticsService>(() => hockeyService); } }
Don’t forget to add a reference to the Application.Interfaces
project in your platform project.
Lastly, let’s update our MainActivity
to initialize our AndroidIoCConfig
before we start up the Xamarin.Forms app:
MainActivity.cs
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity { protected override void OnCreate(Bundle bundle) { TabLayoutResource = Resource.Layout.Tabbar; ToolbarResource = Resource.Layout.Toolbar; base.OnCreate(bundle); global::Xamarin.Forms.Forms.Init(this, bundle); InitializeIoC(); LoadApplication(new App()); } private void InitializeIoC() { var container = new AndroidIoCConfig(); container.RegisterAndroidServices(Application, this); } }
Now we can make calls to our ICrashAnalyticsService
from the Client layer, and use the Android specific logic. For example, we can pass the ICrashAnalyticsService
into the constructor of a ViewModel
, and call the GetFeedback()
method to get access to the HockeyApp Feedback view.
ExampleViewModel.cs
public class ExampleViewModel : BasePageViewModel { private readonly ICrashAnalyticsService _crashAnalyticsService; private ICommand _feedbackCommand; public ICommand FeedbackCommand { get { return _feedbackCommand ?? (_feedbackCommand = new RelayCommand(() => { _crashAnalyticsService.GetFeedback(); })); } } }
It’s all that simple! The same pattern can be applied to anything that needs to be platform specific.
In the next and final segment, we will look at building mock implementation of our Infrastructure layer and using them to test layers individually in Unit tests.