DEV Community

Cover image for Lt. Coremander Data, pt 1
Joel Groomer
Joel Groomer

Posted on

Lt. Coremander Data, pt 1

View project at this stage on GitHub here.

Core Data

One of the first things that I do when getting ready to start a new app is think about the data structure. I usually have a kind of vague idea about how the app will look, but that's much less important to me than how it will work.

Screenshot: snippet of data structure notes I wrote in Bear

Snippet of data structure notes I wrote in Bear

Personally, I think that one of my strengths is being able to think this through and imagine how the different objects will relate to one another before I start coding. Then as I start defining entities in the model editor, in code, or even just in a text editor, potential problems become clear and I'm able to adjust.

Modeling data

It's really important to me that I get this right. I may spend a lot of time thinking about it, going over it again, modeling and re-modeling, writing code and rewriting code. The data model is the foundation of the app. It should be

  • Thorough
  • Logical
  • Forward-thinking
  • Complete

...but without being overly complicated or trying to include things just because they seem cool or like you might want them in the future.

I literally just made these four points up, but I like them, so I'll elaborate.

Routine entity in model editor
Step entity in model editor

Thorough

If you're having trouble wrapping your head around your data model, just try to think of everything you might want to possibly do in your app and how it relates to that object. Write a list if you need to. Start to think about how those actions you want to accomplish relate to properties in objects. What needs to be a property? What might be redundant?

You can see my Entities in the model editor in the images above. For those who aren't familiar with Core Data, an "Entity" in Core Data becomes a Class in your code. The model editor helps you to define them based on Core Data's constraints, and Entities are (by default, anyway) magically converted into subclasses of NSManagedObject that Xcode and the compiler can understand and that you can use in your code. Entities have Attributes which become the properties of your objects.

Again, if you're not familiar with Core Data you might also look at my Entities and think there's a few obvious omissions. Don't we want a timestamp for when objects were created, for example? It's actually not needed in this app for two reasons. First, it's irrelevant. There won't be any sorting done by date. Instead, sorting will be handled by the listOrder attribute. Second, CloudKit, which will be utilized later on, automatically adds a creation timestamp when you first save any CKRecord to iCloud, so if it turns out down the road that a creation date is needed, this will be a good enough approximation.

Another thing that might appear to be missing if you're used to working with databases is an ID number. Core Data is going to keep track of all of that behind the scenes. Although Core Data uses SQLite to actually store the data by default, this is all abstracted away for us. Since the cloud back end is iCloud, and CloudKit is designed to work with Core Data, we don't need to worry about communicating by ID number to a server.

Logical

Another reason that I spend so much time on the data model is because I know that I (and all of us) have biases and assumptions that I need to overcome. When you take time to imagine your objects interacting in code (or if that's too "meta" for you, imagine yourself using the app when it's complete), you start to see those assumptions unravel.

Do your attributes really make sense? Do you need to hold on to old data once its been replaced by new input? Is there something you're storing that can always be easily calculated ("always" being a key word here)? Is there something you assume you'll be able to calculate, count, or infer that you should probably store instead?

Sometimes what seems simple when you're dreaming up an app can become very complicated very quickly when you think about implementation! We all dream of simplicity - especially developers in the Apple ecosystem. But the thing about an app that's simple to use is that data and logic are complex. The app will only be as simple for the user proportionally to the amount of effort its developers are willing to put in to abstract away that complexity.

Relationships

Finally, once you're sure that you're storing the right things, you also need to be sure that you're relating them properly. In Core Data we can do this pretty simply by just adding relationships to our Entities. Just make sure that you've go the type of relationship ("to-one" vs "to-many") correct.

If you're architecting the whole database, it can be a little more complicated depending on the complexity of your data. Make sure to really think about not just how the data is stored, but also how you'll want to get it out. User your unique keys to your advantage and get really good at joining tables so you can make sure that you'll be able to do so in straightforward, not convoluted, ways whenever possible.

Forward-thinking

I try to think ahead as much as possible at this early stage so that when things inevitably change in the future the impact on the data model is minimal. One way you can do this is by using the String type in your model and mapping it to an enum in your Swift code.

Here's an example:

enum StepType: String, CaseIterable {
    case silence, text, mantra, audio, video
}
Enter fullscreen mode Exit fullscreen mode

In the model editor, Step has an attribute of type of type String. The enum above adopt the String type for the rawValue of its cases. This allows us to do three really cool things:

  1. Store the value in Core Data as a string very easily
  2. Utilize the enumerated values in our Swift code for easy switching, looping, etc.
  3. Avoid "magic strings" that are super easy to screw up because the compiler can't help us check if they are entered correctly.

The only real downside is that converting to and from enums to rawValues can be cumbersome and make for some long lines of code.

So how does this help us to stay forward-thinking? It's SUPER easy to add another case to the StepType enum, and that adds the corresponding string as a possible value for the Step entity's type attribute automatically. We don't have to change our model at all, we just have to implement the new feature.

Complete

Now here's one area that I'm not living up to my ideals. I already know that my model doesn't quite cover all of the features I want to implement. The problem is that I need to learn how to implement those features and exactly what I'll need to be storing. Rather than put the whole app on hold, I'll be adding new Entities as needed later on.

To be continued...

That's it for part 1! In part 2, I'll discuss extending the model's classes to add useful and very common functionality that will help me to convert data back and forth between Core Data types and Swift types.

Let me know what you think so far. If you find this discussion useful, I'd love to hear about it. If you have some different ideas, I'm always open to learning and reassessing my own thinking.

Top comments (0)