SwiftUI 没有加载数据

Posted

技术标签:

【中文标题】SwiftUI 没有加载数据【英文标题】:SwiftUI isn't loading data 【发布时间】:2021-09-05 22:31:44 【问题描述】:

您好,我正在构建一个从 json 文件示例中获取事件的应用程序,如下所示:

["title": "Evening Picnic", "start": "November 10, 2018 6:00 PM", "end": "November 10, 2018 7:00 PM"]

在解析事件后,它必须按日期对它们进行排序并按日期对它们进行分组,List Section 还检查事件之间的冲突并将行颜色更改为红色。

我做了一个简单的用户界面,一个包含动态部分的列表和内部排序的事件,但由于某种原因,应用程序编译时没有错误或崩溃,但视图是空白的,就像数据没有加载到查看。

到目前为止,这是我的代码:

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  $0 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()
        )
    

【问题讨论】:

而不是try? 使用do/try/catch 看看是否有错误被抛出——即。 print(error)catch 块内。 您忽略了一个明确的keyNotFound 错误。 JSON 中没有 id 【参考方案1】:

这是因为您正在使用Codable,并且它正在尝试从 JSON 中解码 idid 不在您的 JSON 中,我可以看到您希望每个新实例都创建自己的 ID。

如果您在 catch 语句中打印错误,使用 do-try-catch 而不是 try?,您会看到以下内容:

keyNotFound(CodingKeys(stringValue: "id", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "没有与键关联的值CodingKeys(stringValue: "id", intValue: nil) ("id").", underlyingError: nil))

基本上,它们没有与键 id 关联的值,因为键不在 JSON 中。

您需要创建自定义CodingKeys 以排除id 被编码/解码:

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

【讨论】:

以上是关于SwiftUI 没有加载数据的主要内容,如果未能解决你的问题,请参考以下文章

Swiftui 为 Json 数据项创建加载更多按钮

使用 SwiftUI 2.0 和 Core Data 预填充数据

在 SwiftUI 中将 JSON 数据作为对象加载

SwiftUI如何监听WKWebView加载网页的进度

SwiftUI 将数据从 Firebase 加载到 TextView

SwiftUI:使用 .fileImporter 加载图像