Xamarin Forms в действии. Medchest Assistant

f79d85cb71244484adfa441a163212f1.png
Автор: Константин Марс

Когда мы пришли на Medtech Hackathon, нашей целью было создать простой и легковесный инструмент для пользователей, которые хотят вести учет аптечки и вовремя получать оповещения об истечении срока годности препаратов, чтобы приобрести новые.

Мы долго колебались с выбором платформы между популярной и престижной iOS и модным, современным и приятным в разработке Android. Поэтому я предложил использовать Xamarin, который, между прочим, использует C# как основной язык разработки (и это главный язык, на котором в повседневной жизни пишет организатор нашей команды Арсений). Таким образом мы подошли к началу путешествия в мир кроссплатформенной разработки с Xamarin.
Отмечу, что Xamarin изначально базировался на фреймворке Mono, и поэтому несколько отличается от оригинального .NET-фреймворка Microsoft. Но эти отличия обсудим немного позднее.

В статье не станем обсуждать подробности хакатона и не будем углубляться в профессиональные секреты разработки с помощью Xamarin. Эта статья — базовая вводная в мир кроссплатформенной разработки с помощью Xamarin Forms. Цель материала — дать общее представление, как быстро разрабатывать с помощью Xamarin, подсказать, где в будущем искать ответы на более конкретные вопросы.

Архитектура проекта. PCL

Xamarin Forms строится вокруг общего кросс-платформенного кода, и может быть построен по одному из архитектурных подходов — PCL (Portable Class Library) или SAP (Shared Assets Project). Платформо-зависимые проекты — неотъемлемая часть солюшн Xamarin, называются соответственно и размещены в солюшне наравне с проектом общего кода.

Например, в нашем приложении мы имеем такой набор проектов:

682c3bb3bac94ede8b215a015c45734e.png

Как видите, присутствуют проекты для Android (Droid) и iOS, отдельный проект для AndroidWear (он корректно инсталируется вместе с Android-приложением на смарт-часы, но пока не выполняет заметную полезную работу, кроме демонстрации возможностей фреймворка  Xamarin для создания AndroidWear проектов). Еще есть проект библиотеки VuforiaBindings library (Java wrapper, который был задуман как интегратор Java-библиотеки Vuforia для распознавания образов и текста). Это примеры ключевых видов проектов, наиболее часто встречающихся при разработке c помощью Xamarin.

Xamarin Forms. XAML

Главное преимущество разработки приложений с помощью Xamarin для меня — возможность создавать UI для нескольких платформ одновременно. Xamarin позволяет создавать отдельные XML-формы для платформо-зависимых проектов и делать UI разным для разных платформ, но все же основной подход — стартовать с Xamarin Forms, создавать XML пользовательского интерфейса, который будет одинаково работать на всех поддерживаемых платформах.

Это стало возможным благодаря тому, что каждый платформо-зависимый проект стартует код из общего проекта, осуществляя иньекцию небольшого участка кода, специфичного для Xamarin Forms.
Например, в Android-проекте это происходит в MainActivity так:

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

В iOS проекте:

global::Xamarin.Forms.Forms.Init (); 

В то же время, если вы заглянете в MainActivity проекта для AndroidWear — вы не найдете там ничего связанного с Xamarin Forms. Это потому что проект для этой платформы пока не поддерживается Xamarin Forms и является «чистым» платформо-зависимым проектом, написанным на C#.

Разметка UI в Xamarin Forms наывается XAML. Здесь все очень похоже на «классический» .NET (WPF):

70217ae1549046d7ad680c03a8be6678.png

Внутри XAML выглядит тоже вполне привычно для тех, кто имеет опыт работы с .NET:


            

Ключевой функционал Bindings работает по тем же принципам, что и биндинги в WPF:


                
                    
                        
                            
                                
                            
                        
                    
                
            


Мы просто размещаем »{Binding }» в XAML вместо хардкода значений.

Основной проблемой для разработчиков Xamarin, которые работают в Xamarin Studio (например на Mac OS) является отсутсвие адекватного визуального редактора UI. Странно, что при этом студия обеспечивает разработчикам визуальные редакторы для конкретных платформ, таких как Android и iOS, но, увы, не для Xamarin Forms.

f75c18be8c6a46eeabdf3e9fb29bcfb8.png

2c156dad12eb4cd99b5f614cf48ad8ee.png

DependecyService. Платформо-специфичные возможности и Xamarin Forms

Когда приходит время реализовать что-то специфичное для платформы (например, нотификации), нам уже не обойтись без платформо-зависимого кода.
Это неизбежно — ведь те же нотификации реализуются совсем по-разному для Android и iOS, и эта разница выражается во многих нюансах поведения функционала. Но как тогда вызывать подобный платформо-зависимый код из UI, общего для всех платформо-зависимых проектов?

