Another cloud enabled WP7 Wx app ..                  Part Trois: The Cloud Service

This post is a continuation of Part 1 (here) & Part 2 (here) of the series on setting up a cloud-enabled Weather app for Windows Phone.

By now, we have a fully working Windows Phone app which allows the user to pull up weather forecasts for any city of their choice. We also Push Enabled the app so that any number of cities can be pinned to the start screen using the Mango secondary Live Tiles. Now, the only thing left to do is to have some sort of a service running in the cloud or some server that is able to keep track of all these cities the user has chosen to pin &  for the service to be able to push out live weather feeds at pre-defined/customized intervals. Now, for hosting such a service, I chose to utilize a free subscription I had in Azure; this could just as well be done on any machine running IIS with open ports.

Let’s add my thoughts here on the utilization of Azure cloud infrastructure for supplementing a mobile solution. As Windows Phone & other mobile platforms have shown us, smartphones do a lot with their small batteries & any work we can offload from it’s small processing power, helps in increasing battery life & hence user experience. As such, most connected Mobile solutions could compromise of a client running on the phone & some cloud support which does the repetitive heavy-lifting for the phone, sparing it from polling. Now, Windows Azure with its vast infrastructure for scalability could provide the perfect hosting solution for cloud service support; but there are some considerations. If you have MSDN subscription, you get some Azure processing & storage time free. After some benefit upgrades earlier this year, this Azure subscription can come in very handy, as you could really keep a couple of services with storage & decent bandwidth requirements running free 24/7 all month long. However, if your MSDN is paid for by your employer, and you are using it to host a cloud service that will empower your mobile app through which you make money, you are in shady territory 🙂 So, my advice would be to check subscription & legal details to make sure you are safe. Another option is to flat-out pay for Azure services if you know your mobile app is that good & you will need the cloud support. My experience says that you need to be able to justify a $30 cost per month to be able to break even on Azure costs; be it through a paid app or advertisements. The $30 number though could fluctuate a lot based on what the needs are for your service hosting; it is simply based on my experience on running a simple service in Azure on 2 small instances & using some table storage.

So, with my babbling out of the way, here is the Azure hosted cloud service that acts as the backend for our Weather app, as it appears in the Azure portal. Now, this is set up to feed the Windows Phone Live Tiles; however, please take note that it can easily be set up to support iOS & Android Push Notifications. The Azure Toolkit for WP7 (here) already support iOS notifications, with Android support coming soon.

Azure WeatherLite Backend Service

The highlighted URL is where we get to hit up Azure to access the hosted service. This is what we had used to add a service reference for building proxies in our Windows Phone solution. With the metadata exposed, the phone app could call into any methods supported by the service. So, let’s see how we set up the cloud service. Here’s the project setup for the backend VS Solution:

WeatherBackend Project

WeatherBackend Project

 

 

 

 

 

 

 

 

 

 

 

 

So, our cloud backend solution is created using the Azure cloud solution template in VS & is made of 3 projects:

  • WeatherBackendCloud:
    This is the Azure wrapper project that includes the deliverables from the other two projects & provides for deployment settings for our service in the cloud.
  • WeatherBackendCloudSite:
    This is sort-off the web front-end of our service; essentially the WCF service is hosted inside of IIS as a web application. This project provides the “svc” WCF endpoint for our service and a 404 for everything else. Check out this wonderful video tutorial by Aaron Skonnard on how to set up your WCF service to run in IIS (here).
  • WeatherLiteBackend:
    This is our core WCF service that exposes the methods that our Windows Phone app calls into. This project includes the core processing of fetching Weather from Yahoo, packaging it up in a Push Notification payload & sending it out in an HTTP Post to the phone’s unique Channel URI in MPNS.

Now, about some implementation. The interface in our WCF project defines a standard for some methods exposed to consuming applications. The “Register” call that our phone app makes to let the cloud service know about Channel URI, city of choice & Secondary Live Tile ID has been defined as such:


    [ServiceContract]
    public interface IRegistrationService
    {        
        [OperationContract, WebGet]        
        void Register(string channelURI, int WOEID, string liveTileURI);
    }

The actual implementation of our Registration service simply hangs on to the request parameters from the phone, performs core processing to fetch weather & builds a payload for submitting to MPNS. Since this is just for demo purposes, I am storing the subscriber’s Channel URIs in session memory, since the Registration service has been defined as a “Singleton” class. For any cloud services headed for Production, you would have to consider storing the URI & other needed subscriber details in Azure table storage or SQL Azure. Here’s what I am doing:


    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class RegistrationService : IRegistrationService
    {
        public void Register(string channelURI, int woeid, string liveTileURI)
        {
           // Add subscriber details to some list.

           WeatherLiteBackendCore coreEngine = new WeatherLiteBackendCore();
           coreEngine.PerformCoreProcessing();
        }
    }

    public class WeatherLiteBackendCore
    {
        public void PerformCoreProcessing()
        {
            // Fetch current list of Subscribers.
            SubscriberList = RegistrationService.GetSubscribers();

            if (SubscriberList == null || SubscriberList.Count == 0)
                return;

            foreach (Subscriber WP7Client in SubscriberList)
            {
                // Pick up the URIs.
                subscriberChannelUri = new Uri(WP7Client.ChannelUri);
                subscriberLiveTileUri = new Uri(WP7Client.LiveTileUri);

                // Fetch weather for registered location.
                // Send out Tile update payload to MPNS.                
            }
        }
    }

