Разработчикам на Xamarin доступен богатый выбор компонентов для работы с сетью, и в сегодняшней нашей статье мы рассмотрим набор модулей, которые также могут быть использованы в PCL-проектах на Xamarin.Forms.
Все статьи из колонки можно найти и прочитать по ссылке #xamarincolumn, или в конце материала под катом.

Самым популярным в настоящее время протоколом для общения мобильных приложений с сервером является REST в связке с Json. Поэтому наше сегодняшнее знакомство начнем с библиотеки Refit.
Refit позволяет описать спецификации для работы с REST-сервисом в виде простого Interface с понятным набором входных и выходных параметров, включая возможность манипулировать HTTP-заголовками для отдельных запросов. Для примера возьмем демо-API сервиса httpbin.org:
После описания данного интерфейса, он подается на вход для Refit:
Сами данные можно при необходимости (конвертации camel case или snake eyes, преобразование из множества в строковые значения) можно расширить аттрибутами из библиотеки Json.net, так как именно она используется в Refit:
В Refit в качестве выходного значения можно получить уже преобразованные объекты DTO или HttpResponseMessage. Последний позволяет получить информацию о запросе и ответе, что может быть полезно при отладке. При желании также может использоваться ModernHttpClient при создании HttpClient. В целом, Refit — достаточно удобный и универсальный инструмент для Xamarin-разработчиков в том числе.
Примечание 1: для установки Refit 3.0 в PCL-проект Xamarin.Forms потребуется перевести проект на .NET Standard.
Примечание 2: в документации Refit нет упоминания на использование CancelationToken для отмены активных операций, но данный механизм работает и описан в тикете.
При разработке мобильных приложений часто приходится учитывать фактор нестабильности сигнала в сотовых сетях, поэтому при выполнении сетевых запросов часто возникает необходимости делать повторные попытки. Это позволяет не отвлекать лишний раз пользователя просьбой повторить запрос.
Интересный подход по использованию Refit и Polly описал Rob Gibbens в своем блоге (дополнительно там показан пример приоретизацией сетевых запросов с помощью Fusillade).
Вот так мы совершаем запрос:
Вместо loadingFunction необходимо передать ваш код обращения к Refit:
Итак, мы интегрировали Refit, Polly и ModernHttpClient.
И в завершении статьи можно рассмотреть использование кэша при работе с сетью. Xamarin-разработчику доступны все возможности целевых платформ, поэтому для реализации кэша можно использовать различные СУБД. Одним из самых популярных кэшеров выступает Akavache, работающий поверх SQLite.
Также можно использовать для реализации кэша очень удобную мобильную СУБД Realm. Ниже представлен пример кэшера на базе Realm:
Итак, сегодня мы рассмотрели использование Refit, Json.net, ModernHttpClient, Polly и Realm при интеграции с REST API. В следующей статье мы рассмотрим вопросы интеграции Xamarin с внешними сервисами и Azure.

Вячеслав Черников — руководитель отдела разработки компании Binwell. В прошлом — один из Nokia Champion и Qt Certified Specialist, в настоящее время — специалист по платформам Xamarin и Azure. В сферу mobile пришел в 2005 году, с 2008 года занимается разработкой мобильных приложений: начинал с Symbian, Maemo, Meego, Windows Mobile, потом перешел на iOS, Android и Windows Phone.
Другие статьи автора:
Все статьи из колонки можно найти и прочитать по ссылке #xamarincolumn, или в конце материала под катом.

