TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

Conceptualize swift data model help!

Forums > SwiftUI

Ive been playing around with swiftdata for awhile and have overcome a couple hurdles. Ive loaded from a json, used a migration and editted/removed from my list. what I'm struggling with is the use of 2 data models that are linked. Basically I want to have a model of food items. Each item will have a few attritubtes (quantity, weight, favorited, etc). I want the ability to add a new food item or edit those that already exist. I think i have all that covered. Where I'm struggling is how to have another model ( I think i need another model, not entirely sure) of dates. So every day i want to catalog and save a dates worth of food eaten. Users can add which food they ate and their quantity. They might not use every single food item that day. Does that make sense? How do i design the models? here is what i thought below but I am very open to other ideas. Especially because this isnt working

@Model Class addDate { Var newDate: Date Var foods: Food? }

@Model Class Food { a Var title: String Var isFavorited: Bool Var addNotes: String Var quantity: int Var weight: Int }

2      

@SwiftRabbit steps outside SwiftUI and enters the rabbit hole of data modeling!

I'm struggling with the use of two linked data models that are linked. ...snip..... I'm struggling with how to model dates
(I think i need another model, not entirely sure)

It's interesting to see you joined HWS earlier this year but waited until the very end of the year to post your first message! I hope in the new year, you have a resolution to pitch-in and answer questions from fellow learners!

Data Modeling

Top tier universities have entire courses dedicated to data modeling. Junior developers spend a few years learning on the job. Your question isn't a SwiftData question, per se. It's really a data modeling question.

Sometimes you just have to create several bad models before you get that "ah HA!" moment and settle on a good model.

Still here are some pointers for you.

How is this modeled in the Real World™ ?

Lock your iPhone in your desk for a day. How would you record your daily food intake?

First, you might grab a sheet of paper and write today's date on the top. Then you might record what you ate at 9AM, bagel and 4 grammes of soft cheese. You may wait an hour, then record what you ate at 10:05 AM, a small apple. Later in the day, you may record the baguette and prosciutto sandwich you enjoyed at 1 PM with a bottle of beer. But at 2PM, you remember you had some coffee with four hazelnut biscotti at noon.

In short, your daily food diary consists of several diary entries. Each entry has a time stamp, a food item, and a quantity. Note the diary entries may not be made in sequence. And some of the food items may or may not be in your list of commonly eaten foods. Also note your common food unit may indicate a whole apple (and corresponding caloric values). However in your diary entry, you may log that you only ate 2/3 of one apple. (Or you ate 1 and a half apples!)

Verify your Diary

Do you eat 1/2 kilo of grapes? or 400 grammes? Do you eat 3 slices of pizza? or 0.9 kilos? Do you eat 1/2 cup of almond nuts, or one handful of nuts? Who goes to a pub and drinks three fifths of a pint of ale? What are the common terms you use with family and friends. Verification is an important aspect of your data model. Your application may not be useful if it's scientifically correct, but not practical for an average iPhone user. Who eats 1/3 of a tin of tuna fish? Who eats 9 and 1/2 Pringle crisps? Be sure to model real world units!

Analysis

Ok, now that you've observed your actions using pen and paper, time to transfer this to data models.

You might consider starting with basic food items you enjoy. Boil this down to a common unit that you can extrapolate later using maths. For example, you might often eat broiled chicken. Some days you might eat 150 grammes. On other days, you might enjoy 500 grammes. But a common unit might be 100 grammes of broiled chicken. You might record one piece for an apple or banana.

Food Unit

enum FoodBlock {
    // common blocks for food
    case grammes, slices, ounces, litres, pieces, cups, kilos, handful
}

@Model class FoodUnit {
    name:      String  // Broiled Chicken
    unit:      FoodBlock // common description (gramme, piece, ounce, litre, slice, etc)
    quantity:  Double // allows 1 piece, 300 grammes, 0.5 litres, 3 slices, etc
    kcalories: Double // kilocalories in the stated quantity. 1 slice of cheese pizza = 2000 kCal.
    //. ... snip ....
}

Notice the FoodUnit is just basic data. You'll use maths to calculate consumption during a meal.

Diary Entry

@Model class DiaryEntry {
// What did you eat? 
// How much?
// and When?
    timeStamp:        Date     // date and time when consumed
    item:             FoodUnit // what did you eat? This links to the FoodUnit model!
    quantityConsumed: Double   // how much of a basic food unit?
    // ... snip ....
}

Notice the basic FoodUnit may indicate the kilocalories for 1 slice of cheese pizza. This is the basic food unit. But while binge watching Band of Brothers for the fifth time, you may have consumed 4 pieces. You would log 4 basic units of Cheese Pizza at 2:10 PM.

