Как получить массив array?

коллеги! Я начинающий разработчик, поэтому прошу с пониманием относиться к моему, возможно, банальному вопросу. Так же прошу отнестись и к формату вопроса - я сейчас пишу первичный в жизни этот вопрос и в целом. Посмотрите, что у меня есть: 1. Существующий класс «Продукт»:

import Foundation
import UIKit

protocol arrayProductsProtocol {
init? (productDict: [String: Any])
}

struct Product {
init(store: String?,
     productPostArticle: String?,
     productPostTitle: String?,
     productPostDescription: String?,
     productPostImageCount: Int?,
     productPostPrice: Double?,
     productPostDiscont: Double?,
     productPostFinalPrice: Double?,
     productPostSex: String?,
     productPostSeason: String?,
     productPostPublicationDate: Date,
     productPostLikesCount: Int?,
     productPostIsLiked: Bool?,
     productPostViewsCount: Int?,
     productPostPhotoCount: Int?,
     productPostIsNew: String?,
     productPostArrayPhotos: [URL]?) {
    self.store = store ?? "Название магазина"
    self.productPostArticle = productPostArticle ?? "0000000000"

    self.productPostTitle = productPostTitle ?? "Название продукта"
    self.productPostDescription = productPostDescription ?? "Описание продукта"
    self.productPostImageCount = productPostImageCount ?? 0
    self.productPostPrice = productPostPrice ?? 0
    self.productPostDiscont = productPostDiscont ?? 0
    self.productPostFinalPrice = productPostFinalPrice ?? 0
    self.productPostSex = productPostSex ?? Product.Sex.unisex.rawValue
    self.productPostSeason = productPostSeason ?? Product.Season.autumn.rawValue
    self.productPostPublicationDate = productPostPublicationDate
    self.productPostLikesCount = productPostLikesCount ?? 0
    self.productPostIsLiked = productPostIsLiked ?? false
    self.productPostViewsCount = productPostViewsCount ?? 0
    self.productPostPhotoCount = productPostPhotoCount ?? 0
    self.productPostIsNew = productPostIsNew ?? Product.New.normal.rawValue
    self.productPostArrayPhotos = productPostArrayPhotos ?? []
}


var productDict: [String: Any] {
    return [
        "productPostArticle" : productPostArticle,

        "productPostTitle" : productPostTitle,
        "productPostDescription" : productPostDescription,
        "productPostPrice" : productPostPrice,
        "productPostDiscont" : productPostDiscont,
        "productPostFinalPrice" : productPostFinalPrice,
        "productPostSex" : productPostSex,
        "productPostSeason" : productPostSeason,
        "productPostPublicationDate" : productPostPublicationDate,
        "productPostLikesCount" : productPostLikesCount,
        "productPostIsLiked" : productPostIsLiked,
        "productPostViewsCount" : productPostViewsCount,
        "productPostIsNew" : productPostIsNew,
        "productPostImageCount" : productPostImageCount,
        "store" : store,
        "productPostPhotoCount" : productPostPhotoCount
    ]
}

//магазин
var store: String = ""

//артукул
var productPostArticle: String

//описание товара
var productPostArrayPhotos: [URL]?

var productPostTitle: String
var productPostDescription: String
var productPostImageCount: Int

//стоимость товара
var productPostPrice: Double
var productPostDiscont: Double
var productPostFinalPrice: Double

//параметры товара
var productPostSex: String
var productPostSeason: String

//параметры поста
var productPostPublicationDate: Date
var productPostLikesCount: Int
var productPostIsLiked: Bool
var productPostViewsCount: Int
var productPostPhotoCount: Int

//новизна товара
var productPostIsNew: String

//параментры товара
enum Sex: String {
    case man = "Для мужчин"
    case woman = "Для женщин"
    case unisex = "Унисекс"
}

enum Season: String{
    case winter = "Зима"
    case spring = "Весна"
    case summer = "Лето"
    case autumn = "Осень"
}

enum New: String {
    case isNew = "Новинка"
    case normal = ""
    case sale = "Скидки"
}

extension Product: arrayProductsProtocol {
init? (productDict: [String : Any]) {
    guard let productPostArticle = productDict["productPostArticle"] as? String,

          let productPostTitle = productDict["productPostTitle"] as? String,
          let productPostDescription = productDict["productPostDescription"] as? String,
          let productPostPrice = productDict["productPostPrice"] as? Double,
          let productPostDiscont = productDict["productPostDiscont"] as? Double,
          let productPostFinalPrice = productDict["productPostFinalPrice"] as? Double,
          let productPostSex = productDict["productPostSex"] as? String,
          let productPostSeason = productDict["productPostSeason"] as? String,
          let productPostPublicationDate = productDict["productPostPublicationDate"] as? Date,
          let productPostLikesCount = productDict["productPostLikesCount"] as? Int,
          let productPostIsLiked = productDict["productPostIsLiked"] as? Bool,
          let productPostViewsCount = productDict["productPostViewsCount"] as? Int,
          let store = productDict["store"] as? String,
          let productPostIsNew = productDict["productPostIsNew"] as? String,
          let productPostImageCount = productDict["productPostImageCount"] as? Int,
          let productPostPhotoCount = productDict["productPostPhotoCount"] as? Int,
          let productPostArrayPhotos = productDict["productPostArrayPhotos"] as? [URL]
    else { return nil }
    
    self.init(
        store: store,
        productPostArticle: productPostArticle,

        productPostTitle: productPostTitle,
        productPostDescription: productPostDescription,
        productPostImageCount: productPostImageCount,
        productPostPrice: productPostPrice,
        productPostDiscont: productPostDiscont,
        productPostFinalPrice: productPostFinalPrice,
        productPostSex: productPostSex,
        productPostSeason: productPostSeason,
        productPostPublicationDate: productPostPublicationDate,
        productPostLikesCount: productPostLikesCount,
        productPostIsLiked: productPostIsLiked,
        productPostViewsCount: productPostViewsCount,
        productPostPhotoCount: productPostPhotoCount,
        productPostIsNew: productPostIsNew,
        productPostArrayPhotos: productPostArrayPhotos
    )
}
}

Вышеописанным способом я пытаюсь создавать Продукты.

Далее у меня есть файл с функциями для Firebase. Вот он:

import Foundation
import Firebase

struct FBDataBase {


static func creatDB(completion: @escaping (([Product]) -> ())) {
    // переходим к списку продуктов (1, 2, 3, 4, 5, ...)
    
    var array: [Product] = []
    let ref = Firestore.firestore().collection("stores").document((Auth.auth().currentUser?.email)!).collection("products")
    ref.addSnapshotListener { (products, error) in
        array = (products?.documents.compactMap({Product(productDict: $0.data())}))!
    }
    completion(array)
    
}
}

Затем я вызываю это из ViewController'a следующим образом:

override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    // #warning Incomplete implementation, return the number of items
    FBDataBase.creatDB(completion: { (array) in
                         self.prodArray = array
                         print("self.prodArray: \(self.prodArray)")
                         print("array: \(array)")
    })
    print("prodArray count: \(self.prodArray.count)")
    return self.prodArray.count
}

