Мои основные данные не загружаются до вызова представления в моем методе init()

#swift #core-data #swiftui

#swift #core-data #swiftui

Вопрос:

Я использую этот метод для установки значений состояния в значения в моих пользовательских настройках объекта CoreData, чтобы я мог отображать их на экране.

Наиболее распространенный метод извлечения объектов CoreData — запрос на выборку, но по какой-то причине он не загружает значения и сообщает, что массив пуст:

Просмотр профиля:

 import HealthKit
import SwiftUI
import Combine //to use Just

struct ProfileView: View {
    
    @Environment(.managedObjectContext) private var viewContext

    @ObservedObject var viewModel: AddViewModel = AddViewModel()
@FetchRequest(
            entity: UserSettings.entity(),
            sortDescriptors: [])
    private var userSettingEntity: FetchedResults<UserSettings>


init(){


let x = viewModel.fetchProgresses()
let y = userSettingEntity.count

print(y)
print(x.count)
if(!x.isEmpty){
    print(x[0].bmr)



userName = x[0].firstName!
  ageInputString = String(x[0].age)
  heightInputString = String(x[0].height)
  weightInputString = String(x[0].weight)
  selectedGender = Double(x[0].age)
  chosenActivityLevel = x[0].activityLevel!
  BMR = x[0].bmr
    
    }
}
   

    var body: some View {

    
    VStack{ //vertical stack for the form
        Form{
            Section{
                Text(String(BMR))
    }

Any help would be really useful :) 
 

Для контекста вот моя viewmodel:

 class AddViewModel: ObservableObject {
    
    @Published var userSettings = [UserSettings]()

    
    init() {
    //    print(fetchProgresses().count)

    }
    
    //Creating the data
    func addCalorieTrackerDate(id: UUID, firstName: String, height: Double, weight: Double, gender: String, age: Double, activityLevel: String, bmr: Double) {
        CoreDataManager.shared.addUserSettingsData(id: id, firstName: firstName, height: height, weight: weight, gender: gender, age: age, activityLevel: activityLevel, bmr: bmr) { (isAdded, error) in
            if let error = error {
                print(error)
            } else {
                print("Data has been added!")
                print(gender)
            }
        }
    }
    
    //fetching the data
    func fetchProgresses() -> [UserSettings] {
        CoreDataManager.shared.fetchCalorieTrackerData { (userSettings, error) in
            if let error = error {
                print(error)
            }
            if let userSettings = userSettings as? [UserSettings] {
                self.userSettings = userSettings
            }
        }
        return userSettings
    }
 

CoreDataManager.swift:

 class CoreDataManager {
    
    static let shared: CoreDataManager = {
        let appDelegate = AppDelegate.instance!
        let instance = CoreDataManager(managedObjectContext: appDelegate.persistanceContainer.viewContext)
        return instance
    }()
    
    var managedContext: NSManagedObjectContext
    
  private init(managedObjectContext: NSManagedObjectContext) {
        managedContext = managedObjectContext
    }
}

//MARK:- CalorieTracker Insert/Update/Delete
extension CoreDataManager {
    func addUserSettingsData(id: UUID, firstName: String, height: Double, weight: Double, gender: String, age: Double, activityLevel: String, bmr: Double, completionHandler: @escaping (_ succeed: Bool, _ error: Error?) -> Void) {
        let userSettingsEntity = NSEntityDescription.insertNewObject(forEntityName: "UserSettings", into: managedContext) as? UserSettings
        userSettingsEntity?.id = id
        userSettingsEntity?.firstName = firstName
        userSettingsEntity?.height = height
        userSettingsEntity?.weight = weight
        userSettingsEntity?.gender = gender
        userSettingsEntity?.age = age
        userSettingsEntity?.activityLevel = activityLevel
        userSettingsEntity?.bmr = bmr
        
        do {
            try managedContext.save()
            completionHandler(true, nil)
        } catch let error {
            completionHandler(false, error)
        }
    }
    
    func fetchCalorieTrackerData(completionHandler: @escaping (_ succeed: Any?, _ error: Error?) -> Void) {
        var progresses = [UserSettings]()
        let progressRequest: NSFetchRequest<UserSettings> = NSFetchRequest<UserSettings>(entityName: "UserSettings")
        
        do {
            progresses = try managedContext.fetch(progressRequest)
            completionHandler(progresses, nil)
        } catch let error {
            completionHandler(nil, error)
        }
    }
    
    func deleteUserSettingData(){
        let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "UserSettings")
        let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
    
        do {
            try managedContext.execute(deleteRequest)
        } catch _ as NSError {
    }
    }
    
    
}
 

Комментарии:

1. Неясно, какой код находится в вашей модели представления и какой код находится в вашем представлении.

Ответ №1:

Вы делаете похожие вещи в разных местах, поэтому было бы неплохо провести рефакторинг и иметь один источник данных.

В модели представления измените fetchProgresses , чтобы она ничего не возвращала, поэтому вместо этого вы будете использовать опубликованное свойство userSettings из своего представления.

Затем в представлении удалите запрос на выборку и инициализацию и, как уже упоминалось, получите ваши данные из модели представления

 struct ProfileView: View {

    @ObservedObject var viewModel: AddViewModel = AddViewModel()

    var body: some View {
        VStack{  
            Form {
                Section {
                    Text(String(viewModel.userSettings.first?.bmr ?? 0))
 

Вы всегда получаете только один элемент в своем массиве UserSettings, поскольку вы используете здесь форму, а не список? Если это так, то вам следует исправить это в своей модели представления, изменив опубликованное свойство на

 @Published var userSettings: UserSettings?
 

или даже лучше, если вы хотите превратить это в более правильный MVVM, тогда у вас есть опубликованное свойство для каждого свойства в UserSettings

 @Published var firstName: String
@Published var bmr: String
...
 

тогда ваш код представления будет проще

Комментарии:

1. После изменения моей ViewModel, чтобы ничего не возвращать, я тестирую способ, который вы упомянули, для получения значений, однако всякий раз, когда я печатаю (ViewModel.UserSettings.first.FirstName), он выводит nil . А также не работает в текстовых представлениях, что странно, потому что данные определенно были введены.

2. По какой-то причине ViewModel.UserSettings всегда пуст, что странно

3. Вы отладили это (или добавили операторы печати) и выполнили fetchCalorieTrackerData и fetchProgresses, чтобы проверить правильность выборки данных?

4. Как ни странно, ваш метод получения значения работает хорошо, если я оставляю ViewModel с типом [UserSettings] . Не могли бы вы предложить использовать это, если это сработает?

5. «Предпочтительным решением, конечно, является не использовать массив, если у вас есть только один объект, но это ваш вызов.