Xamarin.Forms: it works

Xamarin.Forms: it works

In July 2017, I received a call from a former colleague asking me wether I wanted to begin a new app journey with him. The client would be Paris Turf, an editor specialized in horse races results.

My lovely lovely lovely horse [1]

They wanted to diversify and launch a new product called Exalt Training aimed at horses trainers.

For this, they teamed up with Souffl, an award winning innovation french company.

If you want to know more about the product itself: https://souffl.com/projets/la-premiere-solution-de-tracking-sportif-dediee-aux-entraineurs-de-chevaux-de-course/

After failing to deliver an iOS app, they were 6 months behind schedule, and decided to switch from pure native to Xamarin native, to cover both android and iOS platforms.

Xamarin Forms is mature AND powerful

Yup I said it. If anyone of you is still wondering, here is your answer: it is robust, fast, it handles complex scenarios and even fantasmagoric ideas from your favorite designer.

But ENOUGH SAID!

We successfully implemented a complex design including:

  • Gradients backgrounds
  • Many animations
  • Drawing gradient paths on Google Maps
  • Data visualization with curves
  • A carousel with a large number of views (20+)
  • Horizontal list view with snap effect
  • Grid view with drag and drop
  • Custom Tabbar
  • Custom navigation made of Tabbar with hidden menu revealing with a slide up gesture

For each of these prerequisites we had to find a technical answer. We will walk through all these answers as follow:

Technical answer Prerequisites
SkiaSharp Animation / Draw on map / Plots
Custom renderers Gradient / Horizontal list views
Xamarin animation API Animations
Xamarin custom components BottomBar / Tabbar
Lottie Animations
WebSocket Real time communication

Finally we'll add a note on the awesome Visual Studio App Center.

Remark: for convenience, all the included gif and png are captured from the Android emulator, but be assured that the exact same resources are available on our iOS app.

UPDATE: here is the video of the iOS version.

SkiaSharp

SkiaSharp is a cross platform and low-level api. You draw in canvas with primitives like DrawText, DrawCircle, DrawPath. So it's very powerful but also requires some maths skills to make your own layouts.

All the following views have been made with SkiaSharp.

Drawing on top of Google Maps

This one was a bit challenging because you have to convert gps positions to pixel coordinates without calling the Google Maps SDK (to have good response time).
Fortunately I already solved this issue 10 years ago working on another project.

At this time we were rendering custom icons and paths with GDI+ on a Virtual Earth hosted in a web view. So I had already the mathematical Mercator projection magic :)

The remaining job was not difficult, since it's just drawing a path with shaders so we could show the horse effort with gradients.

This skiasharp bit was embedded in a custom ContentView called SessionMap.

The colors represent the effort of the horse, blue is light and red is maximum effort.

Drawing curves

Next to this map we wanted to show the horse effort (heart rate and speed), so we needed to draw curves over the horse training time.

We created a ContentView named SessionGraphView for this.

Linking the two

Of course we wanted some interaction. The user can drag the timeline along updating the effort number AND the matching position on the map.

This was achieved by binding property CurrentTime from the SessionGraphView to the SessionMap component.

Custom control

For another screen, we needed a date range selector indexed by month. Since such a control didn't exist in Xamarin Forms, we decided to implement it in SkiaSharp.

This is a simple control named MonthSlidesView embedded in a ContentView and exposes properties such as CurrentLowerDate and CurrentUpperDate.

Of course this control interacts with curves reflecting horse's progression over time.

Glowing number animation

This animation is 100% SkiaSharp, included in a SKCancasView named GlowingNumberView.

It's made thanks to an infinite loop, it basically follows the technique described here: https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/basics/animation.

With two circles growing and alpha channel interpolation.

Zoom in on animation

Custom renderers

CarouselView

One of the screen feature a carousel of all the horses which are currently training. We call it the Live screen.
So we needed a carousel view. Unfortunately the Xamarin.Forms CarouselView is still in pre alpha (for 2 years now :) https://www.nuget.org/packages/Xamarin.Forms.CarouselView.

So we switched to an open source one: https://github.com/alexrainman/CarouselView.

