How many times have your QA reached out to you saying that the app is “randomly” crashing, only for you to debug and find out that backend sent you fruit and not Fruit for type of food item; not sweet :(
Consider the same example as of our previous article :
This outputs the following:
Ohhh I love some Tea
But what if we change favouriteBeverage in the JSON data from Tea to tea (we just change the casing of the enum case: Tea) ?
__lldb_expr_3/MyPlayground.playground:67: Fatal error: ‘try!’ expression unexpectedly raised an error: Swift.DecodingError.dataCorrupted(Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: “favouriteBeverage”, intValue: nil)], debugDescription: “Cannot initialize Beverage from invalid String value tea”, underlyingError: nil))
Focus on the part of the message: Cannot initialize Beverage from invalid String value tea
If it was an actual app instead of a playground code, the app would have crashed. So how do we ignore the case while decoding our enum such that our app should be able to handle it instead of outright crashing?
One of the ways, and my personal favourite: Case Insensitive Raw Value
We will make use of a protocol for this purpose: CaseInsensitiveRawValue
What the above code(one that belongs to decoder) essentially does is the following:
Fetches the value from the container
First, it tries to create the enum as-is from the value
If above fails, then it iterates through the cases of the enum and tries to match it to each value while ignoring the casing. If one matches, then it assigns the same to the enum
Even after ignoring the casing, if there is no match (which can only happen if the provided value is incorrect), it throws a type-mismatch exception
Alongside providing a custom decoder, we have also added a custom initialiserthat will do the same thing in case we want to use the same mechanism for manually initialising an enum rather than via decoder
Just make sure that whenever you’re initialing the enum using a String raw value, you have to use Beverage(caseInsensitiveRawValue:) and not Beverage(rawValue:)
The overall code will now look like below:
The above code will output the following:
Ohhh I love some Tea
Bonus
This will work fine for values that have different casing from the ones specified inside the enum.
But what if we give it a different value, say juice as favouriteBeverage which is not defined inside the Beverage enum? Answer: It’ll crash
If you’ve read my previous article, you’re aware that we can handle invalid values inside enum while decoding it
Codable: Handling Incorrect Enum Values
Safeguarding Your Codebase: Dealing with Unanticipated Enum Values
What if we could combine both of them ( CaseInsensitiveRawValue and IncorrectEnumFallbackMechanism ) into one that can handle:
Different casing of value than the ones specified inside the enum
Different value than the ones specified inside the enum
Behold: CaseInsensitiveRawValueWithIncorrectFallbackMechanism
(I know, I know. The name is Hysteria-rrifying)
This will take care of both the situations specified above. The combined overall code will now look like this:
The above code will output the following:
Wait! What is that?
… instead of crashing like earlier
There might be other ways out there but like I said, this is my favorite!
That’s it, folks! Happy Coding!