SwiftUI Помогите определить причину ошибки Index out of range
Пожалуйста, разъясните начинающему в чем ошибка. Пытаюсь из Core Data получить данные и вывести их как текстовые поля. В onAppear делаю заполнение массива с типом MyMessage. Для контроля вывожу на печать через print. Вижу, что массив заполнен корректными данными. Но при попытке вывести элементы массива в текстовые поля получаю ошибку:
Fatal error: Index out of range: file Swift/ContiguousArrayBuffer.swift, line 444
Вот код:
import SwiftUI
import CoreData
struct MyMessage: Codable, Hashable {
let msgHeader: String
let msgBody: String
let msgFooter: String
}
struct MyMessageView: View {
@Environment(\.managedObjectContext) var managedObjectContext
@State private var showMsg = [MyMessage]()
var body: some View {
VStack {
Text(self.showMsg[0].msgHeader)
.padding(.bottom, 10)
Text(self.showMsg[0].msgBody)
.padding(.bottom, 10)
Text(self.showMsg[0].msgFooter)
}
.padding()
.navigationBarTitle(Text("Message"), displayMode: .inline)
.onAppear(){
// from Core Data
let requestM = NSFetchRequest<NSFetchRequestResult>(entityName: "Messages")
let sortDescriptorM = NSSortDescriptor(key: "msgId", ascending: true)
requestM.predicate = NSPredicate(format: "msgId == %@", NSNumber(integerLiteral: 1))
requestM.sortDescriptors = [sortDescriptorM]
do {
let resultM = try self.managedObjectContext.fetch(requestM) as NSArray
self.showMsg.append(MyMessage(
msgHeader: (resultM[0] as AnyObject).msgHeader,
msgBody: (resultM[0] as AnyObject).msgBody,
msgFooter: (resultM[0] as AnyObject).msgFooter
))
Print(self.showMsg[0])
} catch {
print(error)
}
} //onAppear
} //body
}
Если я отказываюсь от использования массива и ввожу отдельные переменные для каждого элемента, то все работает отлично. Но у меня есть чувство, что так делать не совсем правильно.
Это код второго варианта
import SwiftUI
import CoreData
struct MyMessageView: View {
@Environment(\.managedObjectContext) var managedObjectContext
@State var msgHeader = ""
@State var msgBody = ""
@State var msgFooter = ""
var body: some View {
VStack {
Text(self.msgHeader)
.padding(.bottom, 10)
Text(self.msgBody)
.padding(.bottom, 10)
Text(self.msgFooter)
}
.padding()
.navigationBarTitle(Text("Message"), displayMode: .inline)
.onAppear(){
// from Core Data
let requestM = NSFetchRequest<NSFetchRequestResult>(entityName: "Messages")
let sortDescriptorM = NSSortDescriptor(key: "msgId", ascending: true)
requestM.predicate = NSPredicate(format: "msgId == %@", NSNumber(integerLiteral: 1))
requestM.sortDescriptors = [sortDescriptorM]
do {
let resultM = try self.managedObjectContext.fetch(requestM) as NSArray
msgHeader = (resultM[0] as AnyObject).msgHeader
msgBody = (resultM[0] as AnyObject).msgBody
msgFooter = (resultM[0] as AnyObject).msgFooter
} catch {
print(error)
}
} //onAppear
} //body
}
Как решить проблему Index out of range для первого варианта?
UPD Как вариант решения на уровне костыля, это делать перед выводом проверку на пустоту массива через if isEmpty.
Ответы (1 шт):
Здесь проблема в том, что onAppear срабатывает позже, чем впервые выполняется код внутри VStack (можете проверить, установив там точку останова), когда массив showMsg еще пустой. Исправить можно например так (упрощенный вариант без класса для модели Core Data)
struct MyMessageView: View {
@Environment(\.managedObjectContext) var managedObjectContext
@State private var showMsg = [MyMessage]()
var body: some View {
VStack {
Text(self.showMsg.first?.msgHeader ?? "")
.padding(.bottom, 10)
Text(self.showMsg.first?.msgBody ?? "")
.padding(.bottom, 10)
Text(self.showMsg.first?.msgFooter ?? "")
}
.padding()
.navigationBarTitle(Text("Message"), displayMode: .inline)
.onAppear(){
// from Core Data
let requestM = NSFetchRequest<NSFetchRequestResult>(entityName: "Messages")
let sortDescriptorM = NSSortDescriptor(key: "msgId", ascending: true)
requestM.predicate = NSPredicate(format: "msgId == %@", NSNumber(integerLiteral: 1))
requestM.sortDescriptors = [sortDescriptorM]
do {
let resultM = try self.managedObjectContext.fetch(requestM) as! [NSManagedObject]
self.showMsg.append(MyMessage(
msgHeader: resultM.first?.value(forKey: "msgHeader") as! String,
msgBody: resultM.first?.value(forKey: "msgBody") as! String,
msgFooter: resultM.first?.value(forKey: "msgFooter") as! String
))
print(self.showMsg[0])
} catch {
print(error)
}
} //onAppear
} //body
}