Swift 使用完成处理程序后数组显示为空

Swift array showing up empty after using completion handler

我正在尝试从 Firebase 数据库中获取服务列表。这是我的参考:

let REF_SERVICES = DB_REF.child("services")

这是我的获取函数:

func fetchServices(completion: @escaping([Service]) -> Void) {
    var services = [Service]()
    REF_SERVICES.observe(.value) { (snapshot) in
        for child in snapshot.children.allObjects as! [DataSnapshot] {
            guard let dictionary = child.value as? [String : Any] else { return }
            let title = child.key
            let service = Service(title: title, dictionary: dictionary)
            services.append(service)
        }
    }
    completion(services)
}

这是 Service 结构:

enum ServiceStatus: Int {
    case available
    case busy
}

struct Service {
    let title: String
    var details: String?
    var location: CLLocation?
    var status: ServiceStatus!

    init(title: String, dictionary: [String: Any]) {
        self.title = title
        if let details = dictionary["details"] as? String {
            self.details = details
        }
        if let index = dictionary["status"] as? Int {
            self.status = ServiceStatus(rawValue: index)
        }
    }
}

我不太了解完成处理程序和异步调用的工作原理,所以我不知道如何让它工作。

这是我在 ViewController 中调用此函数的地方:

private var services: [Service]? {
    didSet {
        let addServiceController = AddServiceController()
        addServiceController.services = self.services!
    }
}

func fetchServices() {
    Services.shared.fetchServices { (services) in
        self.services = services
    }
}

在两个 ViewController 中,数组显示为空。我通过使用 print 语句验证它是否正在获取数据来使用调试的圣杯。

谢谢。

您需要将 completion(services) 调用移动到 observe 方法的完成回调中。

func fetchServices(completion: @escaping([Service]) -> Void) {
    var services = [Service]()
    REF_SERVICES.observe(.value) { (snapshot) in
        for child in snapshot.children.allObjects as! [DataSnapshot] {
            guard let dictionary = child.value as? [String : Any] else { return }
            let title = child.key
            let service = Service(title: title, dictionary: dictionary)
            services.append(service)
        }
        completion(services) //<-- at this point services will contain the info you appended in for loop
    }
}

和上面arturdev说的一样,需要把completion放在observe方法里面。 Firebase 调用是异步代码,这基本上意味着代码尽可能快地执行,而不是从上到下。因此,在您的原始函数中,完成是在执行 REF_SERVICES 之后立即调用的,而不是在 REF_SERVICES 函数中的代码之后。

我相信你还需要包括一个循环计数器,否则完成只会在 1 个循环后执行(同样,async 会尽快执行)。

最后一个提示是尽量避免在代码中使用 "as!",而是使用 if let 语句。如果出于某种原因,您的数据库出现问题并且无法将数据转换为 [DataSnapshot],您的应用程序将会崩溃。

下面的代码应该可以工作,但我还没有测试过。祝你好运!

func fetchServices(completion: @escaping (_ [Service]?) -> ()) {
    REF_SERVICES.observe(.value) { (snapshot) in
        if let data = snapshot.children.allObjects as? [DataSnapshot] {
            var services = [Service]()
            var counter = 0
            for child in data {
                let title = child.key
                if let dictionary = child.value as? [String : Any] {
                    let service = Service(title: title, dictionary: dictionary)
                    services.append(service)
                    counter = counter + 1
                    if counter == data.count {
                        completion(services)
                    }
                } else {
                    print("There was an error casting dictionary for \(title)")
                    counter = counter + 1
                    if counter == data.count {
                        completion(services)
                    }
                }
            }
        } else {
            print("Could not cast data as [DataSnapshot]")
            return nil
        }
    }
}