这是显示异步数据的好方法吗?

时间:2017-01-20 21:26:17

标签: swift asynchronous firebase firebase-realtime-database

我是异步编码的新手,我想知道我用来获取和显示数据的方法在swift中是否正确。

此方法从数据库中的用户部分获取对象列表,然后为列表中的每个项目获取图片。我告诉所有图像是否已被提取的方式是在它们到达时将它们推送到数组,然后如果该数组的长度等于对象列表,则重新加载视图。这是我的代码:

var pets = [String]()
var imgs = [UIImage]()

override func viewDidAppear(_ animated: Bool) {
        imgsLoaded = 0
        imgs.removeAll(keepingCapacity: false)  // Clear images array
        pets.removeAll(keepingCapacity: false)  // Clear list of objects

        let userRef = FIRDatabase.database().reference().child("users").child((FIRAuth.auth()?.currentUser?.uid)!).child("pets")

        userRef.observeSingleEvent(of: .value, with: { snapshot in  // fetch list of objects from database
            if (snapshot.value as? Bool) != nil {                   // User has no pets added
                self.loadScrollView()                               // Reload view
            } else if let snap = snapshot.value as? NSDictionary {  // User has pets
                for value in snap {
                    self.pets.append(value.key as! String)          // Append object to list of objects
                }
                for i in 0..<self.pets.count {                      // For each item in the list, fetch its corresponding image
                    let imgRef = FIRStorage.storage().reference().child("profile_images").child(self.pets[i]+".png")
                    imgRef.data(withMaxSize: 15 * 1024 * 1024) { (data, error) -> Void in
                        if error != nil {
                            print(error)
                        }

                        // Create a UIImage, add it to the array
                        self.imgs.append(UIImage(data: data!)!)      // Push image to list of images
                        self.imgsLoaded+=1
                        if self.imgsLoaded == self.pets.count {      // If same number of images loaded, reload the view
                            self.loadScrollView()
                        }
                    }
                }
            }
        })
    }

正如我所说,我是异步编码的新手,并希望听到我正在尝试的正确方法。我的方法的一个问题是图像阵列可能与数据阵列不对齐,因为图像可以不按顺序取出。我很乐意学习最好的方法,所以请告诉我!

编辑:确保我的数据与相应图像对齐的方法是设置一个字典,其中键是数据,值是我猜的图像。

1 个答案:

答案 0 :(得分:2)

我建议使用DispatchGroup而不是保留2个数组中项目数的计数。这还可以跟踪组中enter()leave()注册的每个块的多个线程的进度。然后,一旦所有块都进入并离开,则调用notify块,这可以刷新UI。还使用带有占位符图像的字典,以防其中一个图像的加载失败。会比忽视失败案更好。

这也可能更容易阅读和推理,因为没有跟踪2个阵列的数量。 enter()leave()

的意图更加明确
var imgs = [String: UIImage]()
var dispatchGroup = DispatchGroup()

 override func viewDidAppear(_ animated: Bool) {

    let userRef = FIRDatabase.database().reference().child("users").child((FIRAuth.auth()?.currentUser?.uid)!).child("pets")

    userRef.observeSingleEvent(of: .value, with: { [weak self] snapshot in  // fetch list of objects from database
        if (snapshot.value as? Bool) != nil {                   // User has no pets added
            self?.loadScrollView()                               // Reload view
        } else if let snap = snapshot.value as? NSDictionary {  // User has pets
            for value in snap {
                self?.dispatchGroup.enter()
                let imgRef = FIRStorage.storage().reference().child("profile_images").child(self!.pets[i]+".png")
                imgRef.data(withMaxSize: 15 * 1024 * 1024) { (data, error) -> Void in
                    if let error = error {
                        print(error) // handle the error here but still call .leave() on the group to be sure the notify block is called.
                        imgs[value] = somePlaceHolderImage
                        self?.dispatchGroup.leave()
                    } else if let data = data, let image = UIImage(data: data) {
                        imgs[value] = image
                        self?.dispatchGroup.leave()
                    }

                    dispatchGroup.notify(queue: DispatchQueue.main, execute: {
                        self?.loadScrollView()
                    })
                }
            }
        }
    })
}