Siri Shortcuts with Xamarin

With iOS 12, Apple introduced Siri shortcuts. For us developers, we can create shortcuts using SiriKit. In this blog post, we will walk through how to do that in an app that we are building with Xamarin.iOS.

The first step is to update our provisioning profile.

    1. Sign in to https://developer.apple.com.
    2. Go to the account page
    3. Go to Certificates, Identifiers & Profiles
    4. Go to App IDs
    5. Select the App ID that you want to use and press the edit button.
    6. Enable the SiriKit service for the selected App ID.

 

    1. Go to publishing profiles and select the profile you want to use for your app that you will implement SiriKit in.
    2. Select to edit the publishing profile
    3. Regenerate the publishing profile
    4. Download the publishing profile and install it on the machine that you are using for building iOS app.

Now when we have the publishing profile we can open the app solution in Visual Studio to start to implement SiriKit. The first thing we will do is to add Siri to our Entitlements.plist file.

<plist version="1.0">
    <dict>
	<key>com.apple.developer.siri</key>
	<true/>
    </dict>
</plist>

 

We need to verify that the Entitlements.plist is used as Custom Entitlements. We have to add the file for each configuration we want to use shortcuts for. Configurations for Custom Entitlements is one under the bundle signing page of the project settings.

We need to add all shortcuts that we will create to Info.plist. We will add them under the NSUserActivityTypes key. The nameing recommendation is {bundleID}.{activityName}. In the examle below there are shortcuts for sedning a message and one for reading unread messages.

<key>NSUserActivityTypes</key>
	<array>
		<string>se.danielhindrikes.DemoApp.sendMessage</string>
		<string>se.danielhindrikes.DemoApp.showUnreadMessages</string>
	</array>

Shortcuts are created in the ViewController that it should be shortcut to. The idea is that shortcuts should be created for pages in the apps that you use often.

If the apps have a minimum iOS version that is lower than 12, we need to verify that the user running the app on iOS 12 or higher.

if (UIDevice.CurrentDevice.CheckSystemVersion(12, 0))
{
    //Add code here
}

Next step is to create a UserActivity (shortcut). Here we creating a separate method for that.

public const string ViewMenuActivityType = "se.danielHindrikes.DemoApp.sendMessage";
 
private NSUserActivity GetSendMessageUserActivity()
{
      //Create a user activity and make it eligible for Siri to find for search and predictions.
      var userActivity = new NSUserActivity(ViewMenuActivityType)
      {
          Title = "Send message",
          EligibleForSearch = true,
          EligibleForPrediction = true
      };
 
      //Add som attributes to the user activity
      var attributes = new CSSearchableItemAttributeSet("SendMessage")
      {
          ThumbnailData = UIImage.FromBundle("Icon.png").AsPNG(),
          Keywords = "send, message, daniel, hindrikes",
          DisplayName = "Send message",
          ContentDescription = "Send message in the Daniel Hindrikes Demo App"
      };
 
      userActivity.ContentAttributeSet = attributes;
 
      //Add a suggestion for a phrase the user can use to activate the shortcut via Siri.
      var phrase = "Send message in the Daniel Hindrikes Demo App";
      userActivity.SuggestedInvocationPhrase = phrase;
      return userActivity;
}

When we have created the UserActivity we can use it to set the UserActivity property of the ViewController. We will do this in the ViewDidLoad override.

public override void ViewDidLoad()
{
     base.ViewDidLoad();
 
     if (UIDevice.CurrentDevice.CheckSystemVersion(12, 0))
     {
           UserActivity = GetSendMessageUserActivity();
           UserActivity.BecomeCurrent();
     }
}

If we are developing a Xamarin.Forms app, we can access the ViewController via a Custom Renderer for the page.

[assembly: ExportRenderer(typeof(SendMessageView), typeof(CustomSendMessageViewViewRenderer))]
namespace Se.Co.App.iOS.Renderers
{
    public class CustomSendMessageViewViewRenderer : PageRenderer
    {
 
