Xamarin Android : Why the loosely typed key value pair implementation for Intent extra data is bad! And why it should be looser…
An usual way to go about adding extra data to your Intents in Xamarin is by using the convenience methods provided such as :
data.PutExtra("Key1", "key"); data.PutExtra("Key2", 33);
And on the receiving end you’d use:
var value1 = data.Extras.GetString("Key1"); var value2 = data.Extras.GetInt("Key2");
It all sounds very good in theory, however, anti-pattern discussions aside regarding the choice of creating overloads for each type instead of a strongly-typed generic method, the Xamarin team made the mistake of having those retrieval methods work more like a TryParse then a straight-forward parse. The problem then is that should the data you add to the PutExtra() method not match exactly the type described in the method name when using Extras.GetWhateverType() you may end up in a silent bug situation. For instance, using the code above, value2 would be equal to 33 and of type int in this case, which is what is expected. However, if you change the retrieving code to :
var value2 = data.Extras.GetLong("Key2");
value2 ends up with the value of 0. Yep. No warning or exceptions. It just simply sets it zero and continues on happily.
Now we all know that an int fits into a long, so what’s the problem?
The thing is that Xamarin is attempting to manage the extras list as a strongly typed key-value pair store of sorts, resulting in what behaves more like a really weird implicitly typed Dictionary, so you have to pick the right method for the right type of data set, not matter if they are compatible or not.
In fact, if you change the setting to code to explicitly store a long, such as using
it actually works! Sigh.
One way to go around this is to use their convenience methods such as
var value2 = data.GetLongExtra("Key2",1)
Now as you can see, for the convenience methods they have added a mandatory second parameter which forces you to give a default value in case Xamarin can’t make the conversion successfully. Ah! So they did think about this, after all! They just didn’t do anything about it as far as the calls from the Extras collection is concerned. Now, for me, this is even worse actually. Default values should be used for genuine cases when there is no value associated with an optional piece of data and null is not appropriate. In this case though, it is being used to mask an error. Not good at all. So what’s the best solution? In my opinion, you should just take it back to basics and treat the simple Key-Value pair store as just that, a simple KV store! Meaning, use strings for all inputs and retrieve them as strings. Then perform any conversions as needed. Not only this makes it much easier to establish a convention that can be used across the development team, but also centralizes the conversion code and allows you to then use the simple native C# conversion calls that you are used to making you able to choose whether you want to default with a TryParse() call or actually throw and handle that exception somehow. As for the retrieval you can use either method, the one on the Extras collection or the convenience methods, as both would work fine :
//setting intent.PutExtra("Name", "name");
//retrieving var name = data.Extras.GetString("Name"); var name = data.GetStringExtra("Name");
Whether the Xamarin team eventually improves this or not I would still recommend following the approach of treating the key-value store as a simple collection of strings and explicitly handling your conversions so as to keep consistent with the same wide-spread paradigm of key value stores as implemented by most NoSQL storage technologies, and also to prevent obfuscation of errors.