SwiftUI 未加载数据
SwiftUI isn't loading data
您好,我正在构建一个从 json 文件示例中获取事件的应用程序,如下所示:
[{"title": "Evening Picnic", "start": "November 10, 2018 6:00 PM", "end": "November 10, 2018 7:00 PM"}]
在解析事件后,它必须按日期对它们进行排序并按日期对它们进行分组 List Section
还检查事件之间的冲突并将行颜色更改为红色。
我制作了一个简单的 UI,一个包含动态部分和事件的列表,但由于某种原因,应用程序编译时没有错误或崩溃,但视图是空白的,例如数据未加载到视图中。
到目前为止,这是我的代码:
Events.swift
public struct Event: Codable, IntervalProtocol, Hashable, Identifiable {
public var id = UUID()
var title: String
var start: String
var end: String
}
struct AppConstants {
static let mockDataFilename = "mock"
static let jsonDataFormat = "MMMM d, yyyy h:mm a"
static let sectionDateFormat = "MMMM d, yyyy"
static let eventDateFormat = "h:mm a"
}
extension Event {
public static var jsonDateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = AppConstants.jsonDataFormat
return formatter
}()
// Parse dates into Struct variables.
var startDate: Date {
guard let date = Event.jsonDateFormatter.date(from: self.start) else {
fatalError("Unable to parse date.")
}
return date
}
var endDate: Date {
guard let date = Event.jsonDateFormatter.date(from: self.end) else {
fatalError("Unable to parse date.")
}
return date
}
var interval: Interval {
return Interval(self.startDate.timeIntervalSince1970, self.endDate.timeIntervalSince1970)
}
}
ViewModel.swift
class ViewModel: ObservableObject {
let queue = DispatchQueue(label: "com.elbeheiry")
@Published var sections = [Date: [Event]]()
@Published var sortedDays = [Date]()
@Published var intervalTree: IntervalTree!
func getEvents() {
let events = Bundle.main.eventJsonData(fileName: AppConstants.mockDataFilename)
self.intervalTree = IntervalTree(events ?? [])
let inOrder = self.intervalTree.inOrder.map { [=12=] as! Event }
self.buildSections(inOrder)
}
func buildSections(_ events: [Event]) {
for event in events {
let startTimestamp = Date(timeIntervalSince1970: event.interval.min)
let strippedDate = Calendar.current.dateComponents([.year, .month, .day],
from: startTimestamp)
guard let date = Calendar.current.date(from: strippedDate) else {
fatalError("Failed to remove time from Date object")
}
self.sections[date, default: []].append(event)
}
self.sortedDays = self.sections.keys.sorted()
}
}
public extension Bundle {
func eventJsonData(fileName: String) -> [Event]? {
guard let url = self.url(forResource: fileName, withExtension: "json") else {
fatalError("File was not reachable.")
}
guard let data = try? Data(contentsOf: url) else {
fatalError("File was not readable.")
}
return try? JSONDecoder().decode([Event].self, from: data)
}
}
ContentView.swift
struct ContentView: View {
@ObservedObject var viewModel: ViewModel = ViewModel()
var body: some View {
VStack {
List {
ForEach(viewModel.sortedDays, id: \.self) { header in
Section(header: Text(header, style: .date)) {
ForEach(viewModel.sections[header]!) { event in
EventCell(event: event)
.background(Color(self.viewModel.intervalTree.isConflicted(event) ? #colorLiteral(red: 0.8078431487, green: 0.02745098062, blue: 0.3333333433, alpha: 1) : #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)))
}
}
}
}
}
.onAppear(perform: {
viewModel.getEvents()
})
}
}
这是因为您正在使用 Codable
,它正在尝试从 JSON 解码 id
。 id
不在您的 JSON 中,我可以看出您希望每个新实例都创建自己的 ID。
如果您使用 do-try-catch
而不是 try?
在 catch 语句中打印错误,您会看到以下内容:
keyNotFound(CodingKeys(stringValue: "id", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: "id", intValue: nil) ("id").", underlyingError: nil))
基本上,它们是没有值与键id
关联的,因为键不在JSON.
您需要创建自定义 CodingKey
以排除 id
成为 encoded/decoded:
public struct Event: Codable, Hashable, Identifiable {
private enum CodingKeys: CodingKey {
case title
case start
case end
}
public var id = UUID()
var title: String
var start: String
var end: String
}
您好,我正在构建一个从 json 文件示例中获取事件的应用程序,如下所示:
[{"title": "Evening Picnic", "start": "November 10, 2018 6:00 PM", "end": "November 10, 2018 7:00 PM"}]
在解析事件后,它必须按日期对它们进行排序并按日期对它们进行分组 List Section
还检查事件之间的冲突并将行颜色更改为红色。
我制作了一个简单的 UI,一个包含动态部分和事件的列表,但由于某种原因,应用程序编译时没有错误或崩溃,但视图是空白的,例如数据未加载到视图中。
到目前为止,这是我的代码:
Events.swift
public struct Event: Codable, IntervalProtocol, Hashable, Identifiable {
public var id = UUID()
var title: String
var start: String
var end: String
}
struct AppConstants {
static let mockDataFilename = "mock"
static let jsonDataFormat = "MMMM d, yyyy h:mm a"
static let sectionDateFormat = "MMMM d, yyyy"
static let eventDateFormat = "h:mm a"
}
extension Event {
public static var jsonDateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = AppConstants.jsonDataFormat
return formatter
}()
// Parse dates into Struct variables.
var startDate: Date {
guard let date = Event.jsonDateFormatter.date(from: self.start) else {
fatalError("Unable to parse date.")
}
return date
}
var endDate: Date {
guard let date = Event.jsonDateFormatter.date(from: self.end) else {
fatalError("Unable to parse date.")
}
return date
}
var interval: Interval {
return Interval(self.startDate.timeIntervalSince1970, self.endDate.timeIntervalSince1970)
}
}
ViewModel.swift
class ViewModel: ObservableObject {
let queue = DispatchQueue(label: "com.elbeheiry")
@Published var sections = [Date: [Event]]()
@Published var sortedDays = [Date]()
@Published var intervalTree: IntervalTree!
func getEvents() {
let events = Bundle.main.eventJsonData(fileName: AppConstants.mockDataFilename)
self.intervalTree = IntervalTree(events ?? [])
let inOrder = self.intervalTree.inOrder.map { [=12=] as! Event }
self.buildSections(inOrder)
}
func buildSections(_ events: [Event]) {
for event in events {
let startTimestamp = Date(timeIntervalSince1970: event.interval.min)
let strippedDate = Calendar.current.dateComponents([.year, .month, .day],
from: startTimestamp)
guard let date = Calendar.current.date(from: strippedDate) else {
fatalError("Failed to remove time from Date object")
}
self.sections[date, default: []].append(event)
}
self.sortedDays = self.sections.keys.sorted()
}
}
public extension Bundle {
func eventJsonData(fileName: String) -> [Event]? {
guard let url = self.url(forResource: fileName, withExtension: "json") else {
fatalError("File was not reachable.")
}
guard let data = try? Data(contentsOf: url) else {
fatalError("File was not readable.")
}
return try? JSONDecoder().decode([Event].self, from: data)
}
}
ContentView.swift
struct ContentView: View {
@ObservedObject var viewModel: ViewModel = ViewModel()
var body: some View {
VStack {
List {
ForEach(viewModel.sortedDays, id: \.self) { header in
Section(header: Text(header, style: .date)) {
ForEach(viewModel.sections[header]!) { event in
EventCell(event: event)
.background(Color(self.viewModel.intervalTree.isConflicted(event) ? #colorLiteral(red: 0.8078431487, green: 0.02745098062, blue: 0.3333333433, alpha: 1) : #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)))
}
}
}
}
}
.onAppear(perform: {
viewModel.getEvents()
})
}
}
这是因为您正在使用 Codable
,它正在尝试从 JSON 解码 id
。 id
不在您的 JSON 中,我可以看出您希望每个新实例都创建自己的 ID。
如果您使用 do-try-catch
而不是 try?
在 catch 语句中打印错误,您会看到以下内容:
keyNotFound(CodingKeys(stringValue: "id", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: "id", intValue: nil) ("id").", underlyingError: nil))
基本上,它们是没有值与键id
关联的,因为键不在JSON.
您需要创建自定义 CodingKey
以排除 id
成为 encoded/decoded:
public struct Event: Codable, Hashable, Identifiable {
private enum CodingKeys: CodingKey {
case title
case start
case end
}
public var id = UUID()
var title: String
var start: String
var end: String
}