#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. «Предпочтительным решением, конечно, является не использовать массив, если у вас есть только один объект, но это ваш вызов.