        public override void ViewDidLoad()
        {
            base.ViewDidLoad();
 
            if (UIDevice.CurrentDevice.CheckSystemVersion(12, 0))
            {
                 ViewController.UserActivity = GetShowStoresUserActivity();
                 ViewController.UserActivity.BecomeCurrent();
            }
        }

The last thing we need to do is to add code to the AppDelegate.cs that handles what should happen when a shortcut is activated.

public override bool ContinueUserActivity(UIApplication application, NSUserActivity userActivity, UIApplicationRestorationHandler completionHandler)
{
            if(userActivity.ActivityType == SendMessageViewController.ViewMenuActivityType)
            {
                //Navigate to the send message view.
 
                return true;
            }
            else if (userActivity.ActivityType == ShowUnreadMessagesViewController.ViewMenuActivityType)
            {
                //Navigate to the show unread messages view
 
                return true;
            }
 
            return base.ContinueUserActivity(application, userActivity, completionHandler);
}

To make it easier to test the shortcuts go to Settings and then to Developer and then enable, Display Recent Shortcuts and Display Donations on Lock Screen.

Now when you have deployed the app and run the views that added UserActivities, you will see shortcuts when you are swiping down at the home screen. You can also see all shortcuts if you open Siri under Settings.

Read more about how to use SiriKit in Xamarin.iOS here, https://docs.microsoft.com/en-us/xamarin/ios/platform/sirikit/

Using Machine Learning and Azure Custom Vision for image classification in your apps

Last fall, me, my family and a friends family took a trip to the forest for collection mushrooms. This blog post will not be about the trip to the forest, but that my friend not was good to recognize mushroom species I got an idea to write an app for that. So this post will be about how I wrote that app using Azure and Xamarin.

Train a model

The first thing we need to do to detect mushrooms in photos is to train a model that I can use to make predictions. In Azure Cognitive Services there is a service called "Custom Vision". We can do two things with custom vision, we can use image classification by upload images and tag them and we can upload images and tag specific areas in the image so the trained model can be used for object detection. The object detection part is in preview at the time of writing. So what I did was to upload photos of different mushrooms and tagged them. While this blog post will focus on how to consume a trained model I recommend you to read the official documentation about the custom vision service, https://docs.microsoft.com/en-in/azure/cognitive-services/custom-vision-service/home. We will also focus on the image classification part, while that is enough to do a mushroom recognition app and the trained object detection models is not able to export right now. And in an app like this is really nice to be able to do classification without an internet connection.

Run prediction on a device using an exported model

When we are doing image classification in Custom Vision we can export models for doing prediction locally on the user's device, that makes it possible to do predictions without any connection to the service in Azure.

While I need to do the prediction in the platform project I created an interface to so I can use it from shared code.

public interface IImageClassifier
{
        event EventHandler<ClassificationEventArgs> ClassificationCompleted;
        Task Classify(byte[] bytes);
}
 
public class ClassificationEventArgs : EventArgs
{
     public Dictionary<string, float> Classifications { get; private set; }
 