Notice this is a 1 to 1 relationship. You may have eaten many items at 2:10 PM, but in this model each FoodUnit gets a separate Diary Entry.

Others might try to model a meal. For example, you ate your lunch between 12:15 and 12:45 PM. In the elapsed time, you may have eaten three or four FoodUnits in different quantities. How would you model this?

Optional? Why isn't item optional in the DiaryEntry model? In my realworld model, I would not record 400 grammes of >NOTHING< eaten at 3:45PM. I would require myself to add a food description. In my real world scenario, every diary entry requires a real food item.

Diary

All the food logged for a period of time (month, day, etc) may be collected into a single Diary. This is where experience plays a part. You may not need a unique data model for a Diary. You may recognize that you can select a single day's consumption from the Diary Entry table with the proper SwiftData queries.

This is subjective, and there may be no wrong answers. But to continue the analysis, you might consider modeling a Diary that is a collection of Diary Entry objects.

Keep Coding!

Please return here and let others know how you modeled your solution!

2      

@Obelix thank you for your response. I agree i could be more helpful with answering other posts. I think stems from a previous lack if confidence in my swift skills but I do think I've improved enough to answer some inquieries. what is that old adege? See one, do one, teach one? A great strategy to learn!

Now onto your solution. I've never been great with implementing enums. i really like that though. a great example being a drink verse a solid. grams vs milliliters.

I agree for a real use case a timestamp on each meal is important, however for simplicity let's assume i just want per day. Per your last sentence would this be just one model?

i agree this isn't a swiftui question as much as a data modeling question. i actually use a lot of data modeling in my real job with SQL.

To pick your brain one more time. would the use of enums let me consolidate this into one model?

also sorry about spelling/ punctuation. i was excited to see your response and typed this out on my phone.

2      

lack of confidence in my swift skills but I do think I've improved enough to answer some inquieries

This is part of @twoStraw's goals.

  1. Build your confidence
  2. Share you knowledge

I've answered plenty of questions in these forums, often wrong. But by explaining the concept in a forum answer it makes me think through several scenarios to devise a clear, elegant response. This is what helped me solidify my knowledge. I wasn't chastising you. Just encouraging you to be a voice in the community. Share your experiences!

2      

I've never been great with implementing enums.
i really like that though. a great example being a drink verse a solid. grams vs milliliters.

In SQL you might have a third table that just lists the valid portions of common foods. In SQL you'd link your FoodUnit model to the FoodPortion model so that FoodUnits can only describe themselves using records that are entered into FoodPortions. This may be overkill for your solution.

I eat three chicken drumsticks, or twelve chicken nuggets. At work I might consume one thermos bottle of soup. Are thermos bottles, drumsticks, or nuggets a common way to measure food? In my book they are, but in industry maybe not. This is your application. Be sure you address your use cases and not be a slave to industry conventions.

In a super large application targeted for multi-national audiences, you may want a datastore for common food measurements. But if your app is a diary for just you, maybe you have fewer requirements and can summarize them in an array, or enumeration?

I can't tell you one is better than the other.

enums

I can share, however, that enums are a great way for the compiler to help you validate your code! enums help you focus on just those options that your application should use. When I write an app and come across a requirement stating a user can select one of several options, I immediately think "enum" as this is a form of insurance that (until otherwise told) limits my options for case statements, pick lists, etc. The compiler is quick to complain if I try to sneak in a non-standard option!

Keep Coding

2      

I agree for a real use case a timestamp on each meal is important,
however for simplicity let's assume i just want per day.
Per your last sentence would this be just one model?

In my mind, you probably want to start with two data stores.

One is a reference table that lists common foods you consume. All of these are probably your "favorites" as you consume them a few times each week. If you are on a stricter diet to control blood sugar, lose weight, control hypertension, etc you'll have a focused set of food items that are very familiar to you.

On occasion you may deviate, and have the need to add a new item to your datastore.

The other data store is a running list of what you consume each day. If you're not worried about the time portion, this simplifies your solution a bit.

Application Design

Have you sketched out your design? Is it easy to review the day's list, or a previous list? Do you have a prominent ADD button to quickly add another food item to your daily diary?

I envision a query that looks into SwiftData and extracts only FoodUnits logged for the current date. Of course, this will be zero at the start of the day. You might click a large "+" icon to add your first entry.

The add FoodUnit view will display a date picker defaulting to the current date. Under that is a searchable list showing items from your FoodPortion data store. Here's where you might sort the list so that your favorites are at the top. Or maybe you sort the list by frequency, pushing your most selected items to the top. Do what makes this easy for you.

