IBTech Xamarin Mobile Header

IBTech is the IT subsidiary of QNB Finansbank, one of the top 5 private banks of Turkey. IBTech currently has a BYOD (bring your own device) strategy for their employees’ mobile phones, so as a result, employees use different types of mobile devices and operating systems. IBTech would like to provide an internal mobile app for its 500+ employees to work across all devices. The company also wants to avoid the cost of developing and testing the application for each platform (namely iOS and Android). This case study describes how IBTech partnered with Microsoft to build an internal application for the QNB Finansbank IT department to meet these needs.

Technologies used

Core team

IBTech Mobile Application Team


  • Ibrahim Kivanc – Senior Technical Evangelist, Microsoft
  • Gökhan Çamaş – Expert Designer/Technical Lead, IBTech
  • Serdar Sert – Senior Designer, IBTech
  • Ulaş Ergin – Consulting Designer/Team Leader, IBTech

Customer profile

IBTech is the IT subsidiary of QNB Finansbank, one of the top 5 private banks of Turkey. Established on the foundation of the QNB Finansbank Information Technologies team, IBTech has been developing competitive products since 2005, as well as operating the IT infrastructure of Finansbank.

The IBTech Software Infrastructure team has a lot of experience providing mobility solutions for QNB Finansbank on various mobile devices. The team has experience on both business-to-business (B2B)/internal applications and business-to-consumer (B2C)/consumer applications.

To maximize ROI for their mobile projects, the team worked with many native and cross-platform development tools and compared benchmarks including user experience, performance, and ease of use. The team has native mobile application development experience; however, they don’t have a Mobile DevOps approach internally.

During this project, IBTech implemented their first Mobile DevOps approach and is looking to expand this experience into different projects and teams at IBTech and QNB Finansbank. The IBTech Software Infrastructure team published an article about this at dzone.com Cross Platform Mobile Development Alternatives. Xamarin was on top of the list, and the team wanted to build a project that encompassed the entire application lifecycle management (ALM) process with an internal application.

The team also compiled their resources in presentations to catch up on the latest technologies and share these learnings at their IBTSMG Slide Share Account.

IBTech Xamarin Mobile Application


This work is a great example of how Azure services can be implemented into enterprise solutions.

“We needed native performance and user experience, but we were reluctant to invest time and resources to build the application twice (in Android and iOS), and Xamarin gave/provided us what we needed” — Ulaş Ergin, Team Leader

Problem statement

Because IBTech has a BYOD (bring your own device) strategy for their employees’ mobile phones, IBTech employees use different types of mobile devices and operating systems. IBTech would like to provide an internal mobile app for its 500+ employees, which they want to build by using native features to work on all devices. This is a challenge for both development and test processes for any mobile application. While the team has native mobile application development experience, they don’t have an internal Mobile DevOps approach.

IBTech’s employee profile includes software architects, software engineers, business analysts, test engineers, and system administrators (UNIX and Windows, database, network, security, and storage).

Because this is a common app, they were looking for native performance and native user experiences. The use of a hybrid approach (embedding a WebView) is not optimal because the user base consists mostly of high-tech IT profile users. The company also wants to avoid the cost of developing and testing the application for each platform (namely iOS and Android).

Solution, steps, and delivery

IBTech built an internal application for the QNB Finansbank IT department, basically a social hub for many needed services for all employees. Following are some common services they’re using in their application:

  • PC password reset
  • Requests for shuttle services
  • Special campaigns for employees
  • Release calendar for products
  • Payment transactions
  • Cafeteria menu
  • Approvals via mobile app

The app also manages other areas such as internal announcements, IM messaging, and vacation requests.

IBTech Bank Xamarin Mobile Application Architecture Diagram


In this section:

Tools used in the solution

To develop the mobile application, we decided to use Xamarin.Forms. The Xamarin Forms approach enables developers to take advantage of the productivity and power of .NET to build mobile apps, and to use C# to write to the full set of native APIs and mobile capabilities provided by each device platform. This enables developers to easily share common app code across their iOS and Android apps while still delivering fully native experiences for each of the platforms. The app uses REST API requests and responses to get or post data. Visual Studio and Xamarin Studio on Mac devices provide a rich mobile development offering that enables existing .NET developers.

From a Mobile DevOps perspective, automated testing with Xamarin Test Cloud solved the need to test for a variety of devices with various operating system versions, screen sizes, and brands. The team spends less time testing, saving time and money on every sprint. By using Xamarin Test Cloud, the team was also able to incorporate an innovative feature in the final product.

Another way to save time and cost was by using continuous delivery with Visual Studio Team Services, which helped the team to reduce time on operations, while delivering new packages and deploying on several devices with versioning.

To help business units and project management teams better understand their users, the team used Application Insights, HockeyApp, and Power BI. Learning their users’ behavior enabled them to more easily determine which feature was their favorite and which function they most often needed. This also helped them plan their next releases based on telemetry data tracked by Application Insights. By incorporating HockeyApp Crash Reporting and Analytics, the team created a mechanism to better understand user interactions and application stability. Power BI Embedded became a major tool to monitor user behavior through Application Insights analytics data.