     public ClassificationEventArgs(Dictionary<string, float> classifications)
     {
          Classifications = classifications;
     }
}

We will get the result from the classifier as a Dictionary of tags and how confident the result is. In this case, we show that we could identify a mushroom if the classification has a confidence that is higher than 90 percent.

public void DoClassification(byte[] bytes)
{
     classifier.ClassificationCompleted += ClassificationCompleted;
     classifier.Classify(bytes);
}
 
 
private void ClassificationCompleted(object sender, PredictedEventArgs e)
{
            var top = e.Predictions.OrderByDescending(x => x.Value).First();
 
            if(top.Value > 0.9)
            {
                //Show what mushroom that was in the photo
            }
            else
            {
                //Handle that mushrooms not could be identified
            }
}

Using CoreML on iOS

CoreML is built-in in iOS from iOS 11 and above.

Import the model

While we have exported our model you need to import it to your iOS project, the mode should be placed in the Resources folder. When the model is added to the Resources folder next step is to use it in the code. Before we can use the model, we need to compile it. If we want we can pre-compile it with Xcode, but the compilation is really fast so in this case, it not necessary. After we have compiled the model we can use the compiled model url to store it in a reusable place, so we don't have to compile the model next time you want to use it. The code example below is not covering to store the compiled model.

var assetPath = NSBundle.MainBundle.GetUrlForResource("mushroom", "mlmodel");
 
var compiledUrl = MLModel.CompileModel(assetPath, out var error);
 
var model = MLModel.Create(compiledUrl, out error);

Make classifications

Now we have a model that we can start to use for classifications.

Before we are doing the prediction we will create a method to handle the result from the classification. When we get the result we will put it in a dictionary with the tag and how confident the result is for that tag.

void HandleVNRequest(VNRequest request, NSError error)
{
     if (error != null) return;
 
     var predictions = request.GetResults<VNClassificationObservation>().OrderByDescending(x => x.Confidence).ToDictionary(x => x.Identifier, x => x.Confidence);
 
     ClassificationCompleted?.Invoke(this, new ClassificationEventArgs(predictions));
}

Now when we have a method that makes the request for classification:

var classificationRequest = new VNCoreMLRequest(VNCoreMLModel.FromMLModel(model, out error), HandleVNRequest);
 
var data = NSData.FromArray(bytes);
var handler = new VNImageRequestHandler(data, CGImagePropertyOrientation.Up, new VNImageOptions());
handler.Perform(new[] { classificationRequest }, out error);

Using TensorFlow on Android

Android has an API for MachineLearning from version 8.1 and above, https://developer.android.com/ndk/guides/neuralnetworks/. But while many Android devices running on a lower Android version I have chosen to use TensorFlow. TensorFlow is open source framework for Machine Learning created by Google. To use it on Xamarin.Android, Larry O'Brian has created bindings (https://github.com/lobrien/TensorFlow.Xamarin.Android) so TensorFlow can be used with Xamarin.Android. To make classifications of images we will use his library in this example. He as also written a blog post about this on the Xamarin blog.

Import the model

When we are exporting from Custom Vision to a Tensorflow we will get a zip file that contains a model file (model.pb) and a file with the labels (labels.text). We need the labels to know what we have tagged the image while that not is included like in the CoreML model.

The model- and label file should be placed in the Asset folder of your Android project.

When we have added the model- and the label file to the Assest folder we can start to write code. The first thing we need to do is to create a TensorFlowInferenceInterface from the model and a list of strings for the labels

var inferenceInterface = new TensorFlowInferenceInterface(Application.Context.Assets, "model.pb");
var sr = new StreamReader(assets.Open("labels.txt"));
var labelsString = sr.ReadToEnd()
var labels = labelsString.Split('\n').Select(s => s.Trim())
                         .Where(s => !string.IsNullOrEmpty(s)).ToList();

Make classifications

Images need to be in 227x227 pixels, that means that the first thing you have to do is to resize the image.

var bitmap = BitmapFactory.DecodeByteArray(bytes, 0, bytes.Length);
 
var resizedBitmap = Bitmap.CreateScaledBitmap(bitmap, 227, 227, false)
                          .Copy(Bitmap.Config.Argb8888, false);

TensorFlow models exported from Custom Vision cannot handle images, so the image needs to be converted to binary data. The images need to be converted to a float array of point values, one per red, green, and blue value for each pixel, and also some adjustments to the color values are necessary.

var floatValues = new float[227 * 227 * 3];
var intValues = new int[227 * 227];
resizedBitmap.GetPixels(intValues, 0, 227, 0, 0, 227, 227);
 
for (int i = 0; i < intValues.Length; ++i)
{
     var val = intValues[i];
     floatValues[i * 3 + 0] = ((val & 0xFF) - 104);
     floatValues[i * 3 + 1] = (((val >> 8) & 0xFF) - 117);
     floatValues[i * 3 + 2] = (((val >> 16) & 0xFF) - 123);
}

The last step is to do the classification. To get the result you need to create a float array and send that into the Fetch method. The last step is to map the output to a label.

var outputs = new float[labels.Count];
inferenceInterface.Feed("Placeholder", floatValues, 1, 227, 227, 3);
inferenceInterface.Run(new[] { "loss" });
inferenceInterface.Fetch("loss", outputs);
 
var result = new Dictionary<string, float>();
 
for (var i = 0; i < labels.Count; i++)
{
     var label = labels[i];
 
     result.Add(label, outputs[i]);
}
 
PredictionCompleted(this, new PredictedEventArgs(result));

Store user data in an secure way

In many apps you want to store user data locally on the device, it could, for example, be passwords, credit card numbers etc. Even if the storage is sandboxed to your apps, you don't want to store it in clear text, you want to store it encrypted.

I have used Xamarin.Auth for many apps while it has an AccountStore class that can be used to store user data encrypted. But while it only supports iOS and Android and needed support for UWP in an app I decided to create my own library. I also felt that I don't want to install a big library when I just wanted one a little piece of it, and furthermore, the main focus was not storing user data encrypted.

So I decided to create TinyAccountManager, it is an open source project where the source can be found on GitHub, https://github.com/dhindrik/TinyAccountManager. It works together with iOS, Android and UWP. And I will properly add support for Mac apps as well.

The easiest way to install it tou your projects is via NuGet, https://www.nuget.org/packages/TinyAccountManager/.


Install-Package TinyAccountManager

The first you need to do is to initialize the AccountManager per platform.

//iOS
TinyAccountManager.iOS.AccountManager.Initialize();
 
//Android
TinyAccountManager.Droid.AccountManager.Initialize();
 
//UWP
TinyAccountManager.UWP.AccountManager.Initialize();

Save
The only property that are required is ServiceId.

var account = new Account()
{
    ServiceId = "TinyAccountManagerSample",
    Username = "dhindrik"
};
 
account.Properties.Add("Password", "MySecretPassword");
 
await AccountMananger.Current.Save(account);

Get and Exists

It's recommended that you use Exists before Get, if you using Get and there is no matching account it will throw an exception.

Account account = null;
 
var exists = await AccountManager.Current.Exists("TinyAccountManagerSample")
 
if(exists)
  account = await AccountManager.Current.Get("TinyAccountManagerSample")
Remove
 
await AccountManager.Current.Remove("TinyAccountManagerSample")

IOC

If you want to use IOC instead of the singleton pattern, you just register the implemenation for each platform with the IAccountManager interface. If you select this way you don't have to run Initialize on each platform

iOS: iOSAccountManager

Android: AndroidAccountManager

UWP: UWPAccountManager

You can find the complete documentation on GitHub, there are also a sample project.
https://github.com/dhindrik/TinyAccountManager

iOS 11 Large Titles in Xamarin.Forms apps

In iOS 11 Apple introduce large tiles in their design language. You can read more about it on the Xamarin blog, https://blog.xamarin.com/go-large-ios-11/, this post will focus on how to implement that in Xamarin.Forms.



Note: There is a pull request for Xamarin Forms about iOS11 fixes created by Rui Marinho, so this will probably be in Xamarin.Forms out of the box soon, https://github.com/xamarin/Xamarin.Forms/pull/1238

The way to do it is to create a custom page renderer, but I don't want it to affect every page, I want to control it on each page. While I don't want to create a new base page I decided to use attached properties.

So the first thing I do is to create a property to attach.

public class LargeTitle
{
        public static readonly BindableProperty ShowLargeTitleProperty =
            BindableProperty.CreateAttached("ShowLargeTitle", typeof(bool), typeof(LargeTitle), false);
 