Refit для удобного описания клиента REST API
Самым популярным в настоящее время протоколом для общения мобильных приложений с сервером является REST в связке с Json. Поэтому наше сегодняшнее знакомство начнем с библиотеки Refit.
Refit позволяет описать спецификации для работы с REST-сервисом в виде простого Interface с понятным набором входных и выходных параметров, включая возможность манипулировать HTTP-заголовками для отдельных запросов. Для примера возьмем демо-API сервиса httpbin.org:
[Headers("Accept: application/json")]
public interface IHttpbinApi
{
[Get("/basic-auth/{username}/{password}")]
Task<AuthResult> BasicAuth(string username, string password, [Header("Authorization")] string authToken, CancellationToken ctx);
[Get("/cache")]
Task<HttpResponseMessage> CheckIfModified([Header("If-Modified-Since")] string lastUpdateAtString, CancellationToken ctx);
[Post("/post")]
Task<HttpResponseMessage> FormPost([Body(BodySerializationMethod.UrlEncoded)] FormData data, CancellationToken ctx);
}
После описания данного интерфейса, он подается на вход для Refit:
var client = new HttpClient(new NativeMessageHandler())
{
BaseAddress = new Uri("http://httpbin.org")
};
_httpbinApiService = RestService.For<IHttpbinApi>(client);
Сами данные можно при необходимости (конвертации camel case или snake eyes, преобразование из множества в строковые значения) можно расширить аттрибутами из библиотеки Json.net, так как именно она используется в Refit:
public class AuthResult
{
[JsonProperty("authenticated")]
public bool IsAuthenticated { get; set; }
[JsonProperty("user")]
public string Login { get; set; }
}
В Refit в качестве выходного значения можно получить уже преобразованные объекты DTO или HttpResponseMessage. Последний позволяет получить информацию о запросе и ответе, что может быть полезно при отладке. При желании также может использоваться ModernHttpClient при создании HttpClient. В целом, Refit — достаточно удобный и универсальный инструмент для Xamarin-разработчиков в том числе.
Примечание 1: для установки Refit 3.0 в PCL-проект Xamarin.Forms потребуется перевести проект на .NET Standard.
Примечание 2: в документации Refit нет упоминания на использование CancelationToken для отмены активных операций, но данный механизм работает и описан в тикете.
Polly
При разработке мобильных приложений часто приходится учитывать фактор нестабильности сигнала в сотовых сетях, поэтому при выполнении сетевых запросов часто возникает необходимости делать повторные попытки. Это позволяет не отвлекать лишний раз пользователя просьбой повторить запрос.
Интересный подход по использованию Refit и Polly описал Rob Gibbens в своем блоге (дополнительно там показан пример приоретизацией сетевых запросов с помощью Fusillade).
Вот так мы совершаем запрос:
protected async Task<RequestResult> MakeRequest<T>(Func<CancellationToken, Task<T>> loadingFunction, CancellationToken cancellationToken)
{
Exception exception = null;
var result = default(T);
try
{
result = await Policy.Handle<WebException>().Or<HttpRequestException>()
.WaitAndRetryAsync(3, i => TimeSpan.FromMilliseconds(300), (ex, span) => exception = ex)
.ExecuteAsync(loadingFunction, cancellationToken);
}
catch (Exception e)
{
// Сюда приходят ошибки вроде отсутствия интернет-соединения или неправильной работы DNS
exception = e;
}
//TODO: Обработать исключения или передать их дальше
return result;
}
Вместо loadingFunction необходимо передать ваш код обращения к Refit:
var authToken = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}"));
return await MakeRequest(ct => _httpbinApiService.BasicAuth(username, password, authToken, ct), cancellationToken);
Итак, мы интегрировали Refit, Polly и ModernHttpClient.
Кэш
И в завершении статьи можно рассмотреть использование кэша при работе с сетью. Xamarin-разработчику доступны все возможности целевых платформ, поэтому для реализации кэша можно использовать различные СУБД. Одним из самых популярных кэшеров выступает Akavache, работающий поверх SQLite.
var cache = BlobCache.LocalMachine;
var cachedObjects = cache.GetAndFetchLatest("objects", GetRemoteObjectAsync,
offset =>
{
TimeSpan elapsed = DateTimeOffset.Now - offset;
return elapsed > new TimeSpan(hours: 0, minutes: 30, seconds: 0);
});
Также можно использовать для реализации кэша очень удобную мобильную СУБД Realm. Ниже представлен пример кэшера на базе Realm:
public static class LocalCache
{
private class CachedObject : RealmObject
{
[PrimaryKey]
public string Key { get; set; }
public string Value { get; set; }
public DateTimeOffset UpdatedAt { get; set; }
}
private static readonly RealmConfiguration Configuration = new RealmConfiguration("cache.realm", true);
private static Realm Db => Realm.GetInstance(Configuration);
public static async Task WriteToCache<T>(string key, T data, DateTimeOffset timeStamp)
{
if (String.IsNullOrEmpty(key) || data == null || timeStamp == DateTimeOffset.MinValue) return;
var currentValue = Db.All<CachedObject>().Where(o => o.Key == key).ToList().FirstOrDefault();
if (currentValue == null)
await Db.WriteAsync(db =>
{
var newValue = db.CreateObject<CachedObject>();
newValue.Key = key;
newValue.UpdatedAt = timeStamp;
newValue.Value = JsonConvert.SerializeObject(data);
});
else
using (var transaction = Db.BeginWrite())
{
currentValue.Value = JsonConvert.SerializeObject(data);
currentValue.UpdatedAt = timeStamp;
transaction.Commit();
}
}
public static DateTimeOffset CacheLastUpdated(string key)
{
if (String.IsNullOrEmpty(key)) return DateTimeOffset.MinValue;
var currentValue = Db.All<CachedObject>().Where(o => o.Key == key).ToList().FirstOrDefault();
return currentValue?.UpdatedAt ?? DateTimeOffset.MinValue;
}
public static void RemoveCache(string key)
{
if (String.IsNullOrEmpty(key)) return;
var currentValue = Db.All<CachedObject>().Where(o => o.Key == key).ToList().FirstOrDefault();
if (currentValue == null) return;
using (var transaction = Db.BeginWrite())
{
Db.Remove(currentValue);
transaction.Commit();
}
}
public static T GetFromCache<T>(string key)
{
if (String.IsNullOrEmpty(key)) return default(T);
var currentValue = Db.All<CachedObject>().Where(o => o.Key == key).ToList().FirstOrDefault();
return currentValue?.Value == null ? default(T) : JsonConvert.DeserializeObject<T>(currentValue.Value);
}
public static void ClearCache()
{
Realm.DeleteRealm(Configuration);
}
}
Заключение
Итак, сегодня мы рассмотрели использование Refit, Json.net, ModernHttpClient, Polly и Realm при интеграции с REST API. В следующей статье мы рассмотрим вопросы интеграции Xamarin с внешними сервисами и Azure.
Об авторах
Вячеслав Черников — руководитель отдела разработки компании Binwell. В прошлом — один из Nokia Champion и Qt Certified Specialist, в настоящее время — специалист по платформам Xamarin и Azure. В сферу mobile пришел в 2005 году, с 2008 года занимается разработкой мобильных приложений: начинал с Symbian, Maemo, Meego, Windows Mobile, потом перешел на iOS, Android и Windows Phone.
Другие статьи автора:
- Авторизация OAuth для Xamarin-приложений
- Деплоим мобильный софт с помощью devops-конвейера Microsoft
- DevOps на службе человека
- Автоматизируем неавтоматизируемое, или про Xamarin в реальных проектах
- Быстрое создание MVP (minimum viable product) на базе Microsoft Azure и Xamarin.Forms
- Готовим Xamarin.Forms: настройка окружения и первые шаги
- Повышаем эффективность работы в Xamarin.Forms
- Работаем с состояниями экранов в Xamarin.Forms
- Подключаем Facebook SDK для Xamarin.Forms
- Подключаем ВКонтакте SDK для Xamarin.Forms
Полезные ссылки
- Анонс бесплатных инструментов Xamarin для разработки кроссплатформенных приложений на С# по ссылке
- Visual Studio 2015 Community, Visual Studio Team Services, Visual Studio Code: бесплатные предложения для разработчиков
- Дополнительные бесплатные инструменты и службы в программе Visual Studio Dev Essentials