And finally, Azure Notification Hubs was used to send push notifications.

Architecture

IBTech decided to build this application for internal use only, and will use their MobileIron mobile device management (MDM) platform to distribute it to users. IBTech has started designing their solution architecture to make it ready for production, and all systems have started using their internal resources.

The application is retrieving data from several existing back-end systems. In addition to their existing backend, the team has planned to integrate it with the Azure cloud platform.

IBTech Bank Xamarin Mobile Application Architecture Diagram


In the banking industry in Turkey, regulations dictate that you can’t store personal and banking data outside of Turkey. While we were working on this mobile app, we considered in which scenarios we can use Azure; these are explained throughout the remainder of this document.

IBTech GitHub account and code samples

The IBTech Software Infrastructure team created an account on GitHub to share their learnings from a technical perspective. They became a very good role model for sharing their knowledge to the developer community via their technical articles and code implementation samples at IBTech Software Architecture Team (IBTSMG).

“Our goal in a cross-platform product is to reduce our development costs and increase efficiency. We’ve been following Xamarin for a long time, and before making a decision, we researched all alternatives. We even published an article on cross-platform development alternatives. At the end, we are happy with our decision.” — Serdar Sert, Senior Designer, IBTech

Also, core common features of a basic solution have been compiled in a sample by technical evangelists on GitHub in the Firarperest Project (see Additional resources).

In this section:

Custom renderers sample

Xamarin.Forms user interfaces are rendered by using the native controls of the target platform, allowing Xamarin.Forms applications to retain the appropriate look and feel for each platform. Custom renderers allow developers to override this process to customize the appearance and behavior of Xamarin.Forms controls on each platform.

IBTech decided to implement a numeric keyboard feature to iOS by using a custom renderer: IBTech Xamarin Custom Renderer Sample on GitHub.

Code snippet for Home screen: XamarinCustomRendererSample/iOS/LaunchScreen.storyboard

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9532" systemVersion="15D21" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
	<dependencies>
		<deployment identifier="iOS" />
		<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9530" />
	</dependencies>
	<scenes>
		<!--View Controller-->
		<scene sceneID="EHf-IW-A2E">
			<objects>
				<viewController id="01J-lp-oVM" sceneMemberID="viewController">
					<layoutGuides>
						<viewControllerLayoutGuide type="top" id="Llm-lL-Icb" />
						<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok" />
					</layoutGuides>
					<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
						<rect key="frame" x="0.0" y="0.0" width="600" height="600" />
						<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES" />
						<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite" />
					</view>
				</viewController>
				<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder" />
			</objects>
			<point key="canvasLocation" x="53" y="375" />
		</scene>
	</scenes>
</document>


Numeric keyboard custom renderer implementation: XamarinCustomRendererSample/iOS/Renderers/NumericEntryRenderer.cs

using System;
using CoreGraphics;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using XamarinCustomRendererSamples.iOS;

[assembly: ExportRenderer(typeof(Entry), typeof(NumericEntryRenderer))]
namespace XamarinCustomRendererSamples.iOS
{
	public class NumericEntryRenderer : EntryRenderer
	{
		protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
		{
			base.OnElementChanged(e);

			try
			{
				if (e.NewElement != null && e.NewElement.Keyboard == Keyboard.Numeric)
				{
					var toolbar = new UIToolbar(new CGRect(0.0f, 0.0f, Control.Frame.Size.Width, 44.0f));

					toolbar.Items = new[]
					{
						new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace),
						new UIBarButtonItem(UIBarButtonSystemItem.Done, delegate { Control.ResignFirstResponder(); })
					};

					this.Control.InputAccessoryView = toolbar;
				}
			}
			catch (Exception) { }
		}
	}
}


Also, you can find a CustomSearchBar renderer control in this directory: XamarinCustomRendererSample/iOS/Renderers/CustomSearchBarRenderer.cs.

IBTech Bank Xamarin Mobile Application Architecture Diagram


Custom plug-in sample

The VMobile app uses asymmetric cryptography (public key cryptography) to remember already registered users. To provide this feature, a user-specific private key is generated at enrollment to create a signature at sign-in. This signature contains user information as well as device and platform-specific information to improve security. The Xamarin.Forms custom plug-in feature was used to improve security according to the device and platform information.

Code snippet for XAML code for XamarinCryptoPluginSample/XamarinCryptoPluginSample/HomePage.xaml

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="XamarinCryptoPluginSample.HomePage">
	<ContentPage.Content>
		<StackLayout HorizontalOptions="Center" Margin="10,30,10,10">
			<Label Text="Xamarin Plugin Sample - Crypto Plugin" FontAttributes="Bold"></Label>
			<Entry x:Name="PlainTextEntry" Placeholder="Plain text..."></Entry>
			<Button Text="Sign with private key" Clicked="SignPlainText"></Button>
			<Label x:Name="SignedDataLabel"></Label>
		</StackLayout>
	</ContentPage.Content>
</ContentPage>