        public static bool GetShowLargeTitle(BindableObject view)
        {
            return (bool)view.GetValue(ShowLargeTitleProperty);
        }
 
        public static void SetShowLargeTitle(BindableObject view, bool value)
        {
            view.SetValue(ShowLargeTitleProperty, value);
        }
}

After that, I can use it in the XAML. I just have to import the namespace.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"            
             xmlns:ap="clr-namespace:Demo.Client"
             x:Class="Demo.Client.Views.MainView" Title="This is a large title"
             ap:LargeTitle.ShowLargeTitle="True">
    <ContentPage.Content>
        <Grid Padding="10">
            <Button Text="Get started" HorizontalOptions="Fill" VerticalOptions="Center" Command="{Binding GetStarted}" />
        </Grid>
    </ContentPage.Content>
</ContentPage

The last thing is to create the custom renderer for iOS. I will override the ViewWillAppear method to set the PreferLargeTitle method to the value of the ShowLargeTitle property that I set the value for in the XAML. And I will also override the ViewDidDisappear to restore the value to false, to ensure that it not will be large on pages I don't want it to be large on.

public class CustomPageRenderer : PageRenderer
    {
        public override void ViewWillAppear(bool animated)
        {
            base.ViewWillAppear(animated);
 
            var showLargetTitle = LargeTitle.GetShowLargeTitle(Element);
 
            if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0) && NavigationController != null && NavigationController.NavigationBar != null)
            {
                NavigationController.NavigationBar.PrefersLargeTitles = showLargetTitle; 
            }
        }
 
        public override void ViewDidDisappear(bool animated)
        {
            base.ViewDidDisappear(animated);
 
            if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0) && NavigationController != null && NavigationController.NavigationBar != null)
            {
                NavigationController.NavigationBar.PrefersLargeTitles = false;
            }
        }
    }