I was disappointed with the performance and some resize issues, so we ended up forking it: https://github.com/roubachof/CarouselView. For example, on Android we switched from PageAdapter to FragmentStatePagerAdapter to avoid memory issues.

Horizontal/Grid-ListView

For this one, we wanted some specific behaviors:

  1. With a horizontal layout, a snap effect should be applied to the first visible item
  2. With a grid layout, we should be able to drag and drop items to reorder the collection
  3. Since the items layout were complex, we wanted some view preloading to smoothen the scrolling

The Xamarin Forms ListView was too simple for this use.
So we dug the web and found this blog post: (https://causerexception.com/2018/02/06/xamarin-forms-ultimate-horizontal-list-guide/).
Unfortunately the implementation was very naive and the performance really bad (each time a view was retrieved, a new one was created, so no recycling...).
We simply had to reimplement from scratch a Forms ListView which we called HorizontalListView.

Implementation

On Android, we used a RecyclerView, snapping was achieved with the help of a LinearSnapHelper and drag and drop with a ItemTouchHelper.
For performance, we preload X item views asynchronously on a background thread.

On iOS, a UICollectionView was used. Drag and drop is fully supported and we use a simple trick to have the snap effect.

The implementation of this Xamarin Forms ListView will be uploaded to github soon.

The horizontal list view standard mode

The list view in grid mode

Other renderers

  1. Gradient
  2. Material frame (cardView on android custom shadow on ios)

We tried to create most of the time custom Xamarin.Forms views.

Xamarin Forms animation

We used the animation api extensively to achieve several visual effects like:

Animations Type Gif
Folding/unfolding Rotation + Height
Heartbeat animation Scale
Appearing view Translation + Fade See TabBar.

Custom Xamarin Forms containers

We also created some components in "pure" Xamarin.Forms, meaning without renderers.

TabBar (android style)

We splitted the component in two:

  1. A TabHostView which is responsible for drawing tabs and keeping their visual state
  2. A ViewSwitcher which hides/shows view with a discrete animation

The two are linked together with a SelectedIndex bindable property.

TaskLoaderView

We created a generic Xamarin.Forms component displaying a ActivityLoader while loading and handling all the error state of the view:

  1. Empty state (no result)
  2. Error state (error message + retry button)
  3. Error while refreshing (snackbar with error message)
  4. Success state (displaying the view content)

This component is based on Stephen Cleary's NotifyTask.

You can have a more detailed technical explanation of it here: https://github.com/roubachof/Xamarin-Forms-Practices.

BottomBar (ios style)

A custom navigation container was designed. At first it's really just a bottom tabbar, but if you swipe the tabbar up, a container taking all the screen reveals itself, showing a menu.
The menu contains the remaining available secondary actions, like user profile and settings.
This component is just a regular TabHostView and ViewSwitcher. But the TabHostView is positionned at the bottom of the screen.
When the component is at Peek state, only the bottom bar is shown.
Applying a slide gesture, we use a simple translation to move the whole view up and reveal the menu which was hidden so far.
Respectively, a slide down closes it.

Lottie

We use lottie for some components which were just too diffult to implement:

Web socket and communication

We used the .Net ClientWebSocket for handling live communication with the server.

For REST services, we used:

  1. Refit
  2. Polly (retry policy for our socket client)
  3. InsanePowerPack (http cache for refit)
  4. Fusillade (lesser priority for sync services and image loading)
  5. Custom in-memory cache (cache invalidation is hard ;)

Visual Studio App Center

We use app center for continuous integration. Each push on master triggers a build on Android and iOS.

We use a prebuild script to create 2 different applications based on the branch the build is running on. If we build an app on the qa branch, the app will be wired to our back office. But if it's built against the qa-mock, then all the data are mocked.

We also use the distribution feature to beta test our app with various users.

What we like the most about app center crash reports, is the ability to include some text with each report. Since we are logging relevant info in our app, we can attach our logs to each report to add specific context to the crash.

Xamarin Forms is mature AND powerful

Ok, so now we said it but we also shown it.

In the following weeks, I will publish posts on various aspects of the project. The HorizontalListView, TabHostView, TaskLoaderView, will be available on GitHub.

Restez à l'écoute !


  1. https://www.youtube.com/watch?v=jzYzVMcgWhg ↩︎