C# code snippet for code behind XamarinCryptoPluginSample/XamarinCryptoPluginSample/HomePage.xaml.cs

using System;
using System.Collections.Generic;

using Xamarin.Forms;
using XamarinCryptoPluginSample.Plugins.Crypto.Abstractions;

namespace XamarinCryptoPluginSample
{
	public partial class HomePage : ContentPage
	{
		public HomePage()
		{
			InitializeComponent();
		}

		private static readonly string privateKey = "[YOUR_CERTIFICATE_PRIVATE_KEY]";

		void SignPlainText(object sender, EventArgs e)
		{
			var cryptoManager = DependencyService.Get<ICryptoManager>();
			var signedData = cryptoManager.SignData(privateKey, PlainTextEntry.Text);
			SignedDataLabel.Text = signedData;
		}
	}
}


C# code snippet for creating this implementation XamarinCryptoPluginSample/Droid/MainActivity.cs

using System;
using System.Security.Cryptography;
using System.Text;
using XamarinCryptoPluginSample.Plugins.Crypto.Abstractions;

namespace XamarinCryptoPluginSample.Plugins.Crypto.Droid
{
	public class CryptoManager : ICryptoManager
	{
		public string SignData(string privateKey, string data)
		{
			string signedData = null;
			try
			{
				using (var rp = new RSACryptoServiceProvider())
				{
					var xmlString = Encoding.UTF8.GetString(Convert.FromBase64String(privateKey));
					rp.FromXmlString(xmlString);

					var encoding = new ASCIIEncoding();
					byte[] orginalData = encoding.GetBytes(data);


					var signedBinaryData = rp.SignData(orginalData, new SHA1CryptoServiceProvider());
					signedData = Convert.ToBase64String(signedBinaryData);
				}
			}
			catch (Exception exception)
			{
				// some error logging code here...
				throw;
			}

			return signedData;
		}
	}
}


MobileIron integration with Xamarin app

MobileIron is a mobile device management (MDM) solution for enterprises that provides a tunneling feature to safely reach internal addresses from the WAN. To use the MobileIron AppTunnel tunneling feature, some prerequisites are required for iOS and Android platforms. Xamarin provides all these prerequisites, and the applications that are developed using Xamarin work perfectly with MobileIron.

  • iOS. Apps built with the Xamarin development platform can access network servers in various ways. AppTunnel with HTTP/S tunneling is supported as follows:
    • The app uses the NSURLConnection or NSURLSession API exposed to C# through the Xamarin.iOS binding.
    • The app uses the ModernHttpClient library with NSURLSession. The ModernHttpClient library with CFNetwork will not work.
  • Android. Apps built with the Xamarin development platform that use ModernHTTPClient and OkHttp are suitable for the MobileIron AppTunnel tunneling feature. HTTP tunneling along with Kerberos constrained delegation are also supported when using these libraries.

Remote notifications with Firebase Cloud Messaging integration

IBTech has implemented an in-app messaging feature via remote notifications, and an information notifications feature is planned to be added in the next release. To use IBTech’s internal notification gateway application that works on the Firebase Cloud Messaging (FCM) service, the following guides have been seamlessly applied step-by-step:

The team shared their sample application via their official GitHub account at Xamarin Azure Notification Hub Sample.

Code snippet for remote notifications registration in shared Portable Class Library in App.xaml.cs

using Xamarin.Forms;

namespace AzurePush
{
	public partial class App : Application
	{
		public const string SenderID = "xxxxxx"; // Google API Project Number
		public const string ListenConnectionString = "Endpoint=sb://ibtxamarin.servicebus.windows.net/;SharedAccessKeyName=DefaultFullSharedAccessSignature;SharedAccessKey=xxxxxxx";
		public const string NotificationHubName = "IBTXamarinNotificationHub";
		public App()
		{
			InitializeComponent();
			MainPage = new AzurePush.MainPage();
		}

		...
	}
}


Code snippets for platform-specific remote notifications registration for Android

MyBroadcastReceiver.cs

using Android.App;
using Android.Content;
using Android.Util;
using Gcm.Client;
using System;
using System.Collections.Generic;
using System.Text;
using WindowsAzure.Messaging;

namespace AzurePush.Droid
{
	[Service]
	public class PushHandlerService : GcmServiceBase
	{
		public static string RegistrationID { get; private set; }
		private NotificationHub Hub { get; set; }

		public PushHandlerService() : base(App.SenderID)
		{
			Log.Info(MyBroadcastReceiver.TAG, "PushHandlerService() constructor");
		}
		protected override void OnUnRegistered(Context context, string registrationId)
		{
			Log.Verbose(MyBroadcastReceiver.TAG, "GCM Unregistered: " + registrationId);

			createNotification("GCM Unregistered...", "The device has been unregistered!");
		}

		protected override bool OnRecoverableError(Context context, string errorId)
		{
			Log.Warn(MyBroadcastReceiver.TAG, "Recoverable Error: " + errorId);

			return base.OnRecoverableError(context, errorId);
		}

