Building your first end-to-end Cross Platform app

By | August 24, 2015

Building a cross platform app means way to develop an app which can run identically on more than one platform (like Windows, Android and IOS etc.).  This enable users of multiple platform to use your app.  If you have decided that building a cross platform app will be beneficial for you, then let’s start how we are going to achieve it. We will divide it into several parts which will consist of building the framework (core component) and then creating Windows, android and IOS app respectively.

Why should we build a cross platform app

Building a cross platform app have both pros and cons so one must be aware of them before taking such decisions

Pros Cons
Reusable code. Lack of support for newer features.
Fasten the speed of development. Performance issue.
Reduced development cost. Dependency of 3rd party tools.

Follow the Series here :

  1. Building your first end-to-end Cross Platform app
  2. Building your first end-to-end Cross Platform app – Part 2 – Windows Universal App
  3. Building your first end-to-end Cross Platform app – Part 3 – Android App
  4. Building your first end-to-end Cross Platform app – Part 4 – iOS App

Getting Started with Development Environment.

Before we jump into the code, we must fulfill we must be clear about the development environment. In this course the platform we are going to target are:

  1. 1. Windows
  2. 2. Android
  3. 3. IOS

The prerequisite for development are:

  1. Windows 10
  2. Visual studio 2015
  3. Xamarin for Windows
  4. Latest iOS SDK.
  5. Latest version of Xcode.
  6. Mac OS X 10.9.3+ (Mavericks) or 10.10 & above.
  7. Xamarin for mac

For more details and installation please visiting Xamarin guide here.

One should have a basic context of MVVM before getting started as we are going to use MVVMCross here, please go through this article to have basic understating about it.

Part 1

In this part, we are going to take the first leap towards developing a cross platform app which is building is core. A typical 1st learning step is to create a hello world sample but that’s going to be too simple so in this tutorial we are going to consume an api which helps us letting know about the temperate of a city for next 7 days.

Let’s get started.

Step 1: Open visual studio and create a new solution called WeatherApp.

image

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.

Adding New Project for Cross Platform App

Adding New Project for Cross Platform App

Adding a PCL

Adding a PCL

Choose Targeted Platform for PCL

Choose Targeted Platform for PCL

Step 3: Download the MvvmCross binaries from here. 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.

Common References

Common References

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 = "http://api.openweathermap.org/data/2.5/forecast/daily?q={0}&units=metric&cnt={1}";
    public const string CityName = "noida";
    public const int NoOfDays = 7;
 }
}

Which includes the api url, name of the city and no 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 modle’s 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.

image
And.. we are done for Part 1, we will see how we can take this forward in Part 2