Fixing a Broken Data Model
Recently I read a tweet that resonated with some iOS app issues I've run into lately.
"I have a dictionary of dictionaries of arrays in Swift, how do I…"
— Rob Napier (@cocoaphony) December 28, 2016
Let's rephrase that to "I have a broken data model, how do I improve?"
(I still write mostly in Objective-C, maybe some of these issues are totally different in Swift, but I think they are mostly the same.)
You're starting an iOS app, and need to interact with an API. Its fairly simple to use NSURLSession
tasks to get some JSON. Converting the response to a NSDictionary
and pass it along with the intention of cleaning it up later when things settle down is where I tend to end up.
There are a few problems with this approach
- The key names are repeated in several places. If (when?) they change you have to change them in several places.
- Access by string key name is cumbersome and error prone. This is especially bad with nested dictionaries.
- If you have to do any conversion, from simple things like
integerValue
to more complicated things like using aNSDateFormatter
, this has to be repeated in different parts of your code. -
JSON
NULL
values. NSDictionaries seem to gracefully handle cases where a given key is not included, but if the JSON returned aNULL
value, that is an additional case that I tend to forget about. Having to addif([value isEqual:[NSNull null]]) { //do something differently }
in more than one place gets cumbersome quickly.
So you've gone down this path and are now bogged down, now what? I think the right answer is a set of model classes, with everything nicely defined as properties, build from a good networking stack. Thats a good ideal - but it takes a good deal of effort to get there. What steps can you do now to help make things better?
Step One
I found myself here with a client app recently. Here is how I started.
Make a new class that will house your model object. (A struct may work if you're in Swift.) Give it an initializer that takes a NSDictionary. I call mine initWithDictionary
.
Make a property for the NSDictionary, and put it in the .h file so that its public. Store your dictionary in that property. Now you can go through your code and replace your Dictionary items with this new class.
Yes, this doesn't fix any of the underlying problems, but gives you a foothold to start from.
Step Two
Now you can start to migrate common items into properties in the class itself.
There are two main options here
Quick and Dirty
Instead of making a normal property with a backing instance variable, just make a custom getter that plucks the value out of your internal dictionary. Yes, this still isn't "good" yet, but at least you have the access and error checking code in one place, instead of strewn all over your project.
The goal here is to have clear actionable steps that won't take forever. If you've gotten to this point on a real project, your data model is starting to collapse on itself, and you probably don't have spare mental energy (or time) to yank everything out and do it "right".
Better
Once you have a little more time, you can take one of these custom getters, and turn it into a full property. Move the parsing logic to your init function, and initialize your property there.
Last step
Once you get everything cleaned up, you don't need the Dictionary you swept under the rug anymore. You can either remove the property for your dictionary, or at least move it to the private section of the interface. This will show you any spots you are still accessing via the original dictionary.