		protected override void OnError(Context context, string errorId)
		{
			Log.Error(MyBroadcastReceiver.TAG, "GCM Error: " + errorId);
		}
		protected override void OnMessage(Context context, Intent intent)
		{
			Log.Info(MyBroadcastReceiver.TAG, "GCM Message Received!");

			var msg = new StringBuilder();

			if (intent != null && intent.Extras != null)
			{
				foreach (var key in intent.Extras.KeySet())
					msg.AppendLine(key + "=" + intent.Extras.Get(key).ToString());
			}

			string messageText = intent.Extras.GetString("message");
			if (!string.IsNullOrEmpty(messageText))
			{
				createNotification("New hub message!", messageText);
			}
			else
			{
				createNotification("Unknown message details", msg.ToString());
			}
		}

		protected override void OnRegistered(Context context, string registrationId)
		{
			Log.Verbose(MyBroadcastReceiver.TAG, "GCM Registered: " + registrationId);
			RegistrationID = registrationId;

			createNotification("PushHandlerService-GCM Registered...",
								"The device has been Registered!");

			Hub = new NotificationHub(App.NotificationHubName,
				App.ListenConnectionString, context);
			try
			{
				Hub.UnregisterAll(registrationId);
			}
			catch (Exception ex)
			{
				Log.Error(MyBroadcastReceiver.TAG, ex.Message);
			}

			var tags = new List<string>() { };

			try
			{
				var hubRegistration = Hub.Register(registrationId, tags.ToArray());
			}
			catch (Exception ex)
			{
				Log.Error(MyBroadcastReceiver.TAG, ex.Message);
			}
		}

		void createNotification(string title, string desc)
		{
			var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
			var uiIntent = new Intent(this, typeof(MainActivity));
			var notification = new Notification(Android.Resource.Drawable.SymActionEmail, title);
			notification.Flags = NotificationFlags.AutoCancel;
			notification.SetLatestEventInfo(this, title, desc, PendingIntent.GetActivity(this, 0, uiIntent, 0));
			notificationManager.Notify(1, notification);
			dialogNotify(title, desc);
		}
		protected void dialogNotify(String title, String message)
		{
			MainActivity.instance.RunOnUiThread(() =>
			{
				AlertDialog.Builder dlg = new AlertDialog.Builder(MainActivity.instance);
				AlertDialog alert = dlg.Create();
				alert.SetTitle(title);
				alert.SetButton("Ok", delegate
				{
					alert.Dismiss();
				});
				alert.SetMessage(message);
				alert.Show();
			});
		}
	}
	[BroadcastReceiver(Permission = Constants.PERMISSION_GCM_INTENTS)]
	[IntentFilter(new string[] { Constants.INTENT_FROM_GCM_MESSAGE },
		 Categories = new string[] { "com.ibtech.xamarinfcmsample" })]
	[IntentFilter(new string[] { Constants.INTENT_FROM_GCM_REGISTRATION_CALLBACK },
		 Categories = new string[] { "com.ibtech.xamarinfcmsample" })]
	[IntentFilter(new string[] { Constants.INTENT_FROM_GCM_LIBRARY_RETRY },
		 Categories = new string[] { "com.ibtech.xamarinfcmsample" })]
	public class MyBroadcastReceiver : GcmBroadcastReceiverBase<PushHandlerService>
	{
		public static string[] SENDER_IDS = new string[] { App.SenderID };

		public const string TAG = "MyBroadcastReceiver-GCM";
	}
}


MainActivity.cs

using Android.App;
using Android.Content.PM;
using Android.OS;
using Gcm.Client;

namespace AzurePush.Droid
{
    [Activity(Label = "AzurePush", Icon = "@drawable/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        public static MainActivity instance;

        protected override void OnCreate(Bundle bundle)
        {
            instance = this;
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(bundle);

            GcmClient.CheckDevice(this);
            GcmClient.CheckManifest(this);
            GcmClient.Register(this, App.SenderID);

            global::Xamarin.Forms.Forms.Init(this, bundle);
            LoadApplication(new App());
        }
    }
}


Code snippet for platform-specific remote notifications registration for iOS; new features of iOS notification features come with iOS 10 and later.

AppDelegate.cs

using Foundation;
using Newtonsoft.Json.Linq;
using System;
using UIKit;

namespace AzurePush.iOS
{
    [Register("AppDelegate")]
    public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
    {
        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            global::Xamarin.Forms.Forms.Init();
            LoadApplication(new App());

            var settings = UIUserNotificationSettings.GetSettingsForTypes(
                 UIUserNotificationType.Alert
                 | UIUserNotificationType.Badge
                 | UIUserNotificationType.Sound,
                 new NSSet());

            UIApplication.SharedApplication.RegisterUserNotificationSettings(settings);
            UIApplication.SharedApplication.RegisterForRemoteNotifications();

            return base.FinishedLaunching(app, options);
        }
        public override void RegisteredForRemoteNotifications(UIApplication application,
     NSData deviceToken)
        {
            const string templateBodyAPNS = "{\"aps\":{\"alert\":\"$(messageParam)\"}}";

            JObject templates = new JObject();
            templates["genericMessage"] = new JObject
             {
               {"body", templateBodyAPNS}
             };

            Push push = TodoItemManager.DefaultManager.CurrentClient.GetPush();
            push.RegisterAsync(deviceToken, templates);
        }
        public override void DidReceiveRemoteNotification(UIApplication application,
    NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler)
        {
            NSDictionary aps = userInfo.ObjectForKey(new NSString("aps")) as NSDictionary;

            string alert = string.Empty;
            if (aps.ContainsKey(new NSString("alert")))
                alert = (aps[new NSString("alert")] as NSString).ToString();

            if (!string.IsNullOrEmpty(alert))
            {
                UIAlertView avAlert = new UIAlertView("Notification", alert, null, "OK", null);
                avAlert.Show();
            }
        }
    }
}


