SwiftUI не загружает данные

#swiftui #intervals #swiftui-list

Вопрос:

Привет, я создаю приложение, которое получает события из примера файла json, как это:

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

и после того, как он проанализирует события, он должен отсортировать их по дате и сгруппировать их по дате, а List Section также проверить наличие конфликтов между событиями и, например, изменить цвет строки на красный.

Я создал простой пользовательский интерфейс, список с динамическими разделами и событиями, отсортированными внутри, но по какой-то причине приложение компилируется без ошибок или сбоев, но представление пустое, как будто данные не были загружены в представление.

Вот мой код до сих пор:

События.стремительные

 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)
    }
}
 

Просмотр содержимого.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()
        })
    }
}
 

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

1. Вместо try? того, чтобы использовать do/try/catch и посмотреть, не возникает ли ошибка, т. е. print(error) внутри catch блока.

2. Вы игнорируете явную keyNotFound ошибку. В JSON его нет id .

Ответ №1:

Это потому , что вы используете Codable , и он пытается декодировать id из JSON. Этого id нет в вашем JSON, и я вижу, что вы хотите, чтобы каждый новый экземпляр создавал свой собственный идентификатор.

Если бы вы напечатали ошибку в операторе catch, используя do-try-catch вместо try? этого , вы бы увидели следующее:

KeyNotFound(кодовые клавиши(строковое значение: «идентификатор», значение ввода: ноль), Swift.Ошибка декодирования.Контекст(Путь к коду: [_JSONKey(строковое значение: «Индекс 0», значение intValue: 0)], Описание отладки: «Нет значения, связанного с ключевыми кодовыми ключами(строковое значение: «идентификатор», значение intValue: ноль) («идентификатор»).», Ошибка в основе: ноль))

В принципе, у них нет значения , связанного с ключом id , потому что ключа нет в JSON.

Вам необходимо создать пользовательские CodingKey символы, которые будут исключены 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
}