Trying to upload an API level 29 and play store console throws an error?
It’s all in the world of Nullability.
“Kotlin’s type system is aimed at eliminating the danger of null references, also known as The Billion Dollar Mistake.”
Source: https://kotlinlang.org/docs/null-safety.html
Kotlin comes with a powerful weapon Safe calls, Safe Type Cast, !! operator, Elvis operator, etc.
Safe calls:
val name = object?.anotherObject?.name ?: ""
or
val name = object?.anotherObject?.name.orEmpty()
Explanation:
Case 1: When object is null, then name = “”
Case 2: When object is valid**, anotherObject** is null, then also name = “”
Case 3: When object and anotherObject are valid**,** but name is null, then name = “” (Typical API problem 😝)
Case 4: obviously a positive case 🙂
Note: Ideally the above cases should be part of your unit tests.
for a blog's sake, I have explained it as a case.. 😆
Safe Type Cast:
Let’s talk about Android bundle, extras, etc.
Sender Class (i.e ListActivity)
intent.putExtra(EXTRA_KEY_AObject, aObject)
Destination Class (i.e ListDetailActivity)
intent.getParcelableExtra(EXTRA_KEY_AObject) as BObject
Damn it! I am an AObject, and how can you assume myself as BObject ? If I am a Java, I would brutally throw “ClassCastException”
Safe Type Cast comes to the rescue here!
intent.getParcelableExtra(EXTRA_KEY_AObject) as? BObject
Ohoo. Still cool, in case of wrong casting, my app wouldn’t crash and still the value will be assigned as `null`
Enough of the story, we all know !! operator(Forced unwrap) and Elvis operator (Optionals) as it is self-explanatory 😜
Now, what is the actual problem in the recent update?
Legacy code aside, Kotlin actually behaves in the way it wanted to.
Reference: https://android-developers.googleblog.com/2020/03/handling-nullability-in-android-11-and.html
If Android target version is 29, the code below makes sense.
intent.getParcelableExtra(EXTRA_KEY_AObject) as AObject
Post API level 29, the same code starts giving compilation errors. (believe me, we had 500 conflicts as such in our project 😧)
Do you know why getParcelableExtra returns null? Android native classes are full of Java man!
How to solve it?
Option 1:
Use Safe Type Cast as below
intent.getParcelableExtra(EXTRA_KEY_AObject) as? AObject
But there is a drawback with this approach.
The AObject even though is not null, we will have to really deal with the optional as below.
aObject?.anotherObject?.name.orEmpty()
Imagine handling defaults at tons of places.
(i.e) Activity to ViewModel to Repository to DataSources
Uff! In all those levels, nullable just flows like a river or needs to handle a null check at ViewModel.
Option 2: (My Favourite approach)
Why handle nullable at tons of places and risk your app in production? Crash in the QA or testing phase is still better than spoiling the data to the user in the production. (By showing null, or EMPTY in the UI)
Just force cast it to AObject
intent.getParcelableExtra(EXTRA_KEY_AObject) as AObject
Note: Going with the above approach,
- Please make sure that objects are non-nullable from the API (At least for the mandatory fields).
— Yes, get the API contracts fixed first. Mobile developers don’t have a luxury to update the app instantly, whereas APIs can be instantly changed. You know what I mean 😉)
2. If you’re in the middle of the release and immediately have to fix things out?., then handling the null checks or passing the defaults value before starting an activity is the solution. — The best part is that you get a warning message and you tend to pass some optional value 😄 (Ideally we should write a test in ViewModel for this case and move to the next screen only when the value is not null)