Carl Zeiss AG – Innovation Project

The Carl Zeiss AG operates in many different markets across all segments and customers. While offering internal and external software solutions for many different devices, they previously chose to invest heavily into the .NET platform and gained a huge expertise in this over the years. As Zeiss continues to support the most modern end customer's devices in the mobile area, they faced the traditional problem of multiple codebases in different programming languages for the Windows, Android and iOS platforms.

With a team of the following people, Microsoft and Zeiss spent two days together on creating a clean architecture for an exemplary Xamarin app for internal use:

  • Daniel Fischer, Carl Zeiss
  • Marc Weber, Carl Zeiss
  • Daria Kern, Carl Zeiss (Intern)
  • Michael Schieszl, Carl Zeiss (Senior Product Engineer)
  • Robin-Manuel Thiel, Microsoft (Technical Evangelist)

Customer profile

During their search for a cross-platform approach that fits both, their high quality standards and the need for flexibility to offer highly customized solutions, they consulted Microsoft. Together, we developed a workshop to implement one of many following internal applications using Microsoft's solution for the mobile platform dilemma: Xamarin.

While the purpose of the developed application itself was a very simple one, Carl-Zeiss asked the Microsoft team to focus the workshop on best-practices when it comes to working with Xamarin and a very clean and separated architecture. Architectural cleanness becomes highly valuable when sharing logic between different projects plays a significant role, which Zeiss is targeting.

As a first project and to demonstrate the benefits of these clean architectures, an exemplary Cantina Application (GustoApp) for internal use has been designed and created.

Problem statement

As a leading industry company, over the last years, Zeiss invested heavily into the .NET platform and now has a huge base of knowledge and experiences employees who's preferred programming language C# is. While always extending the product portfolio also to reach endcustomers' mobile devices, the company faced the traditional platform dilemma of having multiple code-bases in different programming languages for the Windows, Android and iOS platforms, which are hard to maintain, test and develop in parallel.

In this and many upcoming projects at Carl-Zeiss, the mobile application itself acts as a data view layer for information that gets provided by a backend, so traditional servers are where most of the business logic happens. It was a critical success criterion, to support these backends, while also solving problems of the modern and mobile world, like offline support for connection losses.

Carl Zeiss's quality standards are very high. To continuously improve their products, they want to collect customer feedback right on the way and be able to monitor the actual usage of their app and specific features as well as gaining crash reports, whenever a bug might have made it into production.

Solution, steps, and delivery

To guide Zeiss in the future of using Xamarin as their platform for mobile applications and being enabled to reuse their existing C# skills and staying native for every single platform they want to support, we developed an exemplary cantina application for internal use together. We were able to demonstrate the effectiveness of Microsoft's offering for mobile apps and create a very clean architecture for this and future mobile projects.

In this section, we will discuss, how the results of the workshop looked like, how they fit with above's problem statement and why we chose specific approaches and technologies.

Architecture

We were very lucky to have a diverse round of very skilled experts at the table and were able to mix many years of .NET expertise from Carl Zeiss's side with a fair amount of mobile app experience and architectural knowledge from Microsoft. The architecture, we developed as a blueprint for future mobile projects should follow the principles of Clean Code, Separation of concerns and Highest possible reusability.

MVVM

To achieve these concepts, the pattern decision resulted in using the very famous Model-ViewViewModel (MVVM) framework. What's mentionable about MVVM is the fact, that is is very flexible as neither the UI (View) nor its logic (ViewModel) knows anything about each other. The ViewModel only provides a set of information (Properties) and functionality (Commands) that the View can use via Data Binding.

This fact makes the core logic for specific views within the ViewModels re-usable across different platforms so that the same ViewModel can control iOS, Android, Windows and any other UIs and their logic. Because of this re-usability and UI independence, MVVM became the de-facto standard pattern for Xamarin applications.

Structure

The solution structure we ended up with looks like this and will be discussed in the following.

  1. 01 Core (folder)  o GustoApp.Core (PCL)
  2. 02 Backend (folder)  o GustoApp.Backend (ASP.NET WebAPI)
  3. 03 Fronted (folder)  o GustoApp.Frontend (PCL) o GustoApp.Frontend.Forms (PCL) o GustoApp.Frontend.Forms.Droid (Xamarin.Android) o GustoApp.Frontend.Forms.iOS (Xamarin.iOS) o GustoApp.Frontend.Forms.Windows (UWP)
  4. 04 Styles (folder)  o ZeissStyles.Core (PCL) o ZeissStyles.Droid (Xamarin.Android) o ZeissStyles.iOS (Xamarin.iOS)
  5. 05 Test (folder)

Core Project

