Not too long ago, we introduced the native versions of Xuni with a pair of webinars that demonstrated using Xuni to create a simple weather application. Though we've covered creating this weather app natively on both iOS and Android, we haven't covered building the app (or using a web service) with Xamarin.Forms. In this article we'll fill in that blank by building a simple weather application example.
The model for our weather data just needs to capture the data that we’re interested in. To this end we will only include properties for the date, weather description (which alerts us to the expected conditions), temp, humidity, and air pressure. We can also create our own init method for object creation with all of the properties in place. We'll also implement the INotifyPropertyChanged and IEditableObject interface in our model which will facilitate working with our model when it's later bound to our Xuni controls.
namespace XuniWeather
{
class WeatherModel : INotifyPropertyChanged, IEditableObject
{
double _hightemp;
double _lowtemp;
double _pressure;
int _humidity;
string _description;
DateTime _date;
...
We'll also need to implement a few methods for the INotifyPropertyChanged and IEditableObject interfaces. Additionally, each property setter should call our OnPropertyChanged method.
...
public double hightemp{
get {return _hightemp;}
set
{
if (value != _hightemp)
{
_hightemp = value;
OnPropertyChanged("temp");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
PropertyChanged(this, e);
}
WeatherModel _clone;
public void BeginEdit()
{
_clone = (WeatherModel)this.MemberwiseClone();
}
public void EndEdit()
{
_clone = null;
}
public void CancelEdit()
{
if (_clone != null)
{
foreach (var p in this.GetType().GetRuntimeProperties())
{
if (p.CanRead && p.CanWrite)
{
p.SetValue(this, p.GetValue(_clone, null), null);
}
}
}
}
...
The next step that we’ll cover is how to retrieve the data for your application. Once again we'll use the OpenWeatherMap API to keep in line with our previous examples. There’s an abundance of information available on OpenWeatherMap’s API page. We're going to want to add a couple of Nuget packages to our project for the purposes of fetching and deserializing the data. From the Nuget Package Manager, we want to add the Microsoft HTTP CLient Libraries as well as Json.net. We'll use the HTTP Client libraries for retrieving the Json from OpenWeatherMap. Json.net will be used for deserializing the data.
We can create a separate WeatherData class which will be used to handle asynchronously pulling in the Json and deserializing it. Most of the work is done in the GetWeather task method where we can use the HttpClient to retrieve Json from the web service as a long string. Once we have the string, it can be deserialized pretty easily using Json.net with the call to JsonConvert.DeserializeObject().
private static async Task<ObservableCollection<WeatherModel>> GetWeather(string location)
{
var url = string.Format("http://api.openweathermap.org/data/2.5/forecast?zip={0}&units=imperial&APPID=a0a0e5e6d1ded0c79e853990c86f957b", location);
var client = new HttpClient();
var response = await client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
ObservableCollection<WeatherModel> weatherList = new ObservableCollection<WeatherModel>();
string content = await response.Content.ReadAsStringAsync();
var data = JsonConvert.DeserializeObject<WeatherResult>(content);
...
You'll notice specifically that JsonConvert.DeserializeObject(content) is using a WeatherResult object. This object mirrors the structure of the Json that is returned from the OpenWeatherMap API. Assuming our object matches the structure of our Json then deserializing it is a painless process.
public class WeatherResult
{
public City city { get; set; }
public int cod { get; set; }
public double message { get; set; }
public int cnt { get; set; }
public List<WeatherList> list { get; set; }
}
public class Coord
{
public double lon { get; set; }
public double lat { get; set; }
}
public class Sys
{
public int population { get; set; }
}
public class OtherSys
{
public string pod { 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 Weather
{
public int id { get; set; }
public string main { get; set; }
public string description { get; set; }
public string icon { get; set; }
}
public class Clouds
{
public int all { get; set; }
}
public class Wind
{
public double speed { get; set; }
public double deg { get; set; }
}
public class Rain
{
public double? amount { get; set; }
}
public class WeatherList
{
public int dt { get; set; }
public Main main { get; set; }
public List<Weather> weather { get; set; }
public Clouds clouds { get; set; }
public Rain rain { get; set; }
public Wind wind { get; set; }
public OtherSys sys { get; set; }
public string dt_txt { get; set; }
}
public class Main
{
public double temp { get; set; }
public double temp_min { get; set; }
public double temp_max { get; set; }
public double pressure { get; set; }
public double sea_level { get; set; }
public double grnd_level { get; set; }
public int humidity { get; set; }
public double temp_kf { get; set; }
}
We can package items from our weather result into the model which we've already created. To do this we'll just need to iterate through the returned WeatherResult's list where we can pull values from the properties that we want to put in our model.
...
WeatherModel model;
foreach (var item in data.list)
{
model = new WeatherModel();
model.date = Convert.ToDateTime(item.dt_txt);
model.humidity = item.main.humidity;
model.pressure = item.main.pressure;
model.description = item.weather[0].description;
model.hightemp = item.main.temp_min;
model.lowtemp = item.main.temp_max;
weatherList.Add(model);
}
return weatherList;
}
...
Most of the hard work is complete at this point. All that is left is to create the UI in Xaml, and bind the data to our controls in the code behind. The Xaml is relatively simple as it only constitutes a FlexChart with a few added series and a FlexGrid to output some more specific data.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XuniWeather.XuniWeatherPage"
xmlns:xunic="clr-namespace:Xuni.Forms.FlexChart;assembly=Xuni.Forms.FlexChart"
xmlns:xunig="clr-namespace:Xuni.Forms.FlexGrid;assembly=Xuni.Forms.FlexGrid">
<StackLayout>
<Grid VerticalOptions="FillAndExpand">
<xunic:FlexChart x:Name="chart" BindingX="date" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" Grid.Row="0">
<xunic:FlexChart.Series>
<xunic:ChartSeries x:Name="humidity" Name ="Humidity" Binding="humidity" ChartType="Area" ></xunic:ChartSeries>
<xunic:ChartSeries x:Name="hightemp" Name ="High Temp" Binding="hightemp" ChartType="Line"></xunic:ChartSeries>
<xunic:ChartSeries x:Name="lowtemp" Name ="Low Temp" Binding="lowtemp" ChartType="Line" ></xunic:ChartSeries>
</xunic:FlexChart.Series>
</xunic:FlexChart>
<xunig:FlexGrid x:Name="grid" Grid.Row="1"/>
</Grid>
</StackLayout>
</ContentPage>
The code behind is also fairly simple as all we really need to do is retrieve the data from our model asynchronously and assign it as the ItemsSource for both our FlexGrid and FlexChart.
namespace XuniWeather
{
public partial class XuniWeatherPage : ContentPage
{
WeatherData d;
ObservableCollection <WeatherModel> l;
public XuniWeatherPage()
{
InitializeComponent();
d = new WeatherData();
l = new ObservableCollection<WeatherModel> { };
var task = WeatherTask();
}
private async Task WeatherTask()
{
try
{
l = await d.GetResult();
grid.ItemsSource = l;
chart.ItemsSource = l;
}
catch(Exception e)
{
throw e;
}
}
}
}
If we run our weather application we should see something along the lines of the following:
That covers the basic structure of a weather application for Xamarin.Forms. Most of it is relatively straightforward, though structuring the object for the Json data and working with asynchronous tasks can at times be a little tricky. Working with the Xuni controls is actually the easiest part of the whole process, and they provide a simple outlet for visualizing the data.