It wasn't long ago that I said that whenever I use the Xamarin.Forms Messaging Center - it feels a bit like cheating.
After all, I feel like I should be able to structure my app well enough so components can communicate without having to use an intermediary.
But, that's not always the case ...
The Case for Messaging Center
Something in the core project could happen that it wants one of the platform projects to know about... like starting a long running background download...
That background download in one of the platform projects finishes and it needs to broadcast that it's done (to potentially a lot of listeners)...
Things could happen in one view model that you want other view models to know about...
So there are some valid reasons to use the messaging center.
It's just that the syntax to use it is so easy ...
MessagingCenter.Subscribe<TSender>(object, string, Action<TSender>);
and
MessagingCenter.Send<TSender>(TSender, string);
It just seems like cheating ... and wrong.
Now - in is Evolve 2016 talk on Xamarin.Forms performance, Jason Smith does say to prefer to use something else, like Prism, over the built-in Messaging Center. Who am I to argue with the guy who created Xamarin.Forms? But ... I like to stay as close to the vanilla Forms as I can.
How Messaging Center Messed with Me
The big benefit of Messaging Center is that it allows communication between two classes that know nothing about one another. In fact, the subscriber doesn't even need to be instantiated at the time the message is sent - no big deal.
In other words - the classes are decoupled from one another.
But the big way that Messaging Center messed with me, and the reason why it just never sat right with me is the way I used the TSender
argument in the MessagingCenter.Send
function.
I always sent the name of the class in which the message originated from.
So if a message originated from a LoginViewModel
and was subscribed to in a UserDetailViewModel
- the subscribe may look something like this:
MessagingCenter.Subscribe<LoginViewModel>(this, "successful_login", (lvm) => HandleLogin(lvm) );
So the UserDetailViewModel
still knows about the LoginViewModel
... and that doesn't seem right.
And it's not.
I won't go into the reasons into why I was fooled into believing that was the right way ... but it's not.
The right way is to create a class that has nothing to do with either class that's sending or subscribing to the message.
It could be as simple as a marker class - or a class that has no properties or functions.
By having this class that's only related to the message and not the classes that send or subscribe - then that seems right, and not so cheat-y.
A send would look like:
MessagingCenter.Send<LoginMessage>(new LoginMessage, "successful_login");
Then the subscribe:
MessagingCenter.Subscribe<LoginMessage>(this, "successful_login", (lm) => HandleLogin(lm));
Ahh ... much better - now the view models know nothing about each other.
And even better - not the message can originate from one of the platform projects without any issue. (As there's no way I could have done it by sending a class from the platform project into the core project.)
The Moral of the Story?
Always use a marker/generic class that has nothing to do with the sender or receiver class within the Messaging Center!
Comments