When you select an food portion (one chicken drumstick) you may have to augment the quantity for your diary. Maybe you have a Stepper to increase the quantity? Maybe you have a Slider or a TextField. This design is up to you.

Consider the option of multi-tapping. Maybe you had a banana AND an english muffin? Select two items and let your app add two entries to your diary: one for each element tapped.

One Table Design

I find it difficult to think of this in terms of one data store. In my mind, you'll benefit from separating the basic FoodPortions from the daily diary. This way, at a future date, you can add some analysis to your application. How many apples do you eat each month? What percent of your protien comes from chicken vs beef? I fear breaking this out later will be difficult with a single data store model.

2      

That all makese sense to a point. I think I'm still booged down in the nitty gritty on how to set up the model(s) itself. I agree eventually I'll probably want an analysis component so one table is a bad idea. How i envison this is similar to the tutorial series I saw by Tunsdev on swiftdata with some changes. I figured there would be a contentView with a lazyVgrid of cardViews.

Each card would have an image of food and a button to add to todays diet. Maybe you could click the cardView to adjust the quantity or add a note or favorite the item. Also there could be an edit item, if you wanted to change the title or image.

On the contentView I figure i can have a navigation toolbar with a button to bring up a sheet that shows everything you ate today with a button that says log the meal.

Maybe in the leading bottom nav toolbar on the content view there would be another button that says add food item. Which brings up another sheet to add a new food item.

So if I understand correctly, you agree this should be 2 models.

Below is the code you wrote earlier. I think my problem is I'm unaware how to link those tables correctly. By your logic you're adding foodUnits to the DairyEntry which you then log as separate days correct?

@Model class DiaryEntry { // What did you eat? // How much? // and When? timeStamp: Date // date and time when consumed item: FoodUnit // what did you eat? This links to the FoodUnit model! quantityConsumed: Double // how much of a basic food unit? // ... snip .... }

Also to clarify I never took it as chastizing. You were spot on with the timining. A new year, a new resolution to take my coding more seriously :)

2      

@SwiftRabbit may be carrying some SQL baggage...

i actually use a lot of data modeling in my real job with SQL.
I think my problem is I'm unaware how to link those tables correctly.

SQL Relationships

In SQL you might have two tables with related data.

// Simple Example
// --- Employee table
var id:         EmployeeID    // PRIMARY TABLE KEY (UNIQUE)
var name:       String        // the employee's Name
var department: DepartmentID  // Foreign Key to Department Table

// --- Department table
var id:   DepartmentID // PRIMARY TABLE KEY (UNIQUE)
var name: String       // the department's Name

To link these two tables in SQL you have to join the two via their identifiers.

// (Probably not actual SQL code...)
// Select all the employees in a single department
select * from employeeTable where employeeTable.department == departmentTable.id

You link the two tables by specifically matching primary and foreign keys. Gross 🤮. Who wants to do this?

SwiftData Relationships

Years of CoreData development, and now SwiftData, has abstracted this key matching away from the developer. To show a relationship, you just define your objects with the data you want to express.

// SwiftData Employee Model
@Model
class Employee {
    //Notice, primary key does not need to be defined
    var name: String
    // Notice you define a deparment object.
    // You do NOT need to assign a foreign key
    var department: Department // <-- An employee can be in ONE and only ONE department
}

@Model
class Department {
    // Notice primary key does not need to be defined
    var name: String
    var departmentEmployees: [Employee] // <-- A department might have zero or multiple employees
 }

This is more expressive, more natural. This is the deparment in which an employee works. A department may have zero or several employees.

SwiftData Magic!

Behind the scenes, SwiftData does create primary and foreign keys. This boilerplate code is written for you. But SwiftData hides this from you allowing you to easily express the properties of your business objects.

If you really want to see what table elements SwiftData produces, you can use an SQL editor, navigate to the actual datastores, and take a look inside. Warning! It's quite gnarly!

Stewart Lynch has a nice series on SwiftData on YouTube. In his series, he uses an SQL editor to peek inside datastores to demonstrate what SwiftData is doing.

See -> SwiftData: CRUD Operations
Scrub to 6:16, or so to watch him explore a SwiftData file on his device.

2      

@swiftRabbit might be missing my point....

So if I understand correctly, you agree this should be 2 models.

I don't agree that you should take my answer as the proper one and only solution. I agreed that I would try two models if this were my application.

But what I'm encouraging you to do is try and fail. Then learn. Then try and fail and learn again.

After a few attempts of your own, you'll develop a better understanding. This is success.

Keep Coding

2      

Hacking with Swift is sponsored by RevenueCat.

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.