Быстрое получение ошибки выхода индекса за пределы диапазона при чтении данных из Firestore

#swift #uitableview #google-cloud-firestore #swift5

# #swift #UITableView #google-облако-firestore #swift5

Вопрос:

Я пытаюсь извлечь данные изображения из Firestore и обновить пользовательский интерфейс, добавив изображения в ячейки TableView.

Тем не менее, я получаю ошибку выхода индекса за пределы диапазона. Я считаю, что это из-за проблемы с синхронизацией.

Можете ли вы, пожалуйста, помочь мне это исправить?

Заранее спасибо.

Неустранимая ошибка: индекс вне диапазона: файл Swift / ContiguousArrayBuffer.swift, строка 444

     override func viewDidLoad() {
    super.viewDidLoad()
    
    createBubbleView()
    getDataFromFireStore()
    
    detailsTableView.delegate = self
    detailsTableView.dataSource = self

    // MARK: - NIB REGISTRATION
    detailsTableView.register(UINib(nibName: K.ingredientsNibName, bundle: nil), forCellReuseIdentifier: K.ingredientsCell)
    detailsTableView.register(UINib(nibName: K.stepsNibName, bundle: nil), forCellReuseIdentifier: K.stepsCell)
    detailsTableView.register(UINib(nibName: K.recipeNameNib, bundle: nil), forCellReuseIdentifier: K.recipeNameCell) }

    

  func getDataFromFireStore() {
    
    let db = Firestore.firestore()
for ingredient in ingredients {
        db.collection("Ingredients").whereField("name", isEqualTo: ingredient)
            .getDocuments() { [self] (querySnapshot, err) in
                if let err = err {
                    
                    print("Error getting documents: (err)")
                } else {
                    for document in querySnapshot!.documents {
                        print(document.get("image")!)
                        if let imageURLsDB = document.get("image") as? [String] {

                            ingredientsImageURLs.append(document.get("image") as! String)
                            
                        }
                        
                        
                    }
            }
            }
    }
    

 // MARK: - RETURNS THE VALUE OF EACH CELL IN THE TABLEVIEW
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {


    let cell = detailsTableView.dequeueReusableCell(withIdentifier: K.ingredientsCell)
    switch indexPath.section {
    //        RECIPE NAME
    case 0:
        let cell = detailsTableView.dequeueReusableCell(withIdentifier: K.recipeNameCell) as! RecipeTitleCell
        cell.cookingTimeLabel.text = cookingTime
        cell.numberOfIngredientsLabel.text = String(ingredients.count)
        
        return cell
        
    //        INGREDIENTS
    case 1:
        let cell = detailsTableView.dequeueReusableCell(withIdentifier: K.ingredientsCell) as! IngredientsCell
        
        cell.ingredientName.text = ingredients[indexPath.row]
        
        cell.textLabel?.font = UIFont(name:"Mulish-regular", size:12)
        cell.textLabel?.numberOfLines = 0
        cell.textLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping
        
        DispatchQueue.main.async {
            cell.ingredientImage.sd_setImage(with: URL(string: self.ingredientsImageURLs[indexPath.row]), placeholderImage: UIImage(systemName: "square.and.arrow.up"))
            self.detailsTableView.reloadData()
            
        }
        
        
        return cell
 

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

1. В какой строке вы получаете ошибку? Как выглядят методы подсчета строк и разделов и как ingredients ingredientsImageURLs синхронизируются массивы and?

2. @JoakimDanielson, я получаю сообщение об ошибке при попытке отобразить изображения в ячейке ingredientImage. Вы можете увидеть строку ниже; cell.ingredientImage.sd_setImage(с: URL(строка: self.ingredientsImageURLs[indexPath.row]), placeholderImage: UIImage(имясисТемы: «square.and.arrow.up»))

3. Тогда, возможно, вам также следует ответить на другие мои вопросы.

4. @JoakimDanielson, извините. Пожалуйста, посмотрите скриншот здесь — drive.google.com/file/d/1q3md__tZ7tONUsH_e_H6G63SRFUXZQjw /… ingredients и ingredientsImageURLs содержат одинаковое количество элементов в firestore. Однако при отладке я вижу 0 элементов в ingredientsImageURLs

5. Я предполагаю, что код, в котором происходит сбой, вызывается до того, как некоторые (или все) элементы в ingredientsImageURLs были загружены в функцию getDataFromFireStore . Вероятно, лучший способ избежать этого — использовать один массив вместо двух.

Ответ №1:

Спасибо, ребята, за информацию. Вот как я это исправил.

  1. Установите изображения в ячейки после получения нужного количества данных
     if ingredientsImageURLs.count ==  ingredients.count {
            cell.ingredientImage.sd_setImage(with: URL(string: self.ingredientsImageURLs[indexPath.row]), placeholderImage: UIImage(systemName: "square.and.arrow.up"))
        }
     
  2. Перезагрузите данные после загрузки всех изображений.

    Функция getDataFromFireStore() {

     let db = Firestore.firestore()
    
    let recipeDocRef = db.collection("Recipes").document(selectedRecipeID).addSnapshotListener { [self] (snapshot, error) in
    
        if let titleDB = snapshot?.get("title") as? String {
            self.recipeTitle = titleDB
            titleLabel.text = recipeTitle
        }
    
        if let ingredientsDB = snapshot?.get("ingredients") as? [String] {
            self.ingredients = ingredientsDB
        }
    
        if let cookingTimeDB = snapshot?.get("cookingTime") as? String {
            self.cookingTime = cookingTimeDB
        }
    
        if let imageUrlDB = snapshot?.get("imageUrl") as? String {
            self.imageUrl = imageUrlDB
            recipeImageView.sd_setImage(with: URL(string: imageUrl), placeholderImage: UIImage(systemName: "square.and.arrow.up"))
    
        }
    
        if let cookingStepsDB = snapshot?.get("cookingSteps") as? [String] {
            self.steps = cookingStepsDB
        }
    
        if let stepImagesDB = snapshot?.get("stepImages")  as? [String]{
            self.stepsImages = stepImagesDB
        }
    
        for ingredient in self.ingredients {
            db.collection("Ingredients").whereField("name", isEqualTo: ingredient)
                .getDocuments()
                { [self] (querySnapshot, err) in
                    if let err = err {
    
                        print("Error getting documents: (err)")
                    } else {
                        for document in querySnapshot!.documents {
                            ingredientsImageURLs.append(document.get("image") as! String)
                        }
                        self.detailsTableView.reloadData()
    
                    }
                }
            self.detailsTableView.reloadData()
        }
    
    }
     

    }

Ответ №2:

 if let imageURLsDB = document.get("image") as? [String] {

   ingredientsImageURLs.append(document.get("image") as! String)
                        
}
 

Вы проверяете document.get(«image») как строковый массив, но при добавлении в свой массив вы проверяете как строку.

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

1. Обновил его, но, к сожалению, это не решило проблему.

2. Когда вы добавляете данные в ингредиенты?

3. Это уже в БД. Он просто извлекает эти данные.

4. Пожалуйста, можете ли вы поделиться со мной всем кодом ViewController? Вы можете добавить на диск.

Ответ №3:

Вы вызываете серверную часть для изображения, а затем повторно загружаете данные, поэтому он вызывает каждый раз, устанавливая изображение без остановки. Вы не можете вызвать reload data внутри cellForRow, потому что это приведет к повторному вызову делегатов табличного представления.

 private func getImages()
        DispatchQueue.main.async {
        cell.ingredientImage.sd_setImage(with: URL(string:self.ingredientsImageURLs[indexPath.row]), placeholderImage: UIImage(systemName: "square.and.arrow.up"))
        self.imageArray = imagesFromFireStore
        self.detailsTableView.reloadData()
    }
 

// вы можете поместить свой код здесь

 if let imageURLsDB = document.get("image") as? [String] {
                        ingredientsImageURLs.append(document.get("image") as! String)
      //       here just append images to array and call
      self.reloadData()
  }
 

В табличном представлении используйте свой imageArray[indexPath.row]

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

1. Вы правы. Я обновил этот раздел. Однако TableView cellForRow все еще выполняется перед извлечением данных из Firestore. Как я могу заполнить изображения после того, как у меня будут готовы данные из Firestore?

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