Search code examples
swiftuicore-datauserdefaultsswiftdata

Accessing data in SwiftData in SwiftUI


I'm trying to understand and learn SwiftData. I want to keep some data in my application. I wanted to use swiftData to learn SwiftData and created a model like this:

import Foundation
import SwiftData

@Model
final class SwiftDataModel {
    
    var height: Optional<Double>
    var heightUnit: Optional<String>
    var weight: Optional<Double>
    var weightUnit: Optional<String>
    var gender: Optional<String>
    var waterGoal: Optional<Double>
    var volumeUnit: Optional<String>
    var activity: Optional<Int>
    var age : Optional<Int>
    var nutritionLog : Optional<Array<Any>>
    
    
    
    init(height: Optional<Double>, heightUnit: Optional<String>, weight: Optional<Double>, weightUnit: Optional<String>, gender: Optional<String>, waterGoal: Optional<Double>, volumeUnit: Optional<String>, activity: Optional<Int>, age: Optional<Int>, nutritionLog: Optional<Array<Any>>) {
        self.height = height
        self.heightUnit = heightUnit
        self.weight = weight
        self.weightUnit = weightUnit
        self.gender = gender
        self.waterGoal = waterGoal
        self.volumeUnit = volumeUnit
        self.activity = activity
        self.age = age
        self.nutritionLog = nutritionLog
    }
}

Maybe I can also add an array like weightlog. As far as I understand swiftData keeps this data in a list. Therefore, I will always need to update index 0 instead of using context.insert(data). Is this true?

My other case is this: After the user logs in, can I check whether the user's age, weight and height information is kept in SwiftData and determine the page to be opened there?

For example :


@main
struct MyApp: App {
    
    var body: some Scene {
        WindowGroup {
            
            if (ViewModel.isLogin() && !ViewModel.userId.isEmpty ) {
                // If height, weight, and age information is available in swiftData TabBarView()
                // else IntroPageView()
                {
            } else {
                LoginView()
 
            }
    
        }
    }
}

Is such a check possible or should I use "User Deafults" or "Core Data" instead of SwiftData in my application?

I'm looking forward to your answers. Thanks in advance


Solution

  • Thank you for all the answers. It did what I wanted and I'm using it efficiently.

    First, I updated my model like this:

    @Model
    final class UserInfoSwiftDataModel {
    
    @Attribute(.unique) var userId : String?
    var height: Double?
    var heightUnit: String?
    var weight: Double?
    var weightUnit: String?
    var gender: String?
    var waterGoal: Double?
    var consumedWater = [Water]()
    var consumedWaterInt : Double?
    var volumeUnit: String?
    var activity: Int?
    var age : Int?
    var nutrition = [Nutrition]()
    
    
    init(userId: String? = nil, height: Double? = nil, heightUnit: String? = nil, weight: Double? = nil, weightUnit: String? = nil, gender: String? = nil, waterGoal: Double? = nil, consumedWater: [Water] = [Water](), consumedWaterInt: Double? = nil, volumeUnit: String? = nil, activity: Int? = nil, age: Int? = nil, nutrition: [Nutrition] = [Nutrition]()) {
        self.userId = userId
        self.height = height
        self.heightUnit = heightUnit
        self.weight = weight
        self.weightUnit = weightUnit
        self.gender = gender
        self.waterGoal = waterGoal
        self.consumedWater = consumedWater
        self.consumedWaterInt = consumedWaterInt
        self.volumeUnit = volumeUnit
        self.activity = activity
        self.age = age
        self.nutrition = nutrition
    }
    
    
    
    }
    
    @Model
    final class Water {
    var date: String
    var consumed : Double
    var User : UserInfoSwiftDataModel?
    
    init(date: String, consumed: Double, User: UserInfoSwiftDataModel? = 
    nil) {
        self.date = date
        self.consumed = consumed
        self.User = User
    }
    }
    
    @Model
    final class Nutrition{
    var name: String
    var calories: Double
    var serving_size_g: Double
    var fat_total_g: Double
    var fat_saturated_g: Double
    var protein_g: Double
    var sodium_mg: Int
    var potassium_mg: Int
    var cholesterol_mg: Int
    var carbohydrates_total_g: Double
    var fiber_g: Double
    var sugar_g: Double
    var User : UserInfoSwiftDataModel?
    
    init(name: String, calories: Double, serving_size_g: Double, 
    fat_total_g: Double, fat_saturated_g: Double, protein_g: Double, 
    sodium_mg: Int, potassium_mg: Int, cholesterol_mg: Int, 
    carbohydrates_total_g: Double, fiber_g: Double, sugar_g: Double, User: 
    UserInfoSwiftDataModel? = nil) {
        self.name = name
        self.calories = calories
        self.serving_size_g = serving_size_g
        self.fat_total_g = fat_total_g
        self.fat_saturated_g = fat_saturated_g
        self.protein_g = protein_g
        self.sodium_mg = sodium_mg
        self.potassium_mg = potassium_mg
        self.cholesterol_mg = cholesterol_mg
        self.carbohydrates_total_g = carbohydrates_total_g
        self.fiber_g = fiber_g
        self.sugar_g = sugar_g
        self.User = User
    }   
    }
    

    To stop using Index, I added an id match condition to get the data of the logged in user. This way, I can access and update information in a healthy way. I used it as follows:

    @Environment (\.modelContext) private var context
    @Query (sort: \UserInfoSwiftDataModel.height, order: .forward) private 
    var mySwiftData : [UserInfoSwiftDataModel]
    var userId : String // Login/SignUp ekranından geliyor
    var profile: UserInfoSwiftDataModel? {
        return mySwiftData.first { $0.userId == userId }
    }
    
    Text(" \(String(format: "%.1f", profile?.weight ?? 0)) \. 
    (profile?.weightUnit ?? "kg")")
    

    This way, I can easily get my data not only within the list, but everywhere. Thank you for giving a time.