Custom programming applications, web sites and training since 2002-we can also take care of your hosting, self-publishing and more

Xamarin Cross-Platform Solution Creating the Data Directory

Now we will be able to make the connection between the MySQL database designed previously and the individual models. This connection is enabled using the interface, service and Item Manager files shown at the left. Let’s begin by explaining the interface and service together.

public interface IRestService
{
    Task<List<WineryItem>> ReadWineries(string typ, string val, int wrs, int distanceFrom);
    Task<List<string>> GetLTDNames();
    Task<List<string>> GetCounties();
    Task<string> GetName(int id);
    Task<double> GetLat(int id);
    Task<double> GetLong(int id);
}

As with the other interface we have seen, this class file defines the individual procedures declared inside of RestService. After the task declaration we define what will be returned by the individual process (List of WineryItem, list of strings, string, double). Inside the parentheses of each procedure you will notice the type and name of parameters being fed into the task. As we explain each of the defined tasks below, code repetition will be referenced.

public class RestService : IRestService
{
   HttpClient client;
   HttpClient client2;
   HttpClient cliento;
   public List<WineryItem> Items { get; private set; }
   public List<OnlineItem> OItems { get; private set; }
   public List<String> Counties { get; private set; }
   public WineryRecords Records { get; private set; }
   public OnlineRecords Onlines { get; private set; }
   public CountyRecords Countys { get; private set; }

Does that class declaration approach look familiar? It should, the RestService class is declared public and it inherits from the previously mentioned interface. Next, we are declaring the items, lists and records to be used in the routine.

public RestService()
   {
       var authData = string.Format("{0}:{1}", Constants.Username, Constants.Password);
       var authHeaderValue = Convert.ToBase64String(Encoding.UTF8.GetBytes(authData));

       client = new HttpClient();
       client.MaxResponseContentBufferSize = 256000;
       client2 = new HttpClient();
       client2.MaxResponseContentBufferSize = 256000;
       cliento = new HttpClient();
       cliento.MaxResponseContentBufferSize = 25600;
   }

As we set up RestService, we are declaring the authorization requirements and three HttpClients used to load three separate sets of information from our RESTful service.

public async Task<List<WineryItem>> ReadWineries(string typ, string val, int wrs, int distanceFrom)
   {
     Location position = null;
     var uri = new Uri(string.Format(Constants.WineriesUrl, "wineries_by_name.php?name=" + val));
     if (val.Contains("LTD-"))
     {
         string wineName = val.Substring(4);
         int wineId = 0;
         uri = new Uri(string.Format(Constants.WineriesUrl, "wineries_by_name.php?name=" + wineName));
         try
         {
             SHA1Managed sha1 = new SHA1Managed();
             var encUname = sha1.ComputeHash(Encoding.UTF8.GetBytes("* uname *"));
             var sb = new StringBuilder(encUname.Length * 2);
             foreach (byte b in encUname)
             {
                 sb.Append(b.ToString("x2"));
             }
             string encUsername = sb.ToString();
             var encPwd = sha1.ComputeHash(Encoding.UTF8.GetBytes("* pwd *"));
             sb = new StringBuilder(encPwd.Length * 2);
             foreach (byte b in encPwd)
             {
                 sb.Append(b.ToString("x2"));
             }
             string encPassword = sb.ToString();
             var bytearray = Encoding.UTF8.GetBytes(encUsername + ":" + encPassword);
             client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(bytearray));
             var response = await client.GetAsync(uri);
             if (response.IsSuccessStatusCode)
             {
                 var content = await response.Content.ReadAsStringAsync();
                 Records = JsonConvert.DeserializeObject<WineryRecords>(content);
                 wineId = Records.records[0].Id;
                 position = new Location();
                 position.Latitude = Convert.ToDouble(Records.records[0].Latitude);
                 position.Longitude = Convert.ToDouble(Records.records[0].Longitude);
             }
         }
         catch (Exception ex)
         {
             Debug.WriteLine(@"         ERROR {0}", ex.Message);
         }
     }

Now we get to retrieve some data! Reading wineries, that is. Let’s start by looking at the variables fed into this procedure – typ, val, wrs and distanceFrom. Simply put, we can deduce from the code above that val is the name we are searching for. If that parameter starts with “LTD-,” we will remove those characters to determine the actual name.

The next block of code is contained within a try … catch block so that, if it should fail, the processing will continue after noting an error message. A majority of that code is setting up the authorization and other requirements for retrieving a winery record from the REST endpoint, wineries_by_name. After deserializing the results with JSONConvert, we are able to read the record id, latitude and longitude from records[0] (the only one returned).

else if (Constants.swMilDisplay == true || (Constants.swMilDisplay == false && typ != "Region"))
     {
         position = await Geolocation.GetLocationAsync();
     }

This is a quick little section of code, and it introduces some new concepts not yet encountered in this programming adventure. What are Constants and why haven’t we seen them defined in this class? Constants are global variables, usually defined by program settings. Yes, we have a Settings option and will cover that later.

We will now use the typ parameter which was fed in. We have already determined that val doesn’t start with “LTD-” and will now look at the swMilDisplay (switch for mileage display), possibly combined with typ, to determine if we are setting the position Location. This will use the Geolocation library to retrieve the current coordinates from the GPS.