Здесь нам на помощь приходит DependencyService.
Как обычно в кросс-платформенной разработке, нужно объявить интерфейс в общем проекте и реализовать его в платформо-зависимых проектах. Единственным вопросом будет «как определить, какую реализацию вызвать в каждом конкретном случае?». И тут за работу берется DependencyService, магический деятель фреймворка Xamarin. В зависимости от того, для какой платформы мы собираем проект, DependencyService подставляет необходимую реализацию вместо интерфейса.
Также стоит отметить, что для того чтобы эта магия заработала, нужно использовать аннотацию Xamarin.Forms.Dependency:

[assembly: Xamarin.Forms.Dependency (typeof (NotificationHelperImpl))]

Например, интерфейс нотификаций, объявленный в общем проекте Xamarin Forms выглядит так:

namespace MedChestAssistant
{
    public interface INotificationHelper
    {
        void notify(String message);
    }
}


А платформо-зависимая реализация в Android-проекте выглядит так:

[assembly: Xamarin.Forms.Dependency (typeof (NotificationHelperImpl))]
namespace MedChestAssistant.iOS
{
    public class NotificationHelperImpl: INotificationHelper
    {
        #region INotificationHelper implementation

        public void notify (string message)
        {
            var notification = new UILocalNotification();

            // set the fire date (the date time in which it will fire)
            notification.FireDate = NSDate.FromTimeIntervalSinceNow(5);

            // configure the alert
            notification.AlertAction = "Medical Chest Reminder";
            notification.AlertBody = "Lyrica will expire in 2 days. Don't forget renew it";

            // modify the badge
            notification.ApplicationIconBadgeNumber = 1;

            // set the sound to be the default sound
            notification.SoundName = UILocalNotification.DefaultSoundName;

            // schedule it
            UIApplication.SharedApplication.ScheduleLocalNotification(notification);
        }

        #endregion

        public NotificationHelperImpl ()
        {
        }
    }
} 



В целом все достаточно просто :).

Зависимости. Пакеты. Галерея NuGet


Xamarin имеет довольно богатую библиотеку пакетов, совместимых с поддерживаемыми платфорамами — NuGet. Например, тот, кому нужно распознавание и сканирование баркодов может воспользоваться пакетом Zxing, совместимым с Xamarin.

165d209c6831481e8beb164b5765c0a5.png

После добавления пакет появися в списке подключенных пакетов проекта.
Вот, например, кросс-платформенная часть пакета Zxing в нашем кросс-платформенном проекте Xamarin Forms:

90487e64d2ce4402baacaccc422c6a33.png

Иногда (например для библиотеки Zxing library) нам нужно осуществить также некоторую специфичную для конкретных платформ инициализацию. Например, для Zxing нужно выполнить такие строки на старте платформ-специфичных приложений:

Android:

ZXing.Mobile.MobileBarcodeScanner.Initialize (Application);

iOS:

ZXing.Net.Mobile.Forms.iOS.Platform.Init();

Также платформ-специфичная инициализация бывает нужна и для стандартного функционала.
Например, нотификции в iOS требуют декларирования поддерживаемых форматов на старте приложения (это делается всегда в нативном iOS, и Xamarin просто покрывает уже существующую специфику):

if (UIDevice.CurrentDevice.CheckSystemVersion (8, 0)) {
                var notificationSettings = UIUserNotificationSettings.GetSettingsForTypes (
                    UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound, null
                );

                app.RegisterUserNotificationSettings (notificationSettings);
            }

Вот и весь краткий обзор возможностей разработки с Xamarin.

Исходный код нашего проекта с хакатона вы можете посмотреть здесь: https://github.com/DataArt/MedChestAssistant.git

Заметьте, это всего лишь код, написанный за короткий отрезок времени на хакатоне. Поэтому здесь покрыты лишь некоторые основные возможности Xamarin. Вы не найдете тут сервисов, выполняющихся в бэкграунде, сложных архитектурных паттернов, юнит-тестов или dependency-injection, которые конечно же понадобятся в коммерческих проектах. Наше приложение призвано показать, как быстро и легко решать конкретные задачи в сжатые сроки с помощью отличного кросс-платформенного инструмента, называемого Xamarin.

Если вы хотите узнать больше про Xamarin, обратитесь к официальной книге от Microsoft https://developer.xamarin.com/guides/xamarin-forms/creating-mobile-apps-xamarin-forms/
и посетите Xamarin Portal для изучения самых актуальных рецептов кросс-платформенной разработки https://developer.xamarin.com/guides/xamarin-forms/creating-mobile-apps-xamarin-forms/

Удачи вам в мире кросс-платформа! И новых свершений с Xamarin! :)

© Habrahabr.ru