Add custom tiles to map in Xamarin Forms

If we want to use other maps than the platforms default in our apps we need to provide tiles to the map view. To do that we need to create a custom renderer per platform.

iOS
In iOS we need to create an url template that contains {x}, {y} and {z}. Those be replaced with values from the map engine.

protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
    if(e.NewElement != null)
    {
        var map = (MKMapView)Control;
 
        var urlTemplate = "https://urltomaptiles/{x}/{y}/{z}";
        var tileOverlay = new MKTileOverlay(urlTemplate);
 
        map.OverlayRenderer = OverlayRenderer;
 
        map.AddOverlay(tileOverlay);
    }
}
private MKOverlayRenderer RenderOverlay(MKMapView mapView, IMKOverlay overlay)
{
    var tileOverlay = overlay as MKTileOverlay;
 
    if(tileOverlay != null)
    {
         return new MKTileOverlayRenderer(tileOverlay);
    }
 
    return new MKOverlayRenderer(overlay);
}

If we are getting tiles from a service that not supporting the url format with x-,y- and z value we can customize the url. To do that we need to subclass MKTileOverlay and override the URLForTilePath method. In that method we will write the code that created the url. I recommend to create a helper class for that so we can reuse it on the other platforms.

public class CustomTileOverlay : MKTileOverlay
{
     public override void LoadTileAtPath(MKTileOverlayPath path, MKTileOverlayLoadTileCompletionHandler result)
     {
         base.LoadTileAtPath(path, result);
     }
 
     public override NSUrl URLForTilePath(MKTileOverlayPath path)
     {
         //Here we write the code for creating the url.
         var url = MapHelper.CreateTileUrl((int)path.X, (int)path.Y, (int)path.Z);
 
         return new NSUrl(url);
     }
}

Instead of creating a MKTileOverlay we will create a CustomTileOverlay and add it to the map.

map.AddOverlay(new CustomTileOverlay());

Android
Except to subclass MapRenderer we also need to implement the IOnMapReadyCallback interface. The method OnMapReady will handle when the GoogleMap object is ready so we can work with it. But first we need to request the GoogleMap object in the override of the OnElementChanged method.

public class ExtendedMapRenderer : MapRenderer, IOnMapReadyCallback
{
    protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.View> e)
    {
        base.OnElementChanged(e);
 
        if(e.NewElement != null)
        {
            ((MapView)Control).GetMapAsync(this);    
        }
    }
 
    public void OnMapReady(GoogleMap googleMap)
    { 
        var options = new TileOverlayOptions();
        options.InvokeTileProvider(new CustomTileProvider());
        googleMap.AddTileOverlay(options);
    }
}

In Android we always need to create an own tile provider.

public class CustomTileProvider : UrlTileProvider
{
    public CustomTileProvider() : base(256,256) {}
 
    public override URL GetTileUrl(int x, int y, int zoom)
    {
        //Here we write the code for creating the url.
        var url = MapHelper.CreateTileUrl(x, y, zoom);
 
        return new URL(url);
    }
}

UWP (Windows 10)
As in iOS, UWP using an url that contains {x},{y} and {z} that will be replaced by the map engine.

protected override void OnElementChanged(ElementChangedEventArgs<Map> e)
{
    base.OnElementChanged(e);
 
    if (e.NewElement != null)
    {
        map = Control as MapControl;
 
        HttpMapTileDataSource dataSource = new HttpMapTileDataSource("https://urltomaptiles/{x}/{y}/{z}");      
        MapTileSource tileSource = new MapTileSource(dataSource);
        map.TileSources.Add(tileSource);
 
     }
}

If we want to modify the url we using the UriRequested event on HttpMapTileDataSource.

HttpMapTileDataSource dataSource = new HttpMapTileDataSource();
dataSource.UriRequested += DataSource_UriRequested;

The code for modifying the url is placed in the event handler.

private void DataSource_UriRequested(HttpMapTileDataSource sender, MapTileUriRequestedEventArgs args)
{
    var deferral = args.Request.GetDeferral();
 
    //Here we write the code for creating the url.
    var url = MapHelper.CreateTileUrl(args.X, args.Y, args.ZoomLevel);
    args.Request.Uri = new Uri(url);
 
    deferral.Complete();
}