The team created an Azure Notification Hubs project on Azure portal.

IBTech VMobile Xamarin Mobile Application - Azure Notification Hubs


The team used their Google Cloud Messaging API key in Azure Notification Hubs.

IBTech VMobile Xamarin Mobile Application - Azure Notification Hubs - Google Cloud Messaging


The team tracked metric activities on the Metric dashboard.

IBTech VMobile Xamarin Mobile Application - Azure Notification Hubs - Metric dashboard


This screenshot shows the test from Azure Notification Hubs.

IBTech VMobile Xamarin Mobile Application - Azure Notification Hubs - Test


User interface and user experience

For better collaboration with other teams, all developers and teams need to know special dates. The IBTech team needs a calendar view element and for that they’ve used the Calendar component in Xamarin for the application. In this calendar view, the team shared release/deployment dates for their internal projects.

IBTech VMobile Xamarin Mobile Application - Calendar View


The user list view and search capabilities were created for both platforms by using ListView in Xamarin.Forms, which also automatically specializes the tabbed menu UI for both platforms.

IBTech VMobile Xamarin Mobile Application - User ListView


Whenever a password is forgotten or expires, the user needs to go to another PC to reset their password by using their credentials. This was a common case and requires time on both sides. In this project, the team created a feature connected to an internal Active Directory domain to reset a password via API calls by using the mobile application with Azure Multi-Factor Authentication.

IBTech VMobile Xamarin Mobile Application - Windows PC Password Screen


Another common scenario that is needed is a vacation tool. All users can request vacation via VMobile and managers can approve all vacation processes on VMobile.

IBTech VMobile Xamarin Mobile Application - Vacation Request and Management UI


Security

App test processes in the cloud and sometimes access to internal test resources are restricted by a firewall. The Xamarin Test Cloud Firewall is an important element to enable testing applications with internal banking data. The Xamarin Test Cloud has static IP addresses that give internal access to these endpoints:

- 195.249.159.238
- 195.249.159.239

Mobile DevOps

To build an automated process for their development environment and minimize the operation processes, IBTech decided to build a Mobile DevOps environment to run a seamless development process.

IBTech VMobile Xamarin Mobile Application - Mobile DevOps Architecture


IBTech used Visual Studio Team Services for a code repository and for continuous integration and continuous deployment. To test their application on hundreds of devices, they used Xamarin Test Cloud. After creating packages, they used HockeyApp to distribute the app, track crash reports, and collect feedback. To track user behaviors and telemetry data, they used the power of Application Insights, especially the Application Insights analytics tool, to run custom reports to get insights for the application. To visualize these reports, they used Power BI Desktop, and after creating custom dashboards, they used Power BI Embedded to embed these reports into their portal.

All the Mobile DevOps steps integrated into IBTech’s mobile application have been documented as hands-on labs by Microsoft Technical Evangelist Ibrahim Kivanc on GitHub. If you have any mobile application, you can benefit from these tools by just following this documentation: Mobile DevOps Hands-on Labs documentation on GitHub. The English version is also available at Mobile DevOps (EN) Hands-on Labs documentation on GitHub.

For the Mobile DevOps implementation, IBTech has integrated the following:

Visual Studio Team Services code repository

The team used Visual Studio Team Services to store the VMobile application code repository. The team connects and commits their application source code to Team Services on Mac devices via Visual Studio on Mac.

IBTech VMobile Xamarin Mobile Application - Visual Studio Team Services


Team Services continuous integration and continuous deployment

From an automated build perspective, the team integrated continuous integration and continuous deployment via a build definition on Visual Studio Team Services. To build for two platforms, two separate build definitions are required.

After these steps, the team successfully created their app packages .apk for Android and .ipa for iOS packages to test and distribute. The team first generated app packages to test on Xamarin Test Cloud and then distributed them via HockeyApp.

An important point to consider when creating .ipa packages for iOS devices is that the Visual Studio Team Services build definition steps require a Mac device to use as a build host machine. Hence, IBTech used one of their local physical Mac devices as a build host machine to create .ipa packages for iOS devices.

The following screenshot shows the build definition steps for iOS.

IBTech VMobile Xamarin Mobile Application - Team Services Build Definition iOS


The following screenshot shows the build definition steps for Android.

IBTech VMobile Xamarin Mobile Application - Team Services Build Definition Android


Automated testing with Xamarin Test Cloud