Our “WeatherBackendCloudSite” simply sets up the service endpoint to use the “RegistrationService”; here’s the config in “Registration.svc”:


     <%@ ServiceHost Language="C#" Debug="true" Service="WeatherLiteBackend.RegistrationService" %>

Now, the “PerformCoreProcessing()” method is also invoked once every hour (set at application level) to get a list of all WP7 subscribers & send out appropriate live weather feeds to all. Also, the “WeatherLiteTileNotificationSender” helper class builds the requisite payloads required to send out the Live Tile updates. Please see my earlier post (here) on exactly what the payloads need to look like for the Windows Phone OS to process the incoming data bits as Live Tiles or Toast notifications. Here’s the old-school way of building a secondary Live Tile payload & submitting to MPNS for delivery to subscribing phones:


        private static byte[] PrepareTilePayload()
        {
            MemoryStream stream = new MemoryStream();
            XmlWriterSettings settings = new XmlWriterSettings() { Indent = true, Encoding = Encoding.UTF8 };
            XmlWriter writer = XmlTextWriter.Create(stream, settings);
            writer.WriteStartDocument();
            writer.WriteStartElement("wp", "Notification", "WPNotification");
            writer.WriteStartElement("wp", "Tile", "WPNotification");            
            writer.WriteStartAttribute("ID");
            writer.WriteValue(appropriateLiveTileURI);
            writer.WriteEndAttribute();
            writer.WriteStartElement("wp", "BackgroundImage", "WPNotification");
            writer.WriteValue(appropriatebackgroundImageUri);
            writer.WriteEndElement();
            writer.WriteStartElement("wp", "Count", "WPNotification");
            writer.WriteValue(appropriateTemp.ToString());
            writer.WriteEndElement();
            writer.WriteStartElement("wp", "BackTitle", "WPNotification");
            writer.WriteValue(appropriateCity);
            writer.WriteEndElement();
            writer.WriteStartElement("wp", "BackContent", "WPNotification");
            writer.WriteValue(appropriateWeatherCondition);
            writer.WriteEndElement();
            writer.WriteEndDocument();
            writer.Close();

            byte[] payload = stream.ToArray();
            return payload;
        }

        private void SendMessage(Uri channelUri, byte[] payload, NotificationType notificationType, SendNotificationToMPNSCompleted callback)
        {  
            try
            {
                // Create and initialize the request object.
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(channelUri);
                request.Method = WebRequestMethods.Http.Post;
                request.ContentType = "text/xml; charset=utf-8";
                request.ContentLength = payload.Length;
                request.Headers[MESSAGE_ID_HEADER] = Guid.NewGuid().ToString();
                request.Headers[NOTIFICATION_CLASS_HEADER] = ((int)notificationType).ToString();

                if (notificationType == NotificationType.Toast)
                    request.Headers[WINDOWSPHONE_TARGET_HEADER] = "toast";
                else if (notificationType == NotificationType.Token)
                    request.Headers[WINDOWSPHONE_TARGET_HEADER] = "token";

                request.BeginGetRequestStream((ar) =>
                {                   
                    Stream requestStream = request.EndGetRequestStream(ar);
                   
                    requestStream.BeginWrite(payload, 0, payload.Length, (iar) =>
                    {                      
                        requestStream.EndWrite(iar);
                        requestStream.Close();
                        
                        request.BeginGetResponse((iarr) =>
                        {
                            using (WebResponse response = request.EndGetResponse(iarr))
                            {                               
                                // Give back response to callback method, if any.
                            }
                        },
                        null);
                    },
                    null);
                },
                null);
            }
            catch (WebException ex)
            {
                // Do something.
            }
        }

That’s about it with the code. Once everything compiles & works with locally hosted service, we simply right-click on the cloud wrapper project & allow it to create Azure packages locally. This generates two files in the Bin/Publish directory of the Azure solution — one .cscfg file containing configuration & one .cspkg package container containing the deployable deliverables of the project. Now, we simply create a new hosted solution in Azure & upload these two files into a Production environment to expose the cloud service endpoint for our Windows Phone application. We then have a service running in Azure that the phone application can reach out to if the user chooses to pin a city’s weather to start screen & one that pushes out live weather feeds to the secondary Live Tiles. Voila!

Hope this series was helpful. Please drop comments if you see something that could be done better or have any other thoughts.

Adios!

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s