Monthly Archives: January 2016

Deeplinking to mobile apps using Sitecore Device Detection

DeepLinkNotDeferredSitecore

I have been poking around in Sitecore 8.1 and I just love what I see, there are a lot of new and interesting stuff just waiting for you to play with 🙂
When I found out about the new cool feature, Sitecore Device Detector, I knew in a heartbeat what to do with it – Mobile deep linking

Mobile deep linking is a methodology for launching a native mobile application via a link(from your website). There are a bunch of services out there that will make it easy for you:
Branch
deeplink.me
savvyapps.com
applinks.org

It’s great with the services and all but I want to do it in Sitecore style 🙂

How about following scenario:

When the visitors are checking out your website from their favorite mobile device you want them to use an App(by clicking on a link/banner). You also want them to open a specific view/page in the App. If they don’t have the App installed, you want to direct them to App Store/Google Play/Windows Store for installing the App.

So how do we do that?

1. Detect mobile device to show correct link/banner using Sitecore Device Detector and Sitecore’s content personalization – The banner will link to an OS(operative system) vendor specific “Deep link page”.
2. Deep Link page for each OS type – Apple(IOS), Android and Windows. The page will contain a javascript which will try to open the app on the Mobile device, if no success it will redirect to the stores(App Store, Google Play or Windows Store).

I used my favorite number one “framework” – Sitecore Habitat. A must read is The groundbreaking Sitecore Habitat by Anders Laub.

1. Detect mobile device

We only want to show one link/banner for current mobile device, not a link/banner for each mobile device operating system:
ThereCanBeOnlyOne

Sitecore Device Detector in Sitecore 8.1 will be our savior to help us detect correct mobile device. There is a great blog-post written by Ian Graham and he explains it very well – Sitecore device detection – Personalisation made simple.

When a visitor navigates your Sitecore website, the user agent details are cross referenced against the Netbiscuits database. This is translated into meaningful data to determine the operating system and browser.

Sitecore uses this data in combination with the Sitecore Rules Engine to allow marketers and developers full control over content customisation.

By using Sitecore’s Rules Engine we can show the correct banner(in this case Habitat’s teaser) depending on what mobile device the visitors are using. There are a bunch of Sitecore Device Detection rules to play with:
Rules and parameters for Device Detection-Picture 1-rId10-544901112
There are more than 600 Sitecore Device Detection parameters in the device database. You can use them to further customize your Sitecore sites and adapt the presentation of them on different devices using the Rule Set Editor.

This is what we want the Sitecore’s Rules Engine to do:
Rules
If the visitor is using an iPhone we want to show the “Apple Twitter App” banner. If it’s an Android then it’s the “Android Twitter App” banner and if it’s a Windows Phone we show them the “Windows Twitter App” banner. If it’s none of the above then nothing will be shown.

The banner will link to an OS(operative system) vendor page. One for Android, one for Apple and one for Windows(It will be explained in the next step).
Banner

LinkSelectionTwitter
We also want the visitor to open a specific page/view in the app, to be more specific – we want them to open Sitecore Habitat’s home screen in their Twitter App. In order to do so we need to send sitecorehabitat as a parameter: .

2. Deeplink page for each OS type – Apple(IOS), Android and Windows

The page will contain a javascript which will try to open the app on the visitors Mobile device, if no success it will redirect to the stores(App Store, Google Play or Windows Store).

Let’s do the rendering, Open App, with the following data sources to choose from: Apple(IOS), Android and Windows.
DeeplinkPage

Here are fields in the data source. The important ones are:
App Store Url – The url to the app store
App Device Url – The “base url” to open a page/view in the app
DeeplinkContent

The rendering will have a controller – AppDeeplinkController.
https://github.com/GoranHalvarsson/Habitat/blob/EmotionAware/src/Feature/AppDeeplink/code/Controllers/AppDeeplinkController.cs

namespace VisionsInCode.Feature.AppDeeplink.Controllers
{
  using System.Web.Mvc;
  using Sitecore.Mvc.Controllers;
  using VisionsInCode.Feature.AppDeeplink.Repositories;