The GustoApp.Core project is a Portable Class Library (PCL) and contains everything that can be shared with all other projects. Especially code, that can be shared between frontend and backend can be found here.

  • Platform independent models and common types
  • Interfaces for platform dependent models

Backend project

The GustoApp.Backend project is an ASP.NET WebAPI project in this case but can also be of every other .NET based backend technology, like ASP.NET Core and others. As its name suggests, it contains the backend logic and refers to the Core Project.

  • Backend logic
  • Implementations of interfaces for platform dependent models (from Core Project)

Frontend project

The GustoApp.Frontend project handles all the logic that is frontend related but not dependent on a specific platform or technology. It contains most frontend code and remains sharable across frontend technologies by using Dependency Injection whenever it comes to platform or technology related implementations. It also references the Core Project.

  • ViewModels
  • Implementations of interfaces for platform dependent models (from Core Project)
  • Platform independent services
  • Interfaces for platform specific services

Xamarin.Forms projects

For the cantina application GustoApp, we decided to use Xamarin.Forms as the technology to share UI components across iOS, Android and Windows, because of a simple User Interface that basically worked the same way on all desired platforms. These projects all reference the Frontend project.

Important: While Xamarin.Forms might not be the right choice for all other future mobile projects, the traditional Xamarin platform approach might be. As we separated framework independent logic in an own GustoApp.Frontend project, we can still re-use all of this code on other projects with different frontend technologies or could even change our choice of Xamarin.Forms, whenever it does not fits our needs anymore without having to rewrite lot of code!

  • Xamarin.Forms code
  • Views
  • Implementation of interfaces for platform specific services (from Frontend project)

Connecting the dots

By taking a look at the code, we can gain insights of how all these separated projects work with each other.

ViewModels

The ViewModels contain all the logic that is related to a specific view, without knowing anything specific about it. It does not interact with UI elements directly, but offers variables (properties) and methods (commands) that can be bound to the view later using MVVM's data binding.

Logic inside of commands might need other Services to interact with a backend or platform specific functionality. These services will get injected to the ViewModel using Dependency Injection.

 

public class ExampleViewModel
{
    private IPlatformSpecificService platformSpecificService; private GenericService genericService;

    public ExampleViewModel(IPlatformSpecificService platformSpecificService, GenericService genericService)
    {
        this.platformSpecificService = platformSpecificService; this.genericService = genericService;
    }
}

Usually, there are two types of services, that gets injected into the ViewModel: Platform specific and platform independent ones. While platform independent services are often defined within the Frontend project itself and can get injected directly, the platform specific ones, need an implementation using platform APIs. This is why we define Interfaces for them to declare the functionality, the implementation should offer.

public interface IPlatformSpecificService
{
    void ReadTextToSpeech(string text);
}

This interface gets implemented for each platform separately, and this implementation gets injected into the ViewModel later.

Important: The ViewModel itself does not know anything about the platform specific implementation and this is intended. All it needs is any implementation of the interface that offers any way to achieve what its functionality. This enables, to re-use the ViewModel in any other frontend project without modifications. All a platform needs to do, is injecting an implementation of the needed interfaces to the ViewModel.

Implementation of interfaces for platform specific logic

As described above, the ViewModel relies on the implementations of its needed interfaces. These interfaces might need platform specific APIs and have to get implemented for each platform then.

There project of this implementation will differ regarding to the required APIs. If the

Xamarin.Forms framework itself brings all these requirements, the Frontend.Forms project can be the right place for this implementation.

 

public class XamarinFormsService : IPlatformSpecificService
{
    public void ReadTextToSpeech(string text)
    {
        // Xamarin.Forms implementation 
    }
}

If an iOS, Android or Windows specific implementation is needed, that does not get covered by the Xamarin.Forms framework, it has to be written for each platform inside their Frontend.Forms.XXX projects.

public class AndroidService : IPlatformSpecificService
{
    public void ReadTextToSpeech(string text)
    {
        // Android implementation 
    }
}
public class IosService : IPlatformSpecificService
{
    public void ReadTextToSpeech(string text)
    {
        // iOS implementation 
    }
}
public class WindowsService : IPlatformSpecificService
{
    public void ReadTextToSpeech(string text)
    {
        // Windows implementation 
    }
}

 

Xamarin.Forms brings its own small dependency service to offer these platform specific implementations to the shared cross-platform code. To do so, all we have to do is adding the following line of code.

[assembly: Xamarin.Forms.Dependency(typeof(YourPlatformImplClass))]

Injecting the implementations

To bring the pieces together, the architecture needs a single point, where all implementations get injected into the ViewModels and Services. For this, we created a ServiceLocator class and instantiated it as a static singleton.

