This blog post is an addendum to the article, Build a Xamarin App with Authentication and Offline Support, to be published in MSDN Magazine, September 2015. I'll update this post with a link once the article is available. Part 1, Cloud-Connected Mobile Apps – Create a Web Service with Azure Web Apps and WebJobs, which discusses the backend of the project, is available now.

To summarize from the article: Xamarin.Forms is a framework that lets you use a single code base to implement apps with UI on multiple target platforms. However, as written in Part 2, "shared code doesn’t much reduce the effort needed to thoroughly test the app on each target platform: that part of your process will take about as long as it would if you wrote each app natively. Also, because Xamarin.Forms is quite new [it first came out in May 2014], you may find platform-specific bugs or other behaviors that you'll need to handle in your code."

That particular statement comes from direct experience! Here are the behaviors we encountered with Altostratus and had to manage in the client app code (http://aka.ms/altostratusproject, in the MobileClient project):

  • The Xamarin.Forms ListView control supports grouping, but not invocation of group headers as is supported on some individual platforms. We would have liked to enable this feature in the app, but chose not to until Xamarin.Forms makes it work.
  • ListView headers appear on iOS 7 but don’t appear on iOS 8. This is a known bug in Xamarin.Forms.
  • On iOS and Windows Phone, the OnAppearing and OnDisappearing events for page navigations happen in the expected order: the originating page receives an OnDisappearing before the target page receives an OnAppearing. There’s a significant Xamarin.Forms bug on Android (and here's a duplicate bug) that causes the target’s OnAppearing to fire first. For this reason it’s necessary in the app's Configuration page to update the page’s overall changed status with every UI activity, rather than just check it once within OnDisappearing. This clearly causes a lot of extra churn.
  • On all platforms, setting the Minimum property of a Slider control (as used on the Configuration page) will throw an exception unless Maximum is already set to a higher value. This makes it difficult to set the values through data binding, because the order in which XAML binding statements are processed is indeterminate. For this reason, these properties are set in code rather than through data binding. See https://bugzilla.xamarin.com/show_bug.cgi?id=21181 and https://bugzilla.xamarin.com/show_bug.cgi?id=23665.
  • Data binding the items in a drop-down listbox is not supported at the time of writing.
  • The Xamarin.Forms WebView control is written to fire a Navigating event when the user attempts to navigate a link within the WebView. In the mobile client, we capture this event to specifically disallow navigation directly within the control and redirect the navigation to the default browser, see the code in Altostratus Extra #3. (The code for this is in the ItemPage constructor in ItemPage.xaml.cs.) However, on iOS and Windows Phone 8.1, but not Android, the Navigating event is also raised when the WebView is initialized from local content. This means that on those platforms we want to ignore the first Navigating event, whereas on Android we want to pay attention to all of them. So we just set a flag (navigateToBrowser) within the ItemPage constructor to control whether we delegate a navigation to the browser.

 

The lesson to be learned here is that when platform technologies always have their bugs, especially new ones but often mature ones as well. If something you think should be happening isn't, or you encounter some other behavior that seems odd and especially those that are inconsistent between operating systems, check into the applicable bug database like bugzilla.xamarin.com, or check on the applicable forums. It'll save you plenty of frustration. 🙂


A reader of my recent MSDN Magazine article asked what I thought about performance in Cordova apps, and here's what I wrote in response.

Performance really depends on the underlying hardware, the individual platform, and the version of the platform, because it’s highly dependent upon the quality of the app host and hardware that’s running the code.

On Windows 8.1 and Windows Phone 8.1, you’re running a native app (no webviews), and because Microsoft has put tons of perf work into the IE engine on which the app host is built, JavaScript/Cordova apps run quite well. In the perf tests I did on Windows 8.1 with JavaScript apps (see chapter 18 of my free ebook), I found that the delta from JS to C# was 6-21%, JS to C++ was 25-46%, and C# to C++ was 13-22%. This was for CPU-intensive code (and a Release build outside the debugger, of course) and thus represents the most demanding situations. If you’re primarily working with UI code in the app where the system APIs are doing the bulk of the work, I’d expect the deltas to be smaller because the time spent in UI code is mostly time spent in optimized C++.

On Android, iOS, and Windows Phone 8, Cordova apps run JS inside a Webview, and thus are very much subject to Webview performance. From what I've heard–and I haven't done tests to this effect myself–performance can vary widely between platforms and platform versions. More recent versions of the platforms, e.g. iOS 7/8 and Android 4.2 or so, have apparently improved Webview performance quite a bit over previous versions. 

In short, if you’re targeting the latest platforms, performance is decent and getting better. If you're targeting these systems, then, Cordova performance should be suitable for many types of apps, though probably not for intensive apps.

It's important to note that "performance" is not really a matter of throughput: it's a matter of user experience. I like to say that performance is, ultimately, a UI issue, because even if you don't have the fastest execution engine in the world by some benchmark measure, you can still deliver a great experience. I think of all the video games I played as a kid on hardware that was far inferior to what's probably inside my washing machine today. And yet the developers delivered fabulous experiences.

That said, running JavaScript in a Webview on platforms like iOS isn't going to match a native iOS implementation, especially with signature animations that are really hard to match with CSS transitions and such. But if you're not needing that kind of experience, Cordova can deliver a great experience for users without you having to go native.

I made a diagram that’s on http://www.visualstudio.com/explore/modern-mobile-apps-vs that tries to illustrate where Cordova falls relative to Xamarin, native, and mobile web. The vertical axis is “mobile app user experience” which includes perf as well as the ability to provide a full-on native experience like I just mentioned. 

The diagram is one way to look at the relative strengths of different cross-platform approaches. In the end, you of course have to decide what perf measures are important for your customers, do some tests, and see if Cordova will work for your project. And of course, pester the platform providers to improve Webview perf too! 🙂


Many of you have probably seen this already–an article that I wrote (with contributions from my teammate, Mike Jones), for MSDN Magazine.

http://msdn.microsoft.com/en-us/magazine/dn879349.aspx

I expect to be writing for MSDN more because my larger team at Microsoft owns the content calendar now!

 


If you're finding Visual Studio complaining about an expired certificate when building a Cordova project for a Windows target, this is a known issue because the certificate that's checked into the Cordova source tree expired on 11/11/2014. See http://msopentech.com/blog/2014/11/11/cordova-certificate-issue-were-working-on-it/ for details and workarounds.


Switching platform targets in Visual Studio

One of the most important things to know when working in Cordova is how to switch between target platforms. There aren't obvious controls in Visual Studio for this, but it's there. Either use the Build > Configuration Manager menu item, click the Debug/Release drop-down in the toolbar and select Configuration Manager:

cordova1-1

Once you're there, the Active Solution Platforms drop-down is where you set your target.

cordova1-2

Doing this will change the main VS toolbar to reflect options available for that target. This is how you switch between targets and run the app on devices and emulators.

Tip: to shortcut this you can add the platform drop-down directly to the toolbar. Right click the toolbar and select Customize at the bottom of the long list. In the dialog that appears, click the Commands tab, then select Toolbar and Standard:

cordova1-6

Now click Add Command, then pick Build > Solution Platforms:

cordova1-7

This adds the platform drop down to the toolbar. You might need to use the Move Down button in the Customize dialog to place it where you want. I also used the Modify Selection button to narrow the size to 130, and placing it where I wanted I see this on my toolbar:

cordova1-8

Running the app, and first issues encountered: varying CSS support

With everything migrated now, I was able to run the app on the Local Machine (Windows) as expected. The one caveat I found is that Cordova at present targets Windows 8.0 and not 8.1 (I think Cordova version 3.6 is supposed to do 8.1), which means that it doesn't understand variable-sized views, and only snapped/filled states. This means that in a narrower view that's later than 320px you get the 320px snapped view inside the wider view like you do with any Windows 8.0 app on 8.1.

On Windows Phone the app ran great in portrait mode, but the layout wasn't working in landscape. Hard to tell why, because the present Cordova targets Windows Phone 8.0 which means the app is running inside a webview inside a Silverlight app. This means there's no debugger support whatsoever, so I can't poke into the DOM to see what's going on.

On Android the layout is all wrong…because an Android webview in which a Cordova app runs doesn't support the CSS3 grid specification as we enjoy on Windows. Urk. I've relied heavily on grid layout but clearly I'll have to convert to flexbox, which is why I said CSS comes up early.

On iOS (in the Ripple emulator), looks like we have the same issue.

The upshot here is that when working in Cordova, your HTML/CSS work is going to be very much like writing responsive web pages for different browsers, because that's really what's happening with Cordova: your code is running inside a webview which has varying capabilities depending on the target platform. 

 

Finding your DOM in the Ripple Emulator

When running on my Android device, Visual Studio's DOM Explorer works just like it does for a Windows app–you can do in and tweak styles and so forth.

cordova1-3

When running in the Ripple emulator, you still get the DOM Explorer except that you see everything that's showing in the emulator, including all the surrounding controls:

cordova1-4

Egads! Where is my app? Well, the easy way to find it is through that click-to-select button in the upper left above: click that, then click in the emulator, and you'll see the DOM tree expand to where your app is. You'll see that it's located under the sections ui > middle > device-container > viewport container > and then the iframe "document":

cordova1-5

 

What's very cool in the DOM Explorer is that the CSS editor does inherently known what styles are and aren't supported for that target device. I can see right away, for instance, that display: -ms-grid; isn't among those supported for Android and iOS webviews, which isn't at all surprising. So the next job is to convert my grid-based CSS to cross-platform flexbox, which will be the next post.


I said that CSS would come in early, but the first thing to discuss in migrating the Here My Am! files from my existing Windows Store app project to a Cordova project. For this I'm just starting with the HereMyAm2a code from Chapter 2 of my free ebook. And in fact I'll keep it simple for starters and just migrate the HTML and CSS, leaving off the JS code for now.

Preliminary notes/build environment

Although I'd started working on a universal Windows app migration for this project, it's not a necessary step for working with Cordova, because it also produces cross-platform apps that will work on Windows and Windows Phone alike, as well as iOS and Android. With Cordova, you let plugins worry about platform-specific things–you don't want that kind of code in your project. 

In addition, it's helpful to know that you don't need a Mac as a build server to work with Cordova until you really want to produce an app for the store. With the Visual Studio extension installed, you can run a Cordova app directly in the Ripple emulator , which is very cool in that you can switch between devices, resolutions, and orientations (including between target platforms) much like Blend for Visual Studio does for Windows, so you can see just how (badly) your CSS is performing.

For myself, I'm working on a Surface Pro 3 as my main machine, have a Windows Phone hanging off it, have an Asus MemoPad7 as an Android dev device, which I wrote about recently. My team has a Mac laptop set up in one of my co-worker's offices as a build server (also for Xamarin) which I can get to over VPN to the Microsoft corpnet from my home office…pretty cool!

Project structure

I started with a new Cordova project (File > New > Project > JavaScript > Multi-Device Hybrid App > Blank App (Apache Cordova)); this is what you want to do rather than trying to convert an existing Windows project. I then migrated the appropriate files over:

Windows Project File Cordova Project File
default.html index.html (This is the default; you can use default.html if you change the start page in config.xml)
js/default.js scripts/index.js (didn't migrate the code yet; the project template uses scripts instead of js as a folder…I might change this later)
package.appxmanifest config.xml (the Cordova generic manifest)
css/default.css css/index.css
html/map.html html/map.html (there is no default html folder, so I created one just to hold this file for the time being)
images/taphere.scale-100.png images/taphere.png; I'll be switching to draw this with a canvas later on, so it's just a placeholder.
images/<logos> Not migrated yet as these need to be specific for each platform, whcih I haven't done yet.
WinJS references WinJS is not a cross-platform, open-source library, an instead of having it in the project references I downloaded WinJS 3.0 and brought it directly into the project:

  • /css/frameworks/ui-dark.css (you'll get a whole ton of warnings on styles in here because of the differences in CSS support between platforms)
  • /css/fonts/Symbols.ttf (ui-dark.css and ui-light.css assume this file is in ../fonts, so it needs to be in a sibling folder unless you modify the reference)
  • /scripts/frameworks/base.js
  • /scripts/frameworks/ui.js
  • /scripts/frameworks/WinJS.js

I don't know how much of WinJS I'll be using, but given that I have lots of experience in this area, it seemed like a good opportunity to keep up! Plus the ui-dark.css has styling that I'm used to with this app. We'll see how it goes.

  The project template also includes a merges folder for platform-specific code, and a res folder for resources that I'll get to later on.

 

Updating index.html

With this migrated project, I fixed up the references in index.html to bring in the appropriate files:

    <link href="css/frameworks/ui-dark.css" rel="stylesheet" />    
    <link href="css/index.css" rel="stylesheet" />    

    <!-- Cordova standard -->
    <script src="cordova.js"></script>

    <!-- WinJS -->
    <script src="scripts/frameworks/base.js"></script>
    <script src="scripts/frameworks/ui.js"></script>
    <script src="scripts/frameworks/winjs.js"></script>

    <!-- Project references -->
    <script src="scripts/platformOverrides.js"></script>
    <script src="scripts/index.js"></script>

Note: in a Windows project, especially with WinJS page controls, it's usually helpful to prefix all references with a leading /. You do not want to do this with Cordova because it doesn't work. I'll have to see what happens later on when I try using page controls.

Note also that you need the reference to cordova.js even though it doesn't exist in your project.

With my script/css references fixed up, I just use the same markup as before in index.html:

<body>
    <div id="mainContent">
        <header id="header" aria-label="Header content" role="banner">
            <h1 class="titlearea "> 
                <span class="pagetitle">Here My Am!</span>
            </h1>
        </header>
        <section id="section" aria-label="Main content" role="main">
            <div id="photoSection" class="subsection" aria-label="Photo section">
                <h2 class="group-title" role="heading">Photo</h2>
                <img id="photo" class="graphic" src="images/taphere.png" alt="Tap to capture image from camera" role="img" />
            </div>
            <div id="locationSection" class="subsection" aria-label="Location section">
                <h2 class="group-title" role="heading">Location</h2>
                <div id="map" class="graphic" aria-label="Map"></div>
            </div>
        </section>
    </div>
</body>

 

Up next, running the app.


With my new role as a Content Developer in Microsoft's Developer Division, I'm working now with cross-platform tools like Apache Cordova and Xamarin, and dabbling a little in Unity, and also looking at the Visual Studio ALM tools and how they apply to these project types.

To help myself ramp up on Cordova, I'm in the process of migrating Here My Am!, the app that I build throughout the pages of Programming Windows Store Apps with HTML, CSS, and JavaScript, 2nd Edition, to Cordova. I can already see there will be numerous challenges to overcome, which I'll try to write about as I work through them (preview: the first post or two will be about CSS). 

For all this I'm using the Multi-Device Hybrid Apps Extension (Preview) for Visual Studio 2013 Update 3 (how's that for a name!). One thing that's immediately clear about the extension is that it saves a whole lot of trouble in getting a Cordova environment set up. Normally you have to go get a bunch of downloads from nine different places, some of which have installers and some of which are just ZIP files, and then you have to install them in the right order figure out how to configure each one with the right environment variables. The extension does that for you.

The documentation for the extension is found on http://msdn.microsoft.com/en-us/library/dn771545.aspx; the installer page specifically describes what pieces it downloads and how to do the configuration, manually. My team is, in fact, the one that produces this documentation–more to come!