  public class AppDeeplinkController : SitecoreController
  {

    private readonly IAppDeeplinkViewModelRepository _appDeeplinkViewModelRepository;


    public AppDeeplinkController() : this(new AppDeeplinkViewModelRepository())
    {
    }

    public AppDeeplinkController(IAppDeeplinkViewModelRepository appDeeplinkViewModelRepository)
    {
      _appDeeplinkViewModelRepository = appDeeplinkViewModelRepository;
    }


    public ActionResult OpenApp()
    {
      return View(_appDeeplinkViewModelRepository.Get());
    }
  }
}

Here is the ViewModel – AppDeeplinkViewModel.
https://github.com/GoranHalvarsson/Habitat/blob/EmotionAware/src/Feature/AppDeeplink/code/ViewModels/AppDeeplinkViewModel.cs

namespace VisionsInCode.Feature.AppDeeplink.ViewModels
{

  using System.Web;
  using Sitecore.Data;

  public class AppDeeplinkViewModel
  {
    public HtmlString StoreUrl { get; set; }

    public HtmlString DeviceUrl { get; set; }

    public HtmlString DeviceUrlWithParams { get; set; }

    public struct Constants
    {
      public struct Templates
      {
        public static ID AppDeeplink = new ID("{0E1C2680-F935-446E-82BE-DA1176C5BCB5}");
      }

      public struct Fields
      {
        public struct AppDeeplink
        {
          public static ID AppDeeplinkSettingsAppName = new ID("{275C127A-9E78-468D-B0F2-04B8F409A7E0}");
          public static ID AppDeeplinkSettingsAppId = new ID("{582D7235-2A91-4F79-88D8-1E00F2B8EC9D}");
          public static ID AppDeeplinkSettingsAppStoreURL = new ID("{56260A54-CAD0-4C17-A54B-FAA580F09ADF}");
          public static ID AppDeeplinkSettingsAppDeviceURL = new ID("{3356B59D-5796-4C23-B6F4-4588B4437375}");
        }
      }

      public struct QueryParams
      {
        public const string DeviceParams = "deviceparams";
      }
    }
   
  }
}

AppDeeplinkViewModelRepository will put it all together.
https://github.com/GoranHalvarsson/Habitat/blob/EmotionAware/src/Feature/AppDeeplink/code/Repositories/AppDeeplinkViewModelRepository.cs

namespace VisionsInCode.Feature.AppDeeplink.Repositories
{
  using System.Web;
  using Sitecore.Foundation.SitecoreExtensions.Extensions;
  using Sitecore.Mvc.Presentation;
  using Sitecore.Web.UI.WebControls;
  using VisionsInCode.Feature.AppDeeplink.ViewModels;

  public class AppDeeplinkViewModelRepository : IAppDeeplinkViewModelRepository
  {
    public AppDeeplinkViewModel Get()
    {
      if (RenderingContext.Current == null || RenderingContext.Current.Rendering == null || RenderingContext.Current.Rendering.Item == null)
        return null;

      if (!RenderingContext.Current.Rendering.Item.IsDerived(AppDeeplinkViewModel.Constants.Templates.AppDeeplink))
        return null;

      string deviceUrl = FieldRenderer.Render(RenderingContext.Current.Rendering.Item, AppDeeplinkViewModel.Constants.Fields.AppDeeplink.AppDeeplinkSettingsAppDeviceURL.ToString());

      return new AppDeeplinkViewModel()
      {
        StoreUrl = new HtmlString(FieldRenderer.Render(RenderingContext.Current.Rendering.Item, AppDeeplinkViewModel.Constants.Fields.AppDeeplink.AppDeeplinkSettingsAppStoreURL.ToString())),
        DeviceUrl = new HtmlString(deviceUrl),
        DeviceUrlWithParams = new HtmlString(BuildDeviceUrlWithParams(deviceUrl))
      };

    }


