Mobile & Web Services

Georgian Grigore 's blog

This article intention is to provide an elegant way of using Xamarin.Mobile with Xamarin.Forms, by maximizing the benefits both are providing.

Xamarin.Forms

Xamarin is great for code sharing, but using only Xamarin.iOS and Xamarin.Android, the most that can be shared is business logic and application code, while UI still needs to be written separately for each platform. However, with Xamarin.Forms, the UI code can be shared as well across Android, iOS and Windows Phone, making the cross development process more productive.

Xamarin.Mobile

In addition to Xamarin.Forms, comes Xamarin.Mobile to help with code sharing, offering a library that exposes a single set of APIs for accessing common mobile device functionality across iOS, Android, and Windows platforms. This API abstracts camera, geo-location and contacts, with future plans for notifications and accelerometer services. Xamarin is also collecting suggestions for what else the community needs to be abstracted out as part of this API, so you can post your suggestions here

Problem

Having things like camera abstracted is very nice  and useful for cross platform development, but becasue  it involves UI user interactions which are speciffic to each platform, the code won’t be entirely cross platform. If you’re planning on using Xamarin.Mobile with Xamarin.Android and/or Xamarin.iOS, that won’t be an issue, since the UI will be coded separately,  and the specific Xamarin.Mobile calls will be made from within  each platform. But if you’re planning to use it with Xamarin.Forms (like I did) then the things are a little trickier, since Xamarin.Forms is cross-platform.

Solution

Using Xamarin.Forms, a typical structure of the solution is something of this nature:

MyProject.Shared
MyProject.Droid
MyProject.iOS
MyProject.Windows

where MyProject.Shared does all the heavy lifting. For instance, the MyProject.Droid would contain a  class MainActivity responsible to load the “real” app which is implemented in the Shared project.

namespace MyProject.Droid
{
	[Activity (Label = "MyProject.Droid", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
	public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
	{
		protected override void OnCreate (Bundle bundle)
		{
			base.OnCreate (bundle);

			global::Xamarin.Forms.Forms.Init (this, bundle);

			LoadApplication (new App ());
		}
	}
}

Similarly,  the MyProject.iOS will contain an AppDelegate with the same responsibility – loading the App fro the Shared project.

namespace MyProject.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 ());

			return base.FinishedLaunching (app, options);
		}
	}
}

 

The App from MyProject.Shared will implement all the abstract UI elements. Out of the box, the vanilla App looks like this:

namespace MyProject
{
	public class App : Application
	{
		public App ()
		{
			// The root page of your application
			MainPage = new ContentPage {
				Content = new StackLayout {
					VerticalOptions = LayoutOptions.Center,
					Children = {
						new Label {
							XAlign = TextAlignment.Center,
							Text = "Welcome to Xamarin Forms!"
						}
					}
				}
			};
		}

		protected override void OnStart ()
		{
			// Handle when your app starts
		}

		protected override void OnSleep ()
		{
			// Handle when your app sleeps
		}

		protected override void OnResume ()
		{
			// Handle when your app resumes
		}
	}
}

 

Now say we want to add a button with the action “Take a picture”. First we’ll add the button, so we’ll have:

public class App : Application
	{
		private Button _button;

		void Instantiate ()
		{
			_button = new Button { Text = "Take a pic" };
			_button.Clicked += DoThis;
		}

		public App (TakePhoto tf)
		{
			Instantiate ();
			// The root page of your application
			MainPage = new ContentPage {
				Content = new StackLayout {
					VerticalOptions = LayoutOptions.Center,
					Children = {
						new Label {XAlign = TextAlignment.Center,Text = "Welcome to Xamarin Forms!"},
						_button
					}
				}
			};
		}

		private void DoThis(object sender, EventArgs args){
			Button button = (Button)sender;
			button.Text = "this was clicked";
		}

		protected override void OnStart ()
		{
			// Handle when your app starts

		}

		protected override void OnSleep ()
		{
			// Handle when your app sleeps
		}

		protected override void OnResume ()
		{
			// Handle when your app resumes
		}
	}

Now we have a button that would be displayed under the label, and will change the text upon click. So far so good, the button is coded once and it will run on all the platforms. However, we’d like this button to actually trigger a real take photo action. This won’t work directly with Xamarin.Mobile, since Mobile has a different dll for each platform. To solve this, we’ll have to:

  • Refer in each project the corresponding  Xamarin.Mobile.dll (there’s one for android, one for iOS , one for winrt and one for Win8)
  • Have an abstract implementation for taking picture in the Shared project. To achieve this, we’ll inject a delegate to a function invoking the camera action into the App
  • Implement specific camera actions using the referred Xamarin.Mobile in each of the specific module

Using the steps above, the code for the App becomes:

public class App : Application
	{
		private Button _button;
		private TakePhoto _tf;

		public delegate void TakePhoto();

		void Instantiate (TakePhoto tf)
		{
			_button = new Button { Text = "Take a pic" };
			_button.Clicked += DoThis;
			_tf = tf;
		}

		public App (TakePhoto tf)
		{
			Instantiate (tf);
			// The root page of your application
			MainPage = new ContentPage {
				Content = new StackLayout {
					VerticalOptions = LayoutOptions.Center,
					Children = {
						new Label {XAlign = TextAlignment.Center,Text = "Welcome to Xamarin Forms!"},
						_button
					}
				}
			};
		}

		private void DoThis(object sender, EventArgs args){
			Button button = (Button)sender;
			button.Text = "this was clicked";

			_tf ();
		}

		protected override void OnStart ()
		{
			// Handle when your app starts

		}

		protected override void OnSleep ()
		{
			// Handle when your app sleeps
		}

		protected override void OnResume ()
		{
			// Handle when your app resumes
		}
	}
}

A this point the App takes a delegate as a parameter and the specific implementation would be separately. For example, the Droid project would have this implementation for the TakePhoto method:

public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
	{

		protected override void OnCreate (Bundle bundle)
		{
			base.OnCreate (bundle);

			global::Xamarin.Forms.Forms.Init (this, bundle);

			LoadApplication (new App (TakePhoto));
		}

		void TakePhoto(){

				// MediaPicker is the class used to invoke the
				// camera and gallery picker for selecting and
				// taking photos and videos
				var picker = new MediaPicker (this);

				// We can check to make sure the device has a camera
				// and supports dealing with video.
				if (!picker.IsCameraAvailable || !picker.VideosSupported) {
					ShowUnsupported();
					return;
				}

				// The GetTakeVideoUI method returns an Intent to start
				// the native camera app to record a video.
				Intent intent = picker.GetTakePhotoUI (new StoreVideoOptions {
					Name = "MyImage",
					Directory = "MyDirectory",
				});	

			StartActivityForResult (intent, 1);

		}

		void ShowUnsupported ()
		{
			//dispay an unsupported message

		}

	}

 

This is a simple way to integrate the benefits from Xamarin.Mobile with the ones from Xamarin.Forms

February 3rd, 2015

Posted In: Mobile Development

Tags: , , ,

4 Comments