Получилось!!! Использовал, мне кажется, все что вы советовали, плюс немного в сети накопал.
@voragomod, @ODiN спасибо за поддержку. Я уже, грешным делом, задумывался к БД менять.
Модель Belt выглядит вот так:
struct Belt {
var title: String
var baseTechnique: [BaseTechnique]?
var tuls: [Tul]?
}
Все разворачивается в одном классе. Потом, наверное, нужно будет отдельный файл создавать для загрузки
import UIKit
import Firebase
class BeltViewController: UIViewController {
// Получаю данные для этого массива из другого контроллера
var beltsArrString = [String]()
// В этом массив соберутся нужные мне модели, которые я буду отображать на экране.
var beltsArrObj = [Belt]()
// массив с флагами. Нужен для того, чтобы добавить модель в массив beltsArrObj, когда все данные загружены
var beltDownloadComplete = [false, false]
//Создаю модель Belt в которой постепенно сохраняю данные, когда они подгружаются, она на самом деле больше, урезал для примера
var belt = Belt(title: "Title", baseTechnique: nil, tuls: nil)
let db = Firestore.firestore()
@IBOutlet weak var beltDescription: UILabel!
@IBOutlet weak var imageOutlet: UIImageView!
@IBOutlet weak var segmentedControl: UISegmentedControl!
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
segmentedControl.replaceSegments(segments: beltsArrString)
segmentedControl.isEnabled = false
self.title = "adfvadfvavd"
activityIndicator.startAnimating()
// Беру данные для первого пояса. Раньше это делал в цикле, но с ним много сложностей. Поэтому решил брать их по одному
getBelt(beltsArrString.removeFirst())
}
// в этой фунции делаю запросы к базе по каждому свойству модели Belt
func getBelt(_ beltString: String) {
db.collection("belts").document(beltString).getDocument { [weak self] (belt, error) in
guard error == nil else {
print(error!.localizedDescription)
return
}
if belt != nil && belt!.exists {
let beltData = belt!.data()
self!.getBaseTechnique(baseTechniqueArray: (beltData!["baseTechnique"] as! Array<DocumentReference>))
self!.getTul(tulArray: (beltData!["tuls"] as! Array<DocumentReference>))
}
}
}
// Беру данные для свойства baseTechnique
func getBaseTechnique(baseTechniqueArray: Array<DocumentReference>?) {
guard let baseTechniqueArr = baseTechniqueArray else { return }
var result = [BaseTechnique]()
let count = baseTechniqueArr.count
baseTechniqueArr.forEach { (element) in
// вызываю функцию createObject, которая обращается к базе и в замыкании создаю объект
createObject(element: element) { (dict, error) in
let baseTechniqueObj = BaseTechnique(title: (dict!["title"]! as! String),
description: (dict!["description"]! as! String),
image: UIImage(named: dict!["image"]! as! String)!)
result.append(baseTechniqueObj)
// Если количество созданныъ элементов равно исходному
if count == result.count {
// присваиваю массив свойству baseTechnique
self.belt.baseTechnique = result
// Устанавливаю для одного из флагов значение true. C этим массивом поработаем в loadBeltToArr
self.beltDownloadComplete[0] = true
self.loadBeltToArr()
}
}
}
}
// Для взятия getTul принцип работы тот же что и для getBaseTechnique, расписывать не буду
func getTul(tulArray: Array<DocumentReference>?) {
guard let tulArr = tulArray else { return }
var result = [Tul]()
let count = tulArr.count
tulArr.forEach { (element) in
createObject(element: element) { (dict, error) in
let tulObj = Tul(title: (dict!["title"]! as! String),
description: (dict!["description"]! as! String),
image: UIImage(named: dict!["image"]! as! String)!)
result.append(tulObj)
if count == result.count {
self.belt.tuls = result
self.beltDownloadComplete[1] = true
self.loadBeltToArr()
}
}
}
}
// Создаю объект передаю сюда только ожин элемент с ним и работаю. Все запросы обрабатываюся последовательно благодаря completion: @escaping([String: Any]?, Error?)->Void)
// Важно. Так как я точно знаю на тестовых данных что мне придет, я не делаю проверки на ошибки. Но она тут обязательно нужно и в getTul, getBaseTechnique тоже.
func createObject(element: DocumentReference, completion: @escaping([String: Any]?, Error?)->Void) {
element.getDocument { (snapshot, error) in
if let dict = snapshot?.data() {
completion(dict, error)
}
}
}
// И в getTul, getBaseTechnique, я вызываю loadBeltToArr, она срабатывает, только если две функции отработали, благодаря проверке !beltDownloadComplete.contains(false)
func loadBeltToArr() {
if !beltDownloadComplete.contains(false) {
beltsArrObj.append(belt)
if !beltsArrString.isEmpty {
beltDownloadComplete = [false, false]
// если переданный в контроллер массив не пустой, беру следующий пояс.
getBelt(beltsArrString.removeFirst())
} else {
// Когда все закончено отображаю данные.
activityIndicator.stopAnimating()
activityIndicator.isHidden = true
self.segmentedControl.isEnabled = true
self.segmentedControl.selectedSegmentIndex = 0
self.choiceSegment(segmentedControl)
}
}
} ... продолжение класса, к теме не относится.