To test mobile applications automatically, the team prepared test steps via Xamarin Test Recorder to run these tests on Xamarin Test Cloud. One of the biggest problems for banks is testing the same application on a variety of form factors and different device brands with a variety of operating system versions. Xamarin Test Cloud helps organizations test their mobile applications automatically on hundreds of devices on different platforms.

IBTech VMobile Xamarin Mobile Application - Xamarin Test Cloud


To prepare test cases, the team used Xamarin Test Cloud and Xamarin.UITest project. Xamarin Test Recorder helped to provide C# test cases while using applications with gestures on real devices. Following is the sample code snippet to have the previous test screenshot from the test case.

using System;
using NUnit.Framework;
using Xamarin.UITest;
using Xamarin.UITest.iOS;

[TestFixture]
public class RecorderTest
{
    iOSApp app;
    [SetUp]
    public void SetUp()
    {
        app = ConfigureApp.iOS.InstalledApp("com.ibtech.vmobile").StartApp();
    }

    [Test]
    public void NewTest()
    {
        app.Tap(x => x.Marked("Sicil"));
        app.Screenshot("Tapped on view with class: UITextFieldLabel with text: Sicil with marked: Sicil");
        app.EnterText(x => x.Class("UITextField").Text("T"), "12345");
        app.Tap(x => x.Marked("Done"));
        app.Screenshot("Tapped on view with class: UIToolbarTextButton with marked: Done");
        app.Tap(x => x.Marked("Telefon Numarası"));
        app.Screenshot("Tapped on view with class: UITextFieldLabel with text: Telefon Numarası with marked: Telefon Numarası");
        app.EnterText(x => x.Class("UITextField").Index(3), "053012345678");
        app.Tap(x => x.Marked("Done"));
        app.Screenshot("Tapped on view with class: UIToolbarTextButton with marked: Done");
        app.Tap(x => x.Marked("Doğrula").Index(2));
        app.Screenshot("Tapped on view with class: UIButton with marked: Doğrula");
        app.Tap(x => x.Marked("SMS Kodu"));
        app.Screenshot("Tapped on view with class: UITextFieldLabel with text: SMS Kodu with marked: SMS Kodu");
        app.EnterText(x => x.Class("UITextField").Index(1), "112233");
        app.Tap(x => x.Marked("Done"));
        app.Screenshot("Tapped on view with class: UIToolbarTextButton with marked: Done");
        app.Tap(x => x.Marked("Doğrula").Index(2));
        app.Screenshot("Tapped on view with class: UIButton with marked: Doğrula");
        app.Tap(x => x.Marked("PC Açılış Şifresi"));
        app.Screenshot("Tapped on view with class: UITextFieldLabel with text: PC Açılış Şifresi with marked: PC Açılış Şifresi");
        app.EnterText(x => x.Class("UITextField"), "password");
        app.Tap(x => x.Marked("Doğrula").Index(1));
        app.Screenshot("Tapped on view with class: UIButton with marked: Doğrula");
        app.Tap(x => x.Class("Xamarin_Forms_Platform_iOS_Platform_DefaultRenderer").Index(15));
        app.Screenshot("Tapped on view with class: Xamarin_Forms_Platform_iOS_Platform_DefaultRenderer");
        app.Tap(x => x.Text("Geri"));
        app.Screenshot("Tapped on view with class: UILabel with text: Geri with marked: Geri");
        app.Tap(x => x.Class("Xamarin_Forms_Platform_iOS_Platform_DefaultRenderer").Index(19));
        app.Screenshot("Tapped on view with class: Xamarin_Forms_Platform_iOS_Platform_DefaultRenderer");
        app.ScrollDown();
        app.Screenshot("Swiped up");
        app.ScrollUp();
        app.Screenshot("Swipped down");
        app.ScrollUp();
        app.Screenshot("Swipped down");
        app.Tap(x => x.Text("Tüm Servisler"));
        app.Screenshot("Tapped on view with class: UISegmentLabel with text: Tüm Servisler with marked: Tüm Servisler");
        app.ScrollDown();
        app.Screenshot("Swiped up");
        app.ScrollDown();
        app.ScrollUp();
        app.Screenshot("Swipped down");
        app.Tap(x => x.Text("Geri"));
        app.Screenshot("Tapped on view with class: UILabel with text: Geri with marked: Geri");
        app.Tap(x => x.Marked("İndirimler"));
        app.Screenshot("Tapped on view with class: UILabel with text: İndirimler with marked: İndirimler");
        app.Tap(x => x.Class("Xamarin_Forms_Platform_iOS_Platform_DefaultRenderer").Index(6));
        app.Screenshot("Tapped on view with class: Xamarin_Forms_Platform_iOS_Platform_DefaultRenderer");
        app.Tap(x => x.Text("İndirimler"));
        app.Screenshot("Tapped on view with class: UILabel with text: İndirimler with marked: İndirimler");
        app.Tap(x => x.Text("Geri"));
        app.Screenshot("Tapped on view with class: UILabel with text: Geri with marked: Geri");
        app.Tap(x => x.Marked("Release Takvimi"));
        app.Screenshot("Tapped on view with class: UILabel with text: Release Takvimi with marked: Release Takvimi");
        app.Tap(x => x.Class("Telerik_XamarinForms_InputRenderer_iOS_IOSCalendarDayCell").Index(9));
        app.Screenshot("Tapped on view with class: Telerik_XamarinForms_InputRenderer_iOS_IOSCalendarDayCell");
        app.Tap(x => x.Marked("Geri"));
        app.Screenshot("Tapped on view with class: UINavigationItemButtonView with marked: Geri");
        app.Tap(x => x.Id("vm_shift_shuttle"));
        app.Screenshot("Tapped on view with class: UIImageView with id: vm_shift_shuttle");
        app.Tap(x => x.Text("Geri"));
        app.Screenshot("Tapped on view with class: UILabel with text: Geri with marked: Geri");
        app.Tap(x => x.Class("Xamarin_Forms_Platform_iOS_Platform_DefaultRenderer").Index(13));
        app.Screenshot("Tapped on view with class: Xamarin_Forms_Platform_iOS_Platform_DefaultRenderer");
        app.ScrollUp();
        app.Screenshot("Swipped down");
        app.Tap(x => x.Marked("Bu Ay"));
        app.Screenshot("Tapped on view with class: UISegment with marked: Bu Ay");
        app.ScrollUp();
        app.Screenshot("Swipped down");
        app.ScrollDown();
        app.Screenshot("Swiped up");
        app.Tap(x => x.Marked("Geri"));
        app.Screenshot("Tapped on view with class: UINavigationItemButtonView with marked: Geri");
        app.Tap(x => x.Class("Xamarin_Forms_Platform_iOS_Platform_DefaultRenderer").Index(18));
        app.Screenshot("Tapped on view with class: Xamarin_Forms_Platform_iOS_Platform_DefaultRenderer");
        app.ScrollDown();
        app.Screenshot("Swiped up");
        app.ScrollUp();
        app.Screenshot("Swipped down");
        app.Tap(x => x.Text("Geri"));
        app.Screenshot("Tapped on view with class: UILabel with text: Geri with marked: Geri");
        app.Tap(x => x.Class("Xamarin_Forms_Platform_iOS_Platform_DefaultRenderer").Index(22));
        app.Screenshot("Tapped on view with class: Xamarin_Forms_Platform_iOS_Platform_DefaultRenderer");
        app.Tap(x => x.Id("vm_home"));
        app.Screenshot("Tapped on view with class: UITabBarSwappableImageView with id: vm_home");
        app.Tap(x => x.Id("vm_announcement"));
        app.Screenshot("Tapped on view with class: UITabBarSwappableImageView with id: vm_announcement");
        app.ScrollDown();
        app.Screenshot("Swiped up");
        app.ScrollUp();
        app.Screenshot("Swipped down");
        app.Tap(x => x.Id("vm_groups"));
        app.Screenshot("Tapped on view with class: UITabBarSwappableImageView with id: vm_groups");
        app.ScrollDown();
        app.Screenshot("Swiped up");
        app.Tap(x => x.Class("Xamarin_Forms_Platform_iOS_LabelRenderer").Index(36));
        app.Screenshot("Tapped on view with class: Xamarin_Forms_Platform_iOS_LabelRenderer");
        app.Tap(x => x.Id("circle_mail").Index(1));
        app.Screenshot("Tapped on view with class: UIImageView with id: circle_mail");
        app.ScrollDown();
        app.Screenshot("Swiped up");
        app.ScrollUp();
        app.Screenshot("Swipped down");
        app.Tap(x => x.Marked("Geri"));
        app.Screenshot("Tapped on view with class: UINavigationItemButtonView with marked: Geri");
        app.ScrollDown();
        app.Screenshot("Swiped up");
        app.Tap(x => x.Marked("profile icon"));
        app.Screenshot("Tapped on view with class: UINavigationButton with marked: profile icon");
        app.Tap(x => x.Class("UIView").Index(6));
        app.Screenshot("Tapped on view with class: UIView");
        app.Tap(x => x.Text("VMobile"));
        app.Screenshot("Tapped on view with class: UITabBarButtonLabel with text: VMobile with marked: VMobile");
    }
}


