I was writing a project in Xamarin.Forms recently and I really wanted to try out the Prism framework. I was excited to do so -- it's open source, part of the .NET foundation, has good community and team support, and was recommended by Miguel de Icaza at Xamarin Evolve 2016.
For the most part I've really enjoyed using Prism but there has been one thing bothering me on a personal preference and refactorability level: the navigation.
If you haven't used Prism before that might sound like a big deal; navigation is a huge part of how we think about and architect mobile applications. I should say, however, that the system Prism has in place is really, really great with support for url based navigation and query string arguments so it's ready to go for robust deep linking out of the gate.
My problem with the magic of that url navigation is strings. It might not irk others as much as it does me but I really try to stay away from "magic strings" when I can to keep my code a bit more resilient to refactoring and typos. Adding extensions on top of the string-based navigation system doesn't help too much with type safety but at least if I rename something or am looking for something in intellisense I feel like I won't waste quite as much time dealing with runtime crashes from silly mistakes while iterating.
I'll share what I've come up with (so far) below but I think I should point out one (or two) last thing(s) -- Prism is a large framework that supports WPF, Windows 10 UWP, and Xamarin Forms applications. I was just building a mobile application here (iOS/Android), so it made sense to make some extensions for my convenience but the system they've built out is much more robust for building applications that cross several platforms and form factors and I think that (among other things) more than justifies their string-based navigation. It's worth noting that my solution relies on a decent amount of Reflection which has its own performance issues. This was done purely out of preference.
You should read the Prism docs to get a full picture but the idea is that using your Page names you can create navigation stacks with simple URLs. For instance, "/LandingPage/ContactsPage/ContactDetailPage" would create a nav stack leading up to a ContactDetailPage and giving an Absolute URL like this would set your entire application to that stack (while using a relative URL would just append the pages to your current stack).
What I did first was create a
INavTransaction interface and
NavTransaction class (ignore the bundle object for now, we'll get to it later):
Pretty simple! Each
NavTransaction will have a Type argument of the Page we want to navigate to and we'll use that to generate the URLs for navigation within Prism's
NavigationService. I am currently just using a simple helper class to do just that:
This helper is pretty self-explanatory -- just accept an arbitrary amount of
NavTransaction objects and create a Uri string using the type Name (Note: I'm using MoreLINQ here for the simple
That's really it for making the URL navigation a bit more friendly for my preference. Here's a sample of how you might use that with Prism's NavigationService to create the same absolute navigation sample above:
It's definitely a little long-winded looking but that's a sacrifice I was willing to make.
Okay, back to those bundle objects that are in the
NavTransaction objects. In Prism (seriously, read those docs they're short, well written, and more verbose) you can pass data to your navigation destination with query string like so (taken from Prism docs):
NavigationParameters object from Prism basically creates a dictionary that you can then use to look up your desired data in your destination ViewModel (using
Nice, right? It really is, I'm not trying to be an ass. It's just the casting and the strings that I want to abstract out. So let's do that.
Okay, that was a longer bit of code to swallow and is a work-in-progress for me but it's sufficient for my project at the moment. Let's take a look at what it looks like using these extension methods. Here's a quick sample of a relative navigation to a ContactDetail page using the
NavTransaction's bundle property:
Again, pretty simple if verbose. Here's what it looks like trying to get that data in the destination ViewModel:
Not bad, we got rid of all the strings and things should refactor a little more cleanly with this, I think. I do want to talk about something that was introduced in Prism 6.3, however, and that is
KnownNavigationParameters. Prism introduced some helpful properties to
NavigationParameters that can be seen over at Github. These properties are added to every
NavigationParameter dictionary. For now when resolving objects I am ignoring those properties which is why I have that method to check for and ignore any keys defined in that class (again, using reflection).
These additions have given me a little peace and confidence at the cost of some reflection. I'm interested to see if anyone wants to bash me for this or has any ideas for improvement!