фатальные ошибки с опциями, не имеющими смысла

#swift #uiviewcontroller #segue #instantiation #uistoryboardsegue

#swift #uiviewcontroller #segue #создание экземпляра #uistoryboardsegue

Вопрос:

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

Возьмем, к примеру, эти функции…

 override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    tableView.deselectRow(at: indexPath, animated: true)
    
  
    
    if let displayVC = storyboard?.instantiateViewController(withIdentifier: Constants.Storyboards.TeachStoryboardID) as? SchoolEventDetailsViewController {
        
        displayVC.selectedEventName = events[indexPath.row].eventName
        displayVC.selectedEventDate = documentsDate[indexPath.row].eventDate
        displayVC.selectedEventCost = documentsCost[indexPath.row].eventCost
        displayVC.selectedEventGrade = documentsGrade[indexPath.row].eventGrade
        displayVC.selectedEventDocID = documentsID[indexPath.row]?.docID
        
        navigationController?.pushViewController(displayVC, animated: true)
    }
}
 

Это в сочетании с этой функцией :

 func verifyInstantiation() {
    if let dateToLoad = selectedEventDate {
        dateEditableTextF.text = dateToLoad
    }
    
    if let costToLoad = selectedEventCost {
        costEditableTextF.text = costToLoad
    }
    
    if let gradesToLoad = selectedEventGrade {
        gradesEditableTextF.text = gradesToLoad
    }
    
    if let docIDtoLoad = selectedEventDocID {
        docIDUneditableTextF.text = docIDtoLoad
    }
    
    if let eventNameToLoad = selectedEventName {
        eventNameEditableTextF.text = eventNameToLoad
    }
}
 

Отлично загружает данные, но когда я пытаюсь выполнить переход из контроллера поиска, данных там нет.

 override func viewDidLoad() {
    super.viewDidLoad()
    
    navigationItem.title = selectedEventName
 

Я установил в названии vc имя события, а также недавно добавил текстовое поле для его хранения в экспериментальных целях (этот вопрос).

Теперь проблема в том, что я хочу выполнить передачу данных из поискового контроллера Algolia в этот VC, и у меня появились все остальные поля, кроме одного, и это был идентификатор документа. Итак, я создал функцию обработчика завершения, чтобы получить идентификатор документа в виде строки и вставить его в vc при выполнении перехода, точно так же, как это происходит при создании экземпляра vc.

Вот функция :

 func getTheEventDocID(completion: @escaping ((String?) -> ())) {
    documentListener = db.collection(Constants.Firebase.schoolCollectionName).whereField("event_name", isEqualTo: selectedEventName ?? navigationItem.title).addSnapshotListener(includeMetadataChanges: true) { (querySnapshot, error) in
        if let error = error {
            print("There was an error fetching the documents: (error)")
        } else {
            self.documentsID = querySnapshot!.documents.map { document in
                return EventDocID(docID: (document.documentID) as! String)
            }
            let fixedID = "(self.documentsID)"
            let substrings = fixedID.dropFirst(22).dropLast(3)
            let realString = String(substrings)
            completion(realString)
        }
    }
}
 

Я думал, что либо selectedEventName или navigationItem.title выполнит работу и предоставит значение, когда я использую функцию в функции передачи данных, которую я сейчас покажу :

  //MARK: - Data Transfer From Algolia Search to School Event Details
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

    
    otherVC.getTheEventDocID { (eventdocid) in
        if let id = eventdocid {
            if segue.identifier == Constants.Segues.fromSearchToSchoolEventDetails {
                let vc = segue.destination as! SchoolEventDetailsViewController
                vc.selectedEventName = self.nameTheEvent
                vc.selectedEventDate = self.dateTheEvent
                vc.selectedEventCost = self.costTheEvent
                vc.selectedEventGrade = self.gradeTheEvent
                vc.selectedEventDocID = id
            }
        }
    }
 
    
}
 

Но в итоге ничего не отображается при нажатии на результат поиска, что довольно огорчительно, я не могу понять, почему они оба являются пустыми значениями, когда я объявил их в SchoolEventDetailsVC . Я попытался принудительно развернуть selectedEventName , и он вылетает, говоря, что есть нулевое значение, и я не могу понять, почему. На самом деле в этом вопросе гораздо больше, но я просто постарался сделать его коротким, чтобы люди действительно попытались прочитать его и помочь, поскольку никто никогда не читает вопросы, которые я публикую, так что да, заранее спасибо.

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

1. Что getTheEventDocID делать? Если он работает асинхронно, возможно, код выполняется слишком поздно.

2. getTheEventDocID Получает идентификатор документа события, сгенерированный Firebase в виде строки. Он работает асинхронно, есть ли способ предотвратить выполнение кода слишком поздно? @AndreasOetjen

Ответ №1:

Я немного смущен тем, что otherVC это такое, которое устанавливает само свойство в getTheEventDocID , в то время как в замыкании вы устанавливаете свойства self , которые являются другим контроллером. Но ничего, я надеюсь, вы знаете, что делаете.

Поскольку getTheEventDocID выполняется асинхронно, представление будет загружено и отображено до того, как будут доступны данные. Поэтому viewDidLoad не видит фактические данные, но что-то, что скоро устареет.

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

 override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    otherVC.getTheEventDocID { (eventdocid) in
        if let id = eventdocid {
            if segue.identifier == Constants.Segues.fromSearchToSchoolEventDetails {
                let vc = segue.destination as! SchoolEventDetailsViewController
                vc.selectedEventName = self.nameTheEvent
                vc.selectedEventDate = self.dateTheEvent
                vc.selectedEventCost = self.costTheEvent
                vc.selectedEventGrade = self.gradeTheEvent
                vc.selectedEventDocID = id
                vc.updateUI()
            }
        }
    }
}
 

и в контроллере представления назначения:

 class SchoolEventDetailsViewController ... {
    override func viewDidLoad() {
        super.viewDidLoad()
        updateUI()
    }

    func updateUI () {
        navigationItem.title = selectedEventName
        // and so on
    }
}
 

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

1. Это otherVC vc, который извлекает детали события при создании экземпляра, я не смог вызвать getTheEventDocID в текущем tableVC, потому что в этом vc нет сохраненной информации, но я попробую это сделать. @Andreas Oetjen

2. Да, он по-прежнему вылетает с нулевой ошибкой. @Andreas Oetjen

3. что, если я помещу это в viewWillAppear? будет ли это иметь значение? @Andreas Oetjen

4. Но где именно происходит сбой? В updateUI() или раньше? Что такое стек вызовов?

Ответ №2:

Хорошо, поэтому я решил попробовать обходной путь и полностью отказался от getTheEventDocID() метода, потому что это просто вызывало у меня стресс. Поэтому я решил отказаться от идентификаторов документов, сгенерированных Firebase, и просто использовать 10-значные идентификаторы, сгенерированные из функции, которую я создал. Я также выяснил, как добавить этот точно такой же 10-значный идентификатор в запись Algolia, просто сохранив случайный 10-значный идентификатор в переменной и используя его в обоих местах. Итак, теперь вместо того, чтобы использовать вызов запроса для получения идентификатора документа, сгенерированного Firebase, и каждый раз, когда я нажимаю на результат поиска, мое приложение зависает, я в основном отредактировал Struct запись Algolia и просто добавил eventDocID свойство, с которым можно использовать hits.hitSource(at: indexPath.row).eventDocID .

И теперь точно так же, как я добавил другие поля в vc с помощью segue data transfer, теперь я могу сделать то же самое с моим идентификатором документа, потому что все совпадает :).