    private string BuildDeviceUrlWithParams(string deviceUrl)
    {

      if (String.IsNullOrWhiteSpace(deviceUrl))
        return string.Empty;

      string decodedQuery = System.Web.HttpUtility.UrlDecode(RenderingContext.Current.PageContext.RequestContext.HttpContext.Request.Url.Query);

      object param = HttpUtility.ParseQueryString(decodedQuery).Get(AppDeeplinkViewModel.Constants.QueryParams.DeviceParams);

      if (param == null)
        return string.Empty;

      return $"{deviceUrl}{param}";

    }

  }
}

If you remember we send a parameter, sitecorehabitat, so that the visitor can open Sitecore Habitat’s home screen in their Twitter app.
In the method BuildDeviceUrlWithParams we will add the parameter to the device URL, which will give us: twitter://user?screen_name=sitecorehabitat

Finally the view for the rendering – OpenApp.cshtml
https://github.com/GoranHalvarsson/Habitat/blob/EmotionAware/src/Feature/AppDeeplink/code/Views/AppDeeplink/OpenApp.cshtml

@model VisionsInCode.Feature.AppDeeplink.ViewModels.AppDeeplinkViewModel

@if (Model != null)
{
    <div class="AppDeeplink"
         data-storeurl="@Model.StoreUrl"
         data-deviceurl="@Model.DeviceUrlWithParams"></div>
}

As you can see we put the data in a div element to make it easy for the javascript 🙂

The javascript, AppDeeplink.js, will make it all happen.
https://github.com/GoranHalvarsson/Habitat/blob/EmotionAware/src/Feature/AppDeeplink/code/Scripts/AppDeeplink/AppDeeplink.js

var Habitat = Habitat || {};

jQuery(document).ready(function () {
    Habitat.AppDeeplink.DomReady();
});

Habitat.AppDeeplink = {
    DomReady: function () {
        Habitat.AppDeeplink.Init();
    },
    Init: function () {

        if (jQuery("body").hasClass("pagemode-edit"))
            return;

        var deeplinkData = Habitat.AppDeeplink.GetDeeplinkData("AppDeeplink");

        if (deeplinkData == null ||
            Habitat.AppDeeplink.IsNullOrEmpty(deeplinkData.storeUrl) ||
            Habitat.AppDeeplink.IsNullOrEmpty(deeplinkData.deviceUrl))
            return;

        Habitat.AppDeeplink.OpenApp(deeplinkData);

    },
    OpenApp: function (deeplinkData) {

        document.location.href = deeplinkData.deviceUrl;
        console.log("Opening URL: " + deeplinkData.deviceUrl);

        Habitat.AppDeeplink.OpenStoreTimeOut(deeplinkData);

    },
    OpenStoreTimeOut: function (deeplinkData) {

        setTimeout(function () {
            Habitat.AppDeeplink.OpenStore(deeplinkData);
        }, 400);
    },
    OpenStore: function (deeplinkData) {

        console.log("Timeout. Open fallback url: " + deeplinkData.storeUrl);
        document.location.href = deeplinkData.storeUrl;

    },
    GetDeeplinkData: function (className) {

        var deeplinkData = {};
        deeplinkData.storeUrl = jQuery("." + className).data("storeurl");
        deeplinkData.deviceUrl = jQuery("." + className).data("deviceurl");

        return deeplinkData;

    },
    IsNullOrEmpty: function (value) {
        return value == null || value === "";
    }

}

The javascript will grab the data from the view, next will be to try to open the app on the visitors device. If it fails after the timeout expires it will direct the visitors to the app store in order to install the app.

Bonus: Why not add GOALS to the “Deeplink pages” to track the visitors behaviors.

It’s nothing fancy and there can of course be improvements. Instead of using query strings I could have used rendering parameters. We could also have triggered goals in the javascript, one for installed app and one for not installed app.
Anyway I wanted to show you guys how to use the Sitecore Device Detector. Its all in github: https://github.com/GoranHalvarsson/Habitat/tree/EmotionAware/src/Feature/AppDeeplink/code.

Let me end with the following words – Sitecore.Habitat rocks!

That’s all for now folks 🙂

Advertisements