     var urio = new Uri(string.Format(Constants.OnlinesUrl, "onlines_by_id.php?id=12"));
     Items = new List<WineryItem>();
     OItems = new List<OnlineItem>();
     Location winePosition;
     double dblDistance;
     if (typ == "Name")
         uri = new Uri(string.Format(Constants.WineriesUrl, "wineries_by_name.php?name=" + val));
     else if (typ == "County")
         uri = new Uri(string.Format(Constants.WineriesUrl, "wineries_by_county.php?county=" + val));
     else if (typ == "Region")
         uri = new Uri(string.Format(Constants.WineriesUrl, "wineries_by_region.php?region=" + val));
     else if (typ == "Mileage")
         uri = new Uri(string.Format(Constants.WineriesUrl, "wineries_for_mileage.php"));

In this section of code, we are creating a new URI string called urio which will be used to retrieve the online items associated with the retrieved winery (ies). Items will contain the list of wineries while OItems will contain the list of associated online entries. We will next create the winePosition Location and dblDistance variables.

The next set of if/then statements will use the typ parameter to determine which RESTful endpoint will be used to load the wineries to be shown in our mobile app.

try
{
    SHA1Managed sha1 = new SHA1Managed();
    var encUname = sha1.ComputeHash(Encoding.UTF8.GetBytes("*******"));
    var sb = new StringBuilder(encUname.Length * 2);
    foreach (byte b in encUname)
    {
        // can be "x2" if you want lowercase
        sb.Append(b.ToString("x2"));
    }
    string encUsername = sb.ToString();
    var encPwd = sha1.ComputeHash(Encoding.UTF8.GetBytes("*******"));
    sb = new StringBuilder(encPwd.Length * 2);
    foreach (byte b in encPwd)
    {
        // can be "x2" if you want lowercase
        sb.Append(b.ToString("x2"));
    }
    string encPassword = sb.ToString();
    var bytearray = Encoding.UTF8.GetBytes(encUsername + ":" + encPassword);
    client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(bytearray));
    var response = await client.GetAsync(uri);

Now we have executed the exact same code previously used to load a specific winery and retrieve id, latitude and longitude. Next we will be retrieving the single winery or list based on the previously set up URI based on typ.

if (response.IsSuccessStatusCode)
                {
                    var content = await response.Content.ReadAsStringAsync();
                    Records = JsonConvert.DeserializeObject<WineryRecords>(content);
                    Items = new List<WineryItem>();

                    foreach (var record in Records.records)
                    {
                        dblDistance = 0;
                        if (record.Latitude != null && (Constants.swMilDisplay == true || (Constants.swMilDisplay == false && typ != "Region")))
                        {
                            winePosition = new Location(Convert.ToDouble(record.Latitude), Convert.ToDouble(record.Longitude)); //How do I submit the request timeout here?
                            dblDistance = Location.CalculateDistance(position, winePosition, DistanceUnits.Miles);
                        }
                        if ((typ == "Mileage" && dblDistance <= distanceFrom) || typ != "Mileage")
                        {
                            if ((wrs == 0) ||
                            (wrs == 2 && record.Type == "Restaurant") ||
                            (wrs == 3 && record.Type == "Shop") ||
                            (wrs == 1 && record.Type == "Winery"))
                            {
                                urio = new Uri(string.Format(Constants.OnlinesUrl, "onlines_by_id.php?id=" + record.Id));
                                cliento.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(bytearray));
                                var responseo = await client.GetAsync(urio);
                                WineryItem item = new WineryItem();

                                item.Inlink = null;
                                item.Fblink = null;
                                item.Golink = null;
                                item.Trlink = null;
                                item.Twlink = null;
                                item.Welink = null;
                                item.Yelink = null;
                                item.Yolink = null;
                                if (responseo.IsSuccessStatusCode)
                                {
                                    content = await responseo.Content.ReadAsStringAsync();

                                    Onlines = JsonConvert.DeserializeObject<OnlineRecords>(content);
                                    OItems = new List<OnlineItem>();
                                    foreach (var online in Onlines.records)
                                    {
                                        if (online.Description == "facebook")
                                        {
                                            item.Fblink = online.Url;
                                            if (online.Url == "")
                                                item.FaIcon = false;
                                            else
                                                item.FaIcon = true;
                                        }
                                        if (online.Description == "twitter")
                                        {
                                            item.Twlink = online.Url;
                                            if (online.Url == "")
                                                item.TwIcon = false;
                                            else
                                                item.TwIcon = true;
                                        }
                                        if (online.Description == "instagram")
                                        {
                                            item.Inlink = online.Url;
                                            if (online.Url == "")
                                                item.InIcon = false;
                                            else
                                                item.InIcon = true;
                                        }
                                        if (online.Description == "website")
                                        {
                                            item.Welink = online.Url;
                                            if (online.Url == "")
                                                item.WeIcon = false;
                                            else
                                                item.WeIcon = true;
                                        }
                                        if (online.Description == "googlemaps")
                                        {
                                            item.Golink = online.Url;
                                            if (online.Url == "")
                                                item.IgIcon = false;
                                            else
                                                item.IgIcon = true;
                                        }
                                        if (online.Description == "yelp")
                                        {
                                            item.Yelink = online.Url;
                                            if (online.Url == "")
                                                item.YeIcon = false;
                                            else
                                                item.YeIcon = true;
                                        }
                                        if (online.Description == "tripadvisor")
                                        {
                                            item.Trlink = online.Url;
                                            if (online.Url == "")
                                                item.TrIcon = false;
                                            else
                                                item.TrIcon = true;
                                        }
                                        if (online.Description == "youtube")
                                        {
                                            item.Yolink = online.Url;
                                            if (online.Url == "")
                                                item.YoIcon = false;
                                            else
                                                item.YoIcon = true;
                                        }

                                    }
                                }
                                item.Id = record.Id;
                                item.Address = record.Address;
                                item.City = record.City;
                                item.County = record.County;
                                item.Name = record.Name;
                                item.Phone = record.Phone;
                                item.State = record.State;
                                item.Type = record.Type;
                                item.Website = record.Website;
                                item.Zip = record.Zip;
                                item.Tastings = record.Tastings;
                                item.Tours = record.Tours;
                                item.Region = record.Region;
                                if (dblDistance > 0 && dblDistance < 1000)
                                {
                                    item.intDistance = Convert.ToInt16(dblDistance);
                                    item.strDistance = dblDistance.ToString("N0") + "Mi";
                                }
                                else
                                {
                                    item.intDistance = 0;
                                    item.strDistance = "--";
                                }
                                Items.Add(item);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(@"         ERROR {0}", ex.Message);
            }
            if (typ == "Mileage")
            {
                Items.Sort((x, y) => x.intDistance.CompareTo(y.intDistance));
            }
            return Items;
        }

In this lengthy block of code we are cycling through each of the retrieved wineries. For each of those, we are determining the distance (if necessary based on settings) and loading the associated online entries with a process similar to that previously explained.

As we process each of those online entries, we will be using the description to show or hide each of the icons displayed on our mobile app screen. Once that process has completed, we will set the winery details (item) and continue with the next item and steps.

public async Task<List<NameItem>> ReadLTDWineries()
        {

            var uri = new Uri(string.Format(Constants.WineriesUrl, "wineries_by_region.php?region=piedmont"));

            var NItems = new List<NameItem>();
            uri = new Uri(string.Format(Constants.WineriesUrl, "wineries_for_mileage.php"));
            try
            {
                SHA1Managed sha1 = new SHA1Managed();
                var encUname = sha1.ComputeHash(Encoding.UTF8.GetBytes("* username *"));
                var sb = new StringBuilder(encUname.Length * 2);
                foreach (byte b in encUname)
                {
                    // can be "x2" if you want lowercase
                    sb.Append(b.ToString("x2"));
                }
                string encUsername = sb.ToString();
                var encPwd = sha1.ComputeHash(Encoding.UTF8.GetBytes("* password *"));
                sb = new StringBuilder(encPwd.Length * 2);
                //encUsername = encUsername + ":";
                foreach (byte b in encPwd)
                {
                    // can be "x2" if you want lowercase
                    sb.Append(b.ToString("x2"));
                }
                string encPassword = sb.ToString();
                var bytearray = Encoding.UTF8.GetBytes(encUsername + ":" + encPassword);
                client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(bytearray));
                var response = await client.GetAsync(uri);
                if (response.IsSuccessStatusCode)
                {
                    var content = await response.Content.ReadAsStringAsync();
                    var NRecords = JsonConvert.DeserializeObject<NameRecords>(content);
                    NItems = new List<NameItem>();

                    foreach (var record in NRecords.records)
                    {
                        NameItem item = new NameItem();

                        item.Id = record.Id;

                        item.Name = record.Name;
                        item.Latitude = record.Latitude;
                        item.Longitude = record.Longitude;
                        NItems.Add(item);
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(@"         ERROR {0}", ex.Message);
            }

            return NItems;
        }

ReadLTDWineries uses an approach quite similar to what you have already seen. Unlike previous read routines, this mainly focuses on latitude, longitude for distance calculation purposes.

public async Task<List<string>> GetLTDNames()
        {

            var uri = new Uri(string.Format(Constants.WineriesUrl, "wineries_by_region.php?region=piedmont"));

            var NItems = new List<string>();
            uri = new Uri(string.Format(Constants.WineriesUrl, "wineries_for_mileage.php"));
            try
            {
                SHA1Managed sha1 = new SHA1Managed();
                var encUname = sha1.ComputeHash(Encoding.UTF8.GetBytes("* username *"));
                var sb = new StringBuilder(encUname.Length * 2);
                foreach (byte b in encUname)
                {
                    // can be "x2" if you want lowercase
                    sb.Append(b.ToString("x2"));
                }
                string encUsername = sb.ToString();
                var encPwd = sha1.ComputeHash(Encoding.UTF8.GetBytes("* password *"));
                sb = new StringBuilder(encPwd.Length * 2);
                foreach (byte b in encPwd)
                {
                    // can be "x2" if you want lowercase
                    sb.Append(b.ToString("x2"));
                }
                string encPassword = sb.ToString();
                var bytearray = Encoding.UTF8.GetBytes(encUsername + ":" + encPassword);
                client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(bytearray));
                var response = await client.GetAsync(uri);
                if (response.IsSuccessStatusCode)
                {
                    var content = await response.Content.ReadAsStringAsync();
                    var NRecords = JsonConvert.DeserializeObject<NameRecords>(content);
                    NItems = new List<string>();

                    foreach (var record in NRecords.records)
                    {
                        NItems.Add(record.Name);
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(@"         ERROR {0}", ex.Message);
            }

            return NItems;
        }

GetLTDNames is another quite similar function focused only on the winery name.

public async Task<string> GetName(int id)
        {
            var uri = new Uri(string.Format(Constants.WineriesUrl, "winery_by_id.php?id=" + id));
            string wineryName = null;
            try
            {
                SHA1Managed sha1 = new SHA1Managed();
                var encUname = sha1.ComputeHash(Encoding.UTF8.GetBytes("f^%j#D12PS**t"));
                var sb = new StringBuilder(encUname.Length * 2);
                foreach (byte b in encUname)
                {
                    // can be "x2" if you want lowercase
                    sb.Append(b.ToString("x2"));
                }
                string encUsername = sb.ToString();
                var encPwd = sha1.ComputeHash(Encoding.UTF8.GetBytes("6r5Y$ZhvkH&amqW6*"));
                sb = new StringBuilder(encPwd.Length * 2);
                //encUsername = encUsername + ":";
                foreach (byte b in encPwd)
                {
                    // can be "x2" if you want lowercase
                    sb.Append(b.ToString("x2"));
                }
                //encUsername = encUsername + sb.ToString();
                string encPassword = sb.ToString();
                var bytearray = Encoding.UTF8.GetBytes(encUsername + ":" + encPassword);
                //string encoded = System.Convert.ToBase64String(sb);
                client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(bytearray));
                var response = await client.GetAsync(uri);
                if (response.IsSuccessStatusCode)
                {
                    var content = await response.Content.ReadAsStringAsync();
                    // content is correct, need to read string from first record
                    var res = JsonConvert.DeserializeObject<NameRecords>(content);
                    foreach (var record in res.records)
                    {
                        wineryName = record.Name;
                    }
                }

            }
            catch (Exception ex)
            {
                Debug.WriteLine(@"         ERROR {0}", ex.Message);
            }

            return wineryName;
        }

After reading through this code, you may be wondering what differentiates it from the previous code. Notice the fact that this is receiving an id parameter and returning a string. The other routine receives np filtering parameter and returns a list of winery items.

        
        
        public async Task<double> GetLat(int id)
        {
            var uri = new Uri(string.Format(Constants.WineriesUrl, "winery_by_id.php?id=" + id));
            Double latCoord = 0;
            try
            {
                SHA1Managed sha1 = new SHA1Managed();
                var encUname = sha1.ComputeHash(Encoding.UTF8.GetBytes("* username *"));
                var sb = new StringBuilder(encUname.Length * 2);
                foreach (byte b in encUname)
                {
                    // can be "x2" if you want lowercase
                    sb.Append(b.ToString("x2"));
                }
                string encUsername = sb.ToString();
                var encPwd = sha1.ComputeHash(Encoding.UTF8.GetBytes("* password *"));
                sb = new StringBuilder(encPwd.Length * 2);
                //encUsername = encUsername + ":";
                foreach (byte b in encPwd)
                {
                    // can be "x2" if you want lowercase
                    sb.Append(b.ToString("x2"));
                }
                string encPassword = sb.ToString();
                var bytearray = Encoding.UTF8.GetBytes(encUsername + ":" + encPassword);
                client2.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(bytearray));
                var response = await client2.GetAsync(uri);
                if (response.IsSuccessStatusCode)
                {
                    var content = await response.Content.ReadAsStringAsync();
                    // content is correct, need to read string from first record
                    var res = JsonConvert.DeserializeObject<NameRecords>(content);
                    foreach (var record in res.records)
                    {
                        latCoord = Convert.ToDouble(record.Latitude);
                    }
                }

            }
            catch (Exception ex)
            {
                Debug.WriteLine(@"         ERROR {0}", ex.Message);
            }

            return latCoord;
        }
        public async Task<double> GetLong(int id)
        {
            var uri = new Uri(string.Format(Constants.WineriesUrl, "winery_by_id.php?id=" + id));
            Double longCoord = 0;
            try
            {
                SHA1Managed sha1 = new SHA1Managed();
                var encUname = sha1.ComputeHash(Encoding.UTF8.GetBytes("*username *"));
                var sb = new StringBuilder(encUname.Length * 2);
                foreach (byte b in encUname)
                {
                    // can be "x2" if you want lowercase
                    sb.Append(b.ToString("x2"));
                }
                string encUsername = sb.ToString();
                var encPwd = sha1.ComputeHash(Encoding.UTF8.GetBytes("* password *"));
                sb = new StringBuilder(encPwd.Length * 2);
                foreach (byte b in encPwd)
                {
                    // can be "x2" if you want lowercase
                    sb.Append(b.ToString("x2"));
                }
                string encPassword = sb.ToString();
                var bytearray = Encoding.UTF8.GetBytes(encUsername + ":" + encPassword);
                client2.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(bytearray));
                var response = await client2.GetAsync(uri);
                if (response.IsSuccessStatusCode)
                {
                    var content = await response.Content.ReadAsStringAsync();
                    // content is correct, need to read string from first record
                    Records = JsonConvert.DeserializeObject<WineryRecords>(content);
                    foreach (var record in Records.records)
                    {
                        longCoord = Convert.ToDouble(record.Longitude);
                    }
                }

            }
            catch (Exception ex)
            {
                Debug.WriteLine(@"         ERROR {0}", ex.Message);
            }

            return longCoord;
        }
        public async Task<List<String>> GetCounties()
        {
            Counties = new List<String>();
            var uri = new Uri(string.Format(Constants.WineriesUrl, "read_counties.php"));
            try
            {
                SHA1Managed sha1 = new SHA1Managed();
                var encUname = sha1.ComputeHash(Encoding.UTF8.GetBytes("* username *"));
                var sb = new StringBuilder(encUname.Length * 2);
                foreach (byte b in encUname)
                {
                    // can be "x2" if you want lowercase
                    sb.Append(b.ToString("x2"));
                }
                string encUsername = sb.ToString();
                var encPwd = sha1.ComputeHash(Encoding.UTF8.GetBytes("* password *"));
                sb = new StringBuilder(encPwd.Length * 2);
                foreach (byte b in encPwd)
                {
                    // can be "x2" if you want lowercase
                    sb.Append(b.ToString("x2"));
                }
                string encPassword = sb.ToString();
                var bytearray = Encoding.UTF8.GetBytes(encUsername + ":" + encPassword);
                client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(bytearray));
                var response = await client.GetAsync(uri);
                if (response.IsSuccessStatusCode)
                {
                    var content = await response.Content.ReadAsStringAsync();
                    Countys = JsonConvert.DeserializeObject<CountyRecords>(content);
                    foreach (var record in Countys.records)
                    {
                        Counties.Add(record.ToString());
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(@"         ERROR {0}", ex.Message);
            }
            return Counties;
        }
    }

All three of these Get routines were left to be explained together due to the similarities. Latituide and Longitude are retrieved for a single record, as I am sure that you noticed because of the use of id in the winery retrieval RESTful endpoint.

The GetCounties task returns a list of counties. You may recall from step 2 that the read_counties php routine will return all unique counties using the DISTINCT SQL keyword.

public class WineryItemManager
    {
        IRestService restService;

        public WineryItemManager(IRestService service)
        {
            restService = service;
        }

        public Task<List<WineryItem>> GetWineries(string typ, string val, int wrs, int distanceFrom)
        {
            return restService.ReadWineries(typ, val, wrs, distanceFrom);
        }

        public Task<string> GetName(int id)
        {
            return restService.GetName(id);
        }

        public Task<List<String>> GetCounties()
        {
            return restService.GetCounties();
        }

        public Task<List<string>> GetLTDNames()
        {
            return restService.GetLTDNames();
        }
    }

Now we get to review the WineryItemManager class. It is quite similar in design and functionality to the interface we have seen since it defines some of the same features used in RestService.