HockeyApp implementation

Whenever there’s a new release, a common mobile app test scenario is to have a demo with the CxO or director. A developer deploys the application on a real device, and sometimes it crashes and we don’t know why. HockeyApp helps to release test packages easily and collect crash reports.

After testing application use cases on Xamarin Test Cloud with a test user account, the next step is to test on a device with real credentials. The development test team used HockeyApp to distribute the test app packages to see if any specific error occurs because of permissions on a local manufacturing device.

IBTech integrated the HockeyApp SDK in their Xamarin application via NuGet. To register their HockeyApp SDK with their HockeyApp application, they used the App ID blurred out in the following screenshot.

IBTech VMobile Xamarin Mobile Application - Hockey App Android


Following is the code snippet to register CrashManager in platform-specific code. Following is the code for Android; it’s the same for the iOS-specific code Main.cs.

MainActivity.cs

using System;
using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using HockeyApp.Android;
using HockeyApp.Android.Metrics;

namespace vMobile.Droid
{
    [Activity(Label = "vMobile", Icon = "@drawable/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    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);

            CrashManager.Register(this, "YOUR HOCKEY APP KEY WILL BE HERE");
            MetricsManager.Register(Application, "YOUR HOCKEY APP KEY WILL BE HERE");

            global::Xamarin.Forms.Forms.Init(this, bundle);
            LoadApplication(new App());
        }
    }
}