MvvmLight Navigation Extension

When navigating in iOS we can choose to do a modal navigation. That means that we open a page that is on top of the other pages and not included in the navigation stack. When using MvvmLight we only have one method (NavigateTo) when we want to open a new page.

While I want to use MvvmLight and open "modals" I have created a MvvmLight extension for iOS (for storyboards only in this pre version) and Android, https://www.nuget.org/packages/MvvmLightNavigationServiceExtension. If you're interested in the source, it will be at GitHub, https://github.com/dhindrik/MvvmLightNavigationExtension.

While this is a pre release, feedback is very welcome!

Using the extension from shared code
To use it in your ViewModels you need to add the namespace to the class.

using MvvmLightNavigationExtension;
var navigation = ServiceLocator.Current.GetInstance();
navigation.OpenModal("Page2");

Setup
We will configure the NavigationService in same way as when we using NavigationService from MvvmLight but we using NavigationServiceExtension() instead of NavigationService and our instance of NavigationServiceExtension should be registered to both INavigationService and INavigationServiceExtension.

iOS:

 var nav = new MvvmLightNavigationExtension.iOS.NavigationServiceExtension();
 nav.Initialize((UINavigationController)Window.RootViewController);
 nav.Configure("Page1", "MainView");
 nav.Configure("Page2", "PageView");
 
container.RegisterInstance(nav);
container.RegisterInstance(nav);

Android:

 var nav = new MvvmLightNavigationExtension.Droid.NavigationServiceExtension();
 nav.Initialize():
 nav.Configure("Page1", "MainView");
 nav.Configure("Page2", "PageView");
 
container.RegisterInstance(nav);
container.RegisterInstance(nav);

Use MvvmLight in Xamarin.iOS

I think MVVM is a good design pattern to separate UI and business logic. MvvmLight is one of the most popular frameworks for MVVM. In this blog post I will show how you can use MvvmLight when we're building a iOS app with Xamarin.

The fist step is to create a view model. I always have my view models in a PCL (Portable Class Library) so I can share the view models with other platforms, Android and Windows for example.

When I have created a new project I will install MvvmLight to it. The easiest way to do that is to install the NuGet package for MvvmLight. The package should be added to both the PCL project and the iOS project.

Install-package MvvmLightLibs

When we are working with MVVM all ViewModels need to implement the INotifyPropertyChanged interface. INotifyPropertyChanged has one event handler, PropertyChanged that we have to implement. When using MvvmLight this is done in a base class called ViewModelBase.

In my example the viewmodel

public class MainViewModel : ViewModelBase
{
        private string _name;
 
        public string Name
        {
            get
            {
                return _name;
            }
            set
            {
                _name = value;
                RaisePropertyChanged("Name");
            }
        }
 
        public RelayCommand Send
        {
            get
            {
                return new RelayCommand(() =>
                {
                    var nav = ServiceLocator.Current.GetInstance<INavigationService>();
                    nav.NavigateTo(Views.Hello.ToString(), Name);  
                });
            }
        }
}

Navigation

MvvmLight has a INavigationService interface that uses for navigation and each platform will have their own implementation. On iOS you need to create a NavigationService and configure it in the AppDelegate class.

var nav = new NavigationService();
nav.Initialize((UINavigationController)Window.RootViewController);
nav.Configure(Views.Main.ToString(), "MainView");
nav.Configure(Views.Hello.ToString(), "HelloViewController");

In my example I using Autofac for IoC, we can also use the IoC container that is in the MvvmLight package. When we have created the NavigationService we had to register it in the IoC container as a INavigationService.

var builder = new ContainerBuilder();
builder.RegisterInstance<INavigationService>(nav);
 
builder.RegisterType<MainViewModel>();
builder.RegisterType<HelloViewModel>();
 
var container = builder.Build();
 
var serviceLocator = new AutofacServiceLocator(container);
 
ServiceLocator.SetLocatorProvider(() => serviceLocator);

If you're using storyboards you have to use the Configure(string key, string storyboardId) method. The key is just a key that we set to the view, we will use that key when we will navigate to the view. (See the code fore the ViewModel above. StoryboardId is a property that we're setting in the storyboard designer.

Properties

Data bindings

When using MVVM we want to use data bindings. In iOS we have to create the bindnings in code. MvvmLight will help us with that. In the class for the UIViewController we hade to add a using to the MvvmLight helpers.