Проблема в том, что у меня в struct FBDataBase array всегда равен нулю. Я, признаюсь, пробовал различные методы уже неделю как, но ничего не получилось в итоге. Прошу помощи, коллеги. Очень благодарю за помощь, ибо знаний не хватает, а мечта - свой проект - стоит на месте и мотивация падает. Спасибо.

P.S.: прошу прощения за форматирование поста, но не знаю пока как это делать корректно. Вроде ввожу код через соответствующую кнопку и появляющийся блок, но в предпросмотре поста отображается все "криво".

Вот фото моей базы данных: первая страница, далее раскрываю "products" "до дна", так сказать


Ответы (1 шт):

Автор решения: schmidt9

Попробуйте разобраться с асинхронными коллбэками, у вас данные судя по всему приходят позже, чем Вы их используете.

    ref.addSnapshotListener { (products, error) in
        array = (products?.documents.compactMap({Product(productDict: $0.data())}))!
        completion(array)
    }

Подобным же образом вам нужно поступить при выводе данных во вью контроллере - сначала загрузить данные, а потом уже выводить в коллекшен вью

    FBDataBase.creatDB(completion: { (array) in
                         self.prodArray = array
                         print("self.prodArray: \(self.prodArray)")
                         print("array: \(array)")
                         self.collectionView.reloadData()
    })

При reloadData как раз будут вызывается уже методы UICollectionView с подготовленными данными

override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    self.prodArray.count
}
→ Ссылка