Other than tracking crashes and user behavior logs, MetricsManager.TrackEvent is used for custom events.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace vMobile
{
    public partial class CalendarPage : ContentPage
    {
        public CalendarPage()
        {
            InitializeComponent();
            HockeyApp.MetricsManager.TrackEvent("Calendar Page Visited");
        }
    }
}


After collecting these custom events, the team bridged Application Insights with HockeyApp to get custom queries to see what’s happening in the application.

IBTech VMobile Xamarin Mobile Application - HockeyApp


Apps need to be published with their application packages separately; all metrics and details are tracked by these apps on HockeyApp.

For iOS

IBTech VMobile Xamarin Mobile Application - HockeyApp iOS


For Android

IBTech VMobile Xamarin Mobile Application - Hockey App Android


Application Insights implementation

Application Insights is used for tracking user behavior and client application details. This tool is implemented by bridging Application Insights with HockeyApp on Azure.

After bridging Application Insights with HockeyApp, you can track HockeyApp stats on the Azure portal and use custom queries to get relevant data. You can find more information about how to use Application Insights with HockeyApp at Mobile and Desktop app telemetry experience in Application Insights and HockeyApp.

To create a bridge between HockeyApp and Application Insights, we followed the steps in Application Insights Bridge.

IBTech VMobile Xamarin Mobile Application - Application Insights


After connecting the HockeyApp SDK with Application Insights, we are able to see user behavior on the Azure Dashboard. The number of sessions and the number of users are listed and shown in charts with default settings.

IBTech VMobile Xamarin Mobile Application - Application Insights Sessions and Users


Power BI Desktop and Power BI Embedded implementation

To use custom queries, we imported stats into the Power BI dashboard. On Power BI Desktop, we created the following chart. The Microsoft Technical Evangelist described all the steps in a sample, so you can follow these steps to have the same report on your Application Insights report.

IBTech VMobile Xamarin Mobile Application - Power BI Publish


After successfully creating dashboards on Power BI Desktop, we decided to embed these reports into the existing app health reports at QNB Finansbank. We embedded these reports into a WebView. To see actual telemetry data, see the following sample graph Power BI Embedded.

IBTech VMobile Xamarin Mobile Application - Power BI Embedded


After implementing Power BI, we created a Mobile DevOps process to check and track all the details such as shown in the following screenshot. All components feed each other with relevant data.

IBTech VMobile Xamarin Mobile Application Architecture


Conclusion

With this project, IBTech empowered their internal knowledge with the Azure cloud platform, Xamarin mobile development, and Mobile DevOps. They’ve already released this application internally.

During this project they implemented their first Mobile DevOps approach and are looking to expand this experience into different projects and teams at IBTech and QNB Finansbank.

“We have a lot of development experience with C#, WPF, and XAML. Using these non-mobile development knowledges in a native mobile app made us very comfortable in Xamarin platform and our learning curve was very low” — Gökhan Çamaş, Expert Designer/Technical Lead

General lessons

  • Xamarin is a great tool to enable core .NET developers to build mobile applications.
  • Having one solution for Android and iOS platform applications reduces development cost.
  • An enterprise level, fully functional internal mobile banking application can be developed with Xamarin.
  • In this solution, Xamarin.Forms provides 99.99% code sharing between Android and iOS.
  • Business units and project management teams are getting a better understanding of users by using HockeyApp, Application Insights, and Power BI to point out top features and make plans for next releases.
  • The team was able to save an incredible amount of time by using Mobile DevOps practices such as continuous delivery and automated testing with Xamarin Test Cloud. After this implementation, the team can spend less time on operations, saving time and cost.
  • Mobile DevOps, especially the Visual Studio Mobile Center, is a powerful tool for any mobile app.
  • Xamarin Studio on Mac and its emulators are a great way to easily develop and test applications.
  • Visual Studio Team Services and Mobile DevOps helped to build a sustainable environment.
  • The team has uploaded their existing usage logs into Application Analytics AI.
  • Power BI can be used by other teams.

Opportunities going forward

  • Power BI Embedded is an essential tool to move dashboards forward.
  • The continuous integration and continuous delivery features of Team Services and Team Foundation Server 2015 can be implemented into other projects at the bank.
  • The team discovered Azure Marketplace during the program, and they are considering publishing their solution there.
  • For their next release, the team will add new features such as IP Camera Video Streaming for cafeterias to check how crowded they might be at a given time, GPS Tracking for shuttles to track them online, and meeting room reservations via this application by using Azure.

Additional resources

Local resources