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
Unlike iOS, Android does not ship with a built in launch screen or splash screen, so it looks particularly ugly when we start up a Xamarin.Forms app and wait for everything to initialize.
Here’s a quick tip to add a Splash screen to your Android app while it loads up your Xamarin.Forms Application.
First thing we are going to do is create a new drawable: Resources > drawable > splash.xml
<?xml version="1.0" encoding="utf-8" ?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android" > <item> <shape android:shape="rectangle" > <solid android:color="#3498DB" /> </shape> </item> <item> <bitmap android:src="@drawable/icon" android:gravity="center" android:layout_gravity="center"/> </item> </layer-list>
Now we need to create a style that uses this drawable as the background: Resources > values > styles.xml
... <style name="Theme.Splash" parent="android:Theme"> <item name="android:windowBackground"> @drawable/Splash </item> <item name="android:windowNoTitle">true</item> <item name="android:windowFullscreen">true</item> <item name="android:windowIsTranslucent">false</item> <item name="android:windowIsFloating">false</item> <item name="android:backgroundDimEnabled">true</item> <item name="android:colorPrimaryDark">#FFFFFF</item> </style> ...
Lastly we are going to create a new Activity
, remove the MainLauncher
property from our MainActivity
, and add it to our new SplashActivity
. Our SplashActivity
is going to immediately start up the MainActivity
, but use our theme to show the drawable as the background.
[Activity(Label = "myApp", Icon = "@drawable/icon", Theme = "@style/Theme.Splash", MainLauncher = true, NoHistory = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)] public class SplashActivity : Activity { protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); } protected override void OnResume() { base.OnResume(); var startUp = new Task(() => { var intent = new Intent(this, typeof(MainActivity)); StartActivity(intent); }); startUp.ContinueWith(t => Finish()); startUp.Start(); } }
Also note the NoHistory
flag which disables the back button from going back to this activity, it will destroy itself on leaving.
Now when we run our app, we should see our splash screen before the app loads up!
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 part 1 on the general project structure: Onionizing Xamarin Part 1
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’ll start to dive into the code for our definition layers (or at least what is important).
Let’s get into the Domain and Application layers:
As said before, this is where our core models are, so let’s take one as an example:
User.cs
public class User { public int Id { get; set; } public string Email { get; set; } public string FullName { get; set; } public string PasswordHash { get; set; } }
We’ll focus on our one model, but you could grow your entities out here.
This is where we define our data access layer for consuming our Domain.Models; Stores, DataProviders, and Repositories. These are good places to set up generic definitions so that multiple implementations can be made more easily. Here are examples of each:
IGenericStore.cs and IUserStore.cs
public interface IGenericStore<T> { List<T> Data { get; set; } } public interface IUserStore : IGenericStore<User> { }
If you were to need to define custom methods for the user, you can do that in your IUserStore
Another common practice is to add a manager wrapper for your stores, to make it easier to inject the use of multiple stores in our business layer in the future. In this example, it would look something like this:
IStoreManager.cs
public interface IStoreManager { IUserStore UserStore { get; } IGenericStore<T> Set<T>(); }
IGenericRepository.cs and IUserRepository.cs
public interface IGenericRepository<T> { void Add(T entity); void AddRange(IEnumerable<T> entities); void Remove(T entity); void RemoveRange(T entities); Task<T> FindAsync(Func<T, bool> predicate); Task<IEnumerable<T>> GetAsync(Func<T, bool> predicate); Task CommitAsync(); } public interface IUserRepository : IGenericRepository<User> { }
Just as with the Stores, you can define entity specific methods / queries in your specific repository (IUserRepository
).
Now that we are through our data definition layers, let’s take a look at the application definition layers, starting with Application.Models. This is where our business models live – our Data Transfer Object Models, Input Models, Output Models, etc. So here is how our Domain.Models.User
maps to each of these types:
UserTransferObject.cs
public class UserTransferObject { public int Id { get; set; } public string Email { get; set; } public string FullName { get; set; } public UserTransferObject() { } public UserTransferObject(User entity) { Id = entity.Id; Email = entity.Email; FullName = entity.FullName; } }
Note that we have added a constructor that also consumes a Domain.Models.User
type. This is completely optional. Many people do not want the Application.Models
layer to reference any other layer. Another common way to handle the mapping is via an extension class in the Infrastructure.Business
layer, like so:
public static class UserExtensions { public static UserTransferObject ToDTO(this User entity) { return new UserTransferObject { Id = entity.Id; Email = entity.Email; FullName = entity.FullName; } } }
The important thing to note in all of this, is that the DTO has properties mapped from the entity that are relevant and SAFE to the application. Notice the PasswordHash
field was omitted.
NewUser.cs
public class NewUser { public string Email { get; set; } public string FullName { get; set; } public string NewPassword { get; set; } }
This is one of our input models for creating a new User
. Notice that it only has the properties required for creating one.
Last but not least, our output. This example uses a generic output Result
that holds data from a DTO, errors, and the type of result. The output models you use will depend on the services you’re using, so this is not a catch-all.
Result.cs and ResultType.cs
public class Result<T> { public ResultType Type { get; set; } public IEnumerable<string> Errors { get; set; } public T Data { get; set; } public Result(T data) { Data = data; Type = ResultType.Ok; Errors = new List<string>(); } public Result(ResultType type, IEnumerable<string> errors) { Type = type; Errors = errors; } public Result(ResultType type, string error) { Type = type; Errors = new List<string> { error }; } } public enum ResultType { Ok, BadRequest, Failed, Unauthorized, Forbidden, Invalid }
Now we have our models, let’s define our business layer.
These are the definitions of our business logic that use our Application.Model
layer. We’ll use services here, and like our data definitions, will utilize generic definitions where possible.
IBaseService.cs and IUserService.cs
public interface IBaseService { IEnumerable<string> Validate(object model); } public interface IUserService : IBaseService { Task<Result<UserTransferObject>> CreateUserAsync(NewUser model); Task<Result<UserTransferObject>> FindByIdAsync(int userId); Task<Result<UserTransferObject>> RemoveByIdAsync(int userId); Task<Result<IEnumerable<UserTransferObject>>> GetValidUsers(); }
Notice that each of our consumes either a primitive type, or an input model from our Application.Models
and outputs one of our output models.
That’s all there is for the different definition layers. In the next post, we’ll look at implementing these two layers in our Infrastructure.Data
and Infrastructure.Business
layers. From there, we can look at our actual Xamarin code for consuming these layers and mapping them all together.
Once we’ve gone over all our layers, we will look into replacing different pieces, building tests, and areas where you can add your own flare.