using GalaSoft.MvvmLight.Helpers;

The MvvmLight helper namespace will contains the extension methods SetBinding and SetCommand.

When binding to a UITextField we can set which event who will trigger an update of the our ViewModel with the method UpdateSourceTrigger. WhenSourceChange is used to set which property in the ViewModel who will be updated.

The SetCommand method's first argument will be which event that will execute the command.

public override void ViewDidLoad()
{
            base.ViewDidLoad();
 
           //Creates a binding to a UILabel
            this.SetBinding(() => ViewModel.Name, () => MyLabel.Text);
 
           //Creates a binding to a UITextField
            this.SetBinding(() => Name.Text).
                UpdateSourceTrigger("EditingChanged").
                WhenSourceChanges(() => ViewModel.Name = MyTextField.Text);
 
            //Binding a command to the Button.
            MyButton.SetCommand("TouchUpInside", ViewModel.Send);
}

The complete code for this sample is on GitHub, https://github.com/dhindrik/XamarinMvvmLightSample

How to succeed with Xamarin.Forms

Xamarin.Forms makes it possible to write UI for all the major mobile platforms, iOS, Android and Windows Phone with one shared code base. Many developers think that Xamarin.Forms isn't is good enough to create apps that will be published and they think Xamarin.Forms is more a tool for prototyping.

If you think Xamarin.Forms is a "magic" product that will fix everything, you will properly not succeed with Xamarin.Forms. For me Xamarin.Forms is a framework that helps me build apps for multiple platforms. If you look at Xamarin.Forms in that way you will increase your chances to success with Xamarin.Forms.

Xamarin.Forms is delivered with a lot of controls that uses the Xamarin.Forms framework to render native platform specific controls. A Xamarin.Forms control is in many way just a description of what the control can do. From that description is native controls rendered.

The power of Xamarin.Forms is that you can use the framework to create you own renderers if you want to renderer a control different then the standard renderer. You can also create your own controls by using custom renderers.

I guess one of the most common issues with Xamarin.Forms i ListView performance. It's not surprising, you maybe have 5 or 6 controls for each cell (row). If cells are reused you might have 8 cells. It means that forms need to create at least 40 renderer objects. And if any of the controls is a StackLayout or a RelativeLayout where it's render has do to a lot of calculations where to place controls I guess you will realize that it will use a lot of memory.

So if you instead create your own custom cell that has it own renderer, there will be only one renderer for each row or if you write a renderer for the whole list you will only have one renderer. Can you realize how much memory you will save on that? If not you will see it when you are scrolling in your ListView,

The biggest problem when using Xamarin.Forms is that the developers don't know how Xamarin.Forms works and they don't know much about the target platforms. If you want to create a excellent app with Xamarin.Forms you still need to have knowledge about the target platforms.

Now you maybe want to ask me why you should use Xamarin.Forms? The answer is that even if you have to write platform specific code for some views there are still much you can use of the controls that is delivered with Xamarin.Forms out of the box and the powerful Xamarin.Forms framework makes it possible to write platform specific code when what you get out of the box with Xamarin.Forms not is enough.

My recommendation is to so do as much as possible with what you get out of the box with forms and don't care about performance and if it doesn't look perfect from the beginning. When you have created your app and built all the business logic, then you can start to look at how to make the app perfect. Than you can start write platform specific code to get better performance and a better look of the app.

TechDays 2015

Den 20 oktober börjar TechDays med förkonferens, jag och Johan Karlsson kommer att dela med oss av våra kunskapar gällande cross-platformutveckling.

Att bygga cross-platformlösningar är alltid en stor utmaning då det i grunden är olika operativsystem, programmeringsspråk och utvecklingsmiljöer.
Xamarin löser en stor del av cross platform problematiken genom att man kan utveckla appar för iOS, OSX och Android med C#. iOS och Android kan även byggas i Visual Studio. Under dagen kommer vi gå genom hur man bygger arkitektur och gränssnitt för en optimal native upplevelse på varje plattform, det vill säga iOS, OSX, Android och Windows. Vi kommer också att visa hur man kan testa mobila applikationer på ett effektivt sätt med hjälp av Xamarin TestCloud.

Läse mer här, http://tdswe.kistamassan.se/Program-2015/Sessioner/Pre-Conf-Cross-Platform-pa-ratt-satt