As the Dependency Injection Framework that comes with Xamarin.Forms lacks some important functionality as registering services as singletons for example, we decided to use the 3rd party library MVVM Light for this. It brings a SimpleIoC class that handles registering system-wide services for us.

 

public class ServiceLocator
{
    public ServiceLocator()
    {
        // Create IoC Container 
        Microsoft.Practices.ServiceLocation.ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
        SimpleIoc.Default.Reset();

        // Register cross-platform services         
        SimpleIoc.Default.Register<ICrossPlatService, CrossPlatService>();
        // Register platform specific services 
        var platformImpl = DependencyService.Get<IPlatformSpecificService>(); SimpleIoc.Default.Register<IPlatformSpecificService>(() => platformImpl);
        // Register ViewModels 
        SimpleIoc.Default.Register<MainViewModel>();
        SimpleIoc.Default.Register<DetailViewModel>();
    }
}

 

Getting the instance of specific ViewModels and Services from anywhere in the Solution can now be simply done by calling the following lines of code.

var viewModel = SimpleIoc.Default.GetInstance<MainViewModel>();

When calling this, the IoC Container will take a look at the ViewModel's or Service's constructor and look for instances of the needed dependencies to inject. So it is important to make sure that all the services that the ViewModel or Services has dependencies on, got registered before.

Xamarin Frontend

To share the frontend structure across iOS, Android, and Windows without a need to implement each UI on its own, we chose to use the Xamarin.Forms framework. With its pre-defined UIcontrols, it provides everything, Zeiss needed for their cantina application's interface.

The frontend is described in XAML which will get rendered to the native control equivalent of each platform. As XAML gets rendered at runtime by default, which may influence the overall app performance, it is highly recommended, to activate XAML Compilation by adding this assembly flag to each page.

[XamlCompilation (XamlCompilationOptions.Compile)]

Azure Backend

Carl-Zeiss already had a backend service powered by ASP.NET for that provides cantina meal information. By now, these information have only been accessible via a Website that was not mobile or offline compatible.

The backend was written with ASP.NET and has already been hosted at a Web App within the Microsoft Azure App Services, which made it very easy to simply connect the Xamarin App to the existing backend using the REST interface it provides.

Offline Sync

To achieve offline capabilities for the app, which has been one of the requirements, the team chose to bet on the following solution. The app should be able to let the user see the cantina's menu plan, even without a constant internet connection. So the meal information had to get stored locally and synchronized with the online backend whenever a connection can get established.

Microsoft Azure's Mobile App already provides an integrated Offline Sync SDK that is based on SQLite database technology. SQLite is a cross-platform database that can live on the device and even work in offline mode.

Zeiss does not use this Mobile App from Azure as they have their own ASP.NET backend but the team chose to use the same approach as this SDK does. So the cantina app also creates a local SQLite database on each platform and stores the data locally directly after downloading. This enables the app to choose between two different data sources when displaying data to the user: The downloaded or the locally saved one.

Whenever the user modifies the local data by rating a meal, for example, this information gets synchronized back to the cloud backend, whenever a network connection is available. This ensures, that the user can fully interact with the system, even when no direct connection to the server could be established.

Analytics, Reports and Beta Testers with HockeyApp

As mentioned earlier, Zeiss was looking for a solution to catch crash reports and collect anonymous usage data of their applications even when they are not being debugged but out in the field. Also, they need a way to roll out pre-releases of the application to a specific group of internal testers only and gain their feedback before rolling out global updates.

These requirements perfectly match with HockeyApp, which we were able to quickly set up and integrate into the applications easily with just a few lines of code because of a very good and easy to follow documentation.

Now, the team can add testers to the HockeyApp dashboard that get automatically notified whenever a new version is ready to get tested. While using this version of the app then, telemetry data and crashes will automatically get reported back to the developers. As HockeyApp integrates with other DevOps solutions, it was easy to connect it with Visual Studio Team Services, that the team uses for code control and work planning. This lets HockeyApp create work items for new incoming crashes and bug reports automatically!

Conclusion

By deciding to use Microsoft's Mobile Solutions like Xamarin and HockeyApp, Carl-Zeiss enable themselves to create and maintain business logic of any mobile application just once and share it easily across other platforms. Using HockeyApp ensures to remain on the high quality standards that Carl-Zeiss always had.

Based on the success of the GustoApp cantina application, Carl-Zeiss will leverage the existing knowledge and resources to realise future mobile projects. During this workshop, we created the foundation for future software projects by creating a reference architecure that is highly reusable and maintainable by separating concerns in a clean way and following modern software patterns.

On this strong basis, Carl-Zeiss and others can continue growing their mobile business and driving it to success!

Additional resources

Diesen Beitrag als PDF herunterladen.


PDF herunterladen