Understanding Design Patterns for Cross Platform Mobile Development
Since the inception of Crosslight last year, we have seen a number of approaches which attempt to address cross-platform development, particularly on the native mobile platforms. We have received frequently asked questions regarding the best practices for cross-platform mobile development, and what would be the ideal solution for building cross-platform apps that are highly flexible, maintainable and extensible. Since then, we have helped a lot of our customers understanding the design patterns which allow them to better architect their solutions. In this blog post, I will share some insights based on our past successful experiences.
There are various approaches in designing cross-platform solution depending on the development goals. At the simplest, you can come up with basic code sharing solution without applying design pattern, i.e., through file linking to each platform-specific project. However, that will be definitely less-than-ideal and is not recommended. In this blog post, I’ll be focusing on approaches with applied design pattern such as unified view abstraction pattern and ViewModel pattern.
Unified View Abstraction Pattern
This design pattern comes from the idea that the user-interface element (View) in each platform can be unified into a single intermediate API that reside in the shared project. The intermediate View API represents only the abstraction of the View, while the actual implementation is done at each platform behind the scene. The unification of the View seems interesting and promising at a glance, but it actually exposes more problems than the gained benefits.
One of the biggest problems is that view abstraction pattern makes sense only if the user interaction logic is defined within the View implementation itself. The nature of this pattern commonly disregards the separation of concern (SoC) between the user interaction logic and the view implementation — which is very similar to outdated patterns such as WinForms. This means that your application and user interaction logic will be “tightly coupled” to the view.
To help you understand it better, see the following code snippet which shows an example of a View implemented with this pattern.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public class ItemDetailPage : DetailPage { public ItemDetailPage() { public MainPage () { var panel = new Panel(); var label = new Label(); var button = Button() { Text = “Click me!” }; button.Clicked += (sender, e) => { label.Text = “Button ; }; panel.Children.Add(label); panel.Children.Add(button); Page = panel; } } } |
As seen in the code above, the application and user interaction logic are tightly coupled to the view definition, mostly through event handlers. With such design, we can quickly identify some of the major limitations and design issues such as:
- Limited UI Definitions — the impact of the design creates limitation in which only the lowest common denominations of the features across platforms are supported. It’s obvious that you cannot easily leverage the feature available only in certain platform.
- Poor Maintainability — the code is hard to maintain and extend since logics are coupled to the view.
- Not Testable — Functional layer cannot be unit tested since the implementation requires event initiated from the actual user interface elements.
- Not Designable — View definition must be done with hand-coding to comply with the intermediate View API. There are typically no designer support for such view abstraction.
- Not Scalable — As view is defined through code, all works are done by developers, including both coding and implementing the design. You cannot have a design team specialized in implementing the iOS screen, while another separate team implementing Android screen.
- Low Adaptability — Changing views will often breaking functionality since the application logic is coupled to the view. As the results, modifying views can be tedious, complex, and commonly break the existing code, for instance, consider changing a list view into a collection view.
It is tempting to think that you can also create Model and ViewModel layers to use in conjunction with the unified view pattern. However, doing so brings us back to zero degree. Applying the view abstraction pattern will be pointless when you find yourself needing to create extra layers. So it’s an one-off approach — you can choose to apply the unified view pattern with the imposed limitations and design drawbacks, or choose the ViewModel pattern that emphasize on strong separation of concerns principle.
That said, the view abstraction pattern is actually one that we avoided at best since the first day we laid out our apps infrastructure and components design. Our architect team never consider this approach as a feasible solution due to the limitations and issues it brought. Unless you are implementing super simple apps that do not concern on code’s maintainability, testability and extensibility; you should avoid building cross-platform apps with this pattern.
ViewModel (MVVM) Pattern Comes to Rescue
An ideal cross-platform development based on our years of experience in building cross-platform apps and tools is to adapt the SOLID principle in our design pattern. The principles suggest the architectural pattern to be designed in ways that produce the following results:
- Low coupling between layers, high cohesion within them.
- Separation of concerns, user interaction logic and view should be implemented in separate layers, resulting in greater code maintainability, testability, extensibility and scalability.
- User interface (View) layer should contain no business or interaction logic, enabling changes or modification to the views without affecting user interaction logic.
- Business/application logic layer contains no user interface (view) and don’t refer to any of the view layers.
It is imperative to conform to these principles if your goal is to create great cross-platform apps that are highly scalable, maintainable, and can be easily scaled at times.
One of the popular design patterns today called Model-View-ViewModel (MVVM) allows you to achieve most of the principles described above. By nature, MVVM separates the user interaction logic out from the view layer, but still cohesive within them through the use of two-way data binding. This enables you to easily implement the user interaction logic in a separate layer called ViewModel — without concerning the views. Likewise, you can flexibly design the views without concerning or fearing to break the functional logic. This makes MVVM pattern one of the most appealing solutions for cross-platform development.
The following diagram illustrates a typical architecture implementation with MVVM design pattern.
The only issue with this pattern is that you require a seriously solid MVVM framework and tools that are built with the MVVM pattern. This includes the frameworks for the ViewModels, dependency delegation, event aggregators, commands, as well as View components that support data binding and the ViewModel paradigm.
Well, the good news is that we’ve got you covered. Read on.
Cross Platform Mobile Development Done Right
By now, you should already have insights what’s the most ideal solution for building cross-platform apps. Applying a design pattern is required to build highly successful one, and choosing the right design pattern is crucial in determining the future of the apps.
When our team engineered Crosslight last year, we decided to build it on a solid design pattern — one that can serve as a foundation for mission critical apps. We chosen MVVM, not only because it’s real good, but because we have great success in building cross-platform tools with it. And more importantly, we’ve helped countless of enterprise customers building highly successful apps with this pattern. We’ve experienced it, and seen how development team get real results — clear responsibility, improved productivity and accelerated development speed.
Instead of unifying the heterogenous views across different platforms into a single API, Crosslight lets you build cross-platform mobile apps in the most natural way possible. You write the user interaction logic code entirely in ViewModel, while freely designing the view in each IDE of the platform. The view doesn’t contain any UI logic, so the designer team is free to unleash their creativity to build great user experiences that matter on each platform. It’s the best of three worlds — developers, designers, and users.
The following illustrations show how a Crosslight view is designed in iOS and Android respectively:
As seen in the above screenshots above, you can easily design the view and layout to your liking – all within your favorite IDE. More importantly, you can access all the properties and modify them conveniently in a flick of finger. In contrast, accessing platform-specific properties is something you’re limited to do with unified view abstraction pattern.
The comprehensive data binding support in Crosslight means that you are free to use any existing controls available in your toolbox, arrange and size them the way you want, and set the behaviors and appearance settings the way you desire. As long as the ID matches the binding definition, everything will work beautifully at runtime. No new API to learn. No code required for implementing design. It just works.
With MVVM, Crosslight offers great benefits not available in other solutions such as:
- Targets multiple platforms at once (efficiency)
- High code reusability (maintainability)
- Great native user experiences (usability)
- High extensibility (customizability)
- Loosely-coupled components (testability, scalability)
To learn more about Crosslight architecture and how it applies the MVVM design pattern, see Understanding Crosslight Architecture.
To see Crosslight in action with the supported MVVM capabilities, download the MVVM Samples here.
Fostering Innovative Design, Yet Sharing Single Codebase
While building cross-platform apps, you want to reuse as much code as possible, from the domain models, data access layer, to the application and user interface logic. Yep, we got that! Crosslight allows you to share 100% of these code base, thanks to the solid architecture. And even better, you don’t have to sacrifice the design and user experiences of each platform. Building apps with great user experiences is arguably the most important aspect particularly in native mobile platforms.
One of the features that our developers love the most is the ability to instantly change the user interface elements (view) without breaking any of the existing business and interaction logic. In short, you can have a single ViewModel and have it presented in various type of views. That’s just one of the many benefits of using MVVM pattern.
For example, consider you have a list of items displayed with a table view on the phone. Later, you realized that it doesn’t look so good in the tablet and wish to change it to a collection view. You can easily achieve such scenarios with Crosslight since it embraces the MVVM design pattern. Thinking to use unified view abstraction to achieve this simple goal? Good luck with that : )
The following illustration shows the list view on the iPhone and collection view on the iPad — all done in a single ViewModel.
As seen in the above illustrations, creating great experiences that are unique for each platform and even for each device kind becomes so straightforward and manageable, thanks to the MVVM pattern which emphasizes clear separation of concerns. More than just static user interface elements, Crosslight ships dozens of UI components that are optimized with native user experiences – such as the one shown in iPad screenshot above.
You can download the Inventory samples here and try it for yourself. Seeing is believing.
Wrapping Up
So far, I have only scratched the surface of the benefits of MVVM design pattern, and how Crosslight is built to leverage it to the fullest. Notably, applying a solid design pattern is crucial in apps development, particularly if you are targeting cross-platform. Hopefully this post gives you inspirations and better insights in understanding design patterns for cross- platform development.
You can quickly get started and learn more from the Crosslight Developer Center. In the next blog, I will discuss more UI goodness included in Crosslight, which features to use for particular scenarios, and how you can use them together to build great apps experiences.
Best,
Jimmy
Nice write up Jimmy!
Thanks Ben. Glad it helps.
Feel free to let me know if you have any questions.