Cross platform apps are increasingly popular – and for good reason. Their appeal is that they run identically on more than one platform – Windows, Android, iOS – allowing you to create an app using a single development process and push it out to user of any device immediately.
It’s plain to see why companies should be interested in cross-platform apps – in the era of digital disruption they need not only to get as close as possible to their customers but to do so before their competitors get there first. They can even be useful internally – for companies that want to empower employees to bring their own devices. And cross platform apps allow you to do just that – by providing functionality people can use regardless of their preferred device.
If you’ve identified cross platform apps as something that will be beneficial to your strategy, then it’s time to start planning how you will build it. A cross-platform app build is divided into several stages, consisting of building the framework (core component) and then creating Windows, android and IOS apps respectively.
Before we jump into the code, we must be clear about the development environment. In this course the platforms we want to target are:
Given this, the prerequisites for development are:
For more details and installation specifications please visit the Xamarin guide here.
One should have a basic context of MVVM before getting started as we are going to use MVVMCross here. For a basic understanding of it, please go through this article.
The first stage towards developing a cross platform app is building a core. A typical first learning step is to create a hello world sample. However, that’s far too simple for this tutorial, so we are going to create an API which helps us to know about the climate of a city for next 7 days.
Let’s get started.
Step 1: Open visual studio and create a new solution called WeatherApp.
Step 2: Add a portable class library called Weather.Common to it and select the targets to be .net framework, widows 10 universal, windows 8.1, Xamrian.Android and Xamrian.iOS
Step 3: Download the MvvmCross binaries. In case you don’t want to use the nugget package.
Step 4: Download the NewtonSoft binaries from here. In case you don’t want to use the nugget package.
Step 5: Add Reference to the following binaries to Weather.Common project.
Step 6: Add a new folder called Constants to the project followed by adding a class called constants.
namespace Weather.Common.Constants { static class Constants { public const string WeatherApiUrl = "https://api.openweathermap.org/data/2.5/forecast/daily?q={0}&units=metric&cnt={1}"; public const string CityName = "noida"; public const int NoOfDays = 7; } }
This includes the API, URL, name of the city and number of days for which we want to fetch the data.
Step 7: Create a folder called Model and add a class called DailyTemparture to it.
This class holds properties like name of the city, temperature, date, and short date (of dd MMM yyyy format).
public class DailyTemperature : MvxNotifyPropertyChanged { private string _city; private DateTime _dateTime; private string _shortDate; private string _temprature; public string ShortDate { get { return _shortDate; } set { _shortDate = value; RaisePropertyChanged(() => ShortDate); } } public string City { get { return _city; } set { _city = value; RaisePropertyChanged(() => City); } } public DateTime DateTime { get { return _dateTime; } set { _dateTime = value; RaisePropertyChanged(() => DateTime); } } public string Temprature { get { return _temprature; } set { _temprature = value; RaisePropertyChanged(() => Temprature); } } }
As we can see, this class derives from class MvxNotifyPropertyChanged, having method called RaisePropertyChanged which needs to be called once a property value is changed.
Step 8: Now create a new class called ApiModel which will consist of model to de-serialize our weather api.
public class Coord { public double lon { get; set; } public double lat { get; set; } } public class City { public int id { get; set; } public string name { get; set; } public Coord coord { get; set; } public string country { get; set; } public int population { get; set; } } public class Temp { public double day { get; set; } public double min { get; set; } public double max { get; set; } public double night { get; set; } public double eve { get; set; } public double morn { get; set; } } public class Weather { public int id { get; set; } public string main { get; set; } public string description { get; set; } public string icon { get; set; } } public class List { public int dt { get; set; } public Temp temp { get; set; } public double pressure { get; set; } public int humidity { get; set; } public List<Weather> weather { get; set; } public double speed { get; set; } public int deg { get; set; } public int clouds { get; set; } public double rain { get; set; } } public class WeatherJsonData { public City city { get; set; } public string cod { get; set; } public double message { get; set; } public int cnt { get; set; } public List<List> list { get; set; } } public class WeatherData { public DateTime RecorededDataime { get; set; } public List<DailyTemperature> TempraureCollection { get; set; } }
Step 9: Now it’s time to create services for the fetching the weather information. So let’s create a folder called services and add an interface called IWeatherServices and a class called WeatherServices to it.
This service will us getting the daily temperature for any specific day, the method GetDailyWeatherDataAsync calls the api and get the temperature for next 7 days.
public interface IWeatherService { bool IsNext { get; } bool IsPrevious { get; } Task<DailyTemperature> GetFirst(); Task<DailyTemperature> GetLast(); Task<DailyTemperature> GetNext(); Task<DailyTemperature> GetPrevious(); Task GetDailyWeatherDataAsync(); } public class WeatherService : IWeatherService { private WeatherData weatherData; private DateTime currentSelectedDate; public bool IsNext { get { if ((currentSelectedDate.Date - DateTime.Now.Date).TotalDays >= Constants.Constants.NoOfDays - 1) { return false; } else { return true; } } } public bool IsPrevious { get { if(DateTime.Now.Date < currentSelectedDate.Date) { return true; } else { return false; } } } public async Task<DailyTemperature> GetFirst() { var selectedDate = DateTime.Now; return await GetTemprature(selectedDate); } public async Task<DailyTemperature> GetLast() { var selectedDate = DateTime.Now.AddDays(7); return await GetTemprature(selectedDate); } public async Task<DailyTemperature> GetNext() { var selectedDate = currentSelectedDate.AddDays(1); return await GetTemprature(selectedDate); } public async Task<DailyTemperature> GetPrevious() { var selectedDate = currentSelectedDate.AddDays(-1); return await GetTemprature(selectedDate); } private async Task<DailyTemperature> GetTemprature(DateTime selectedDate) { if (weatherData == null || (DateTime.Now - weatherData.RecorededDataime).TotalHours > 3) { await GetDailyWeatherDataAsync(); } DailyTemperature temprature = null; currentSelectedDate = selectedDate; temprature = weatherData.TempraureCollection.FirstOrDefault(x => x.Date.Date.Equals(selectedDate.Date)); return temprature; } public async Task GetDailyWeatherDataAsync() { var url = string.Format(Constants.Constants.WeatherApiUrl, Constants.Constants.CityName, Constants.Constants.NoOfDays); HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri(url)); request.ContentType = "application/json"; request.Method = "GET"; using (WebResponse response = await request.GetResponseAsync()) { using (Stream stream = response.GetResponseStream()) { StreamReader reader = new StreamReader(stream, Encoding.UTF8); string content = reader.ReadToEnd(); WeatherJsonData weatherJsonData = JsonConvert.DeserializeObject<WeatherJsonData>(content); weatherData = new WeatherData(); weatherData.RecorededDataime = DateTime.Now; currentSelectedDate = DateTime.Now; weatherData.TempraureCollection = new List<DailyTemperature>(); foreach (var item in weatherJsonData.list) { var dateTemprature = new DailyTemperature(); dateTemprature.City = weatherJsonData.city.name; dateTemprature.DateTime = new System.DateTime(1970, 1, 1, 0, 0, 0, 0).AddSeconds(item.dt); dateTemprature.ShortDate = dateTemprature.DateTime.ToString("dd MMM yyyy"); dateTemprature.Temprature = item.temp.day + " °C"; weatherData.TempraureCollection.Add(dateTemprature); } } } } }
Step 10: Create a folder called ViewModels and a class called FirstViewModel to it.
Note all view models should derive from class called MvxViewModel. The role of this view model is to call the weather service, get the temperature and verify if we can fetch the temperature for next of the previous date.
public class FirstViewModel : MvxViewModel { private DailyTemperature _dailyTemperature; private MvxCommand _nextCommand; private MvxCommand _previousCommand; private IWeatherService _weatherService; private bool _isPrevious; private bool _isNext; public DailyTemperature DailyTemperature { get { return _dailyTemperature; } set { _dailyTemperature = value; RaisePropertyChanged(() => DailyTemperature); } } public IMvxCommand NextCommand { get { return _nextCommand; } } public IMvxCommand PreviousCommand { get { return _previousCommand; } } public bool IsPrevious { get { return _isPrevious; } set { _isPrevious = value; RaisePropertyChanged(() => IsPrevious); } } public bool IsNext { get { return _isNext; } set { _isNext = value; RaisePropertyChanged(() => IsNext); } } public FirstViewModel(IWeatherService weatherService) { this._weatherService = weatherService; } public override void Start() { base.Start(); _nextCommand = new MvxCommand(() => NextCommandExecuted()); _previousCommand = new MvxCommand(() => PreviousCommandExecuted()); PageLoad(); } private async void PageLoad() { await this._weatherService.GetDailyWeatherDataAsync(); DailyTemperature = await this._weatherService.GetFirst(); ValidateNavigation(); } private void ValidateNavigation() { IsNext = this._weatherService.IsNext; IsPrevious = this._weatherService.IsPrevious; } private async void NextCommandExecuted() { DailyTemperature = await this._weatherService.GetNext(); ValidateNavigation(); } private async void PreviousCommandExecuted() { DailyTemperature = await this._weatherService.GetPrevious(); ValidateNavigation(); } }
Step 11: Add a new class file called App.cs to the project. Which will help us resolving the dependency between IWeatherService and WeatherService and register FirstViewModel as app starting view model.
public class App : Cirrious.MvvmCross.ViewModels.MvxApplication { IMvxIoCProvider container; public override void Initialize() { container = MvxSimpleIoCContainer.Initialize(); CreatableTypes() .EndingWith("Service") .AsInterfaces() .RegisterAsLazySingleton(); container.RegisterSingleton<IWeatherService>(new WeatherService()); RegisterAppStart<ViewModels.FirstViewModel>(); } }
We are done with setting up the Weather.Common, please have a look and make sure your solution structure looks like this. And don't forget to visit this blog next week for the second stage of this tutorial.