【问题标题】:How do I write a completion handler for firebase data?如何为 firebase 数据编写完成处理程序?
【发布时间】:2017-11-13 09:38:57
【问题描述】:

所以我之前在使用 firebase 的“观察”时遇到了问题,我意识到我无法从异步工作的代码块中获取变量值。一位用户告诉我使用完成处理程序来解决此问题,他的示例是:

func mapRegion(completion: (MKCoordinateRegion)->()) {

 databaseHandle = databaseRef.child("RunList").child(runName).observe(.value, with: { (snapshot) in

    let runData = snapshot.value as? [String: AnyObject]
    self.minLat = runData?["startLat"] as? Double
    self.minLng = runData?["startLong"] as? Double
    self.maxLat = runData?["endLat"] as? Double
    self.maxLng = runData?["endLong"] as? Double
    print("testing")
    print(self.minLat!)
    print(self.maxLng!)

    let region = MKCoordinateRegion(
        center: CLLocationCoordinate2D(latitude: (self.minLat! + self.maxLat!)/2,
                                       longitude: (self.minLng! + self.maxLng!)/2),
        span: MKCoordinateSpan(latitudeDelta: (self.maxLat! - self.minLat!)*1.1,
                               longitudeDelta: (self.maxLng! - self.minLng!)*1.1))
    completion(region)

})
}

并使用代码:

mapRegion() { region in
 mapView.region = region
 // do other things with the region
}

所以我尝试为另一个需要返回对象类型 RunDetail 的数组的方法重新创建它:

func loadRuns(completion: ([RunDetail]) -> ()) {

    // we need name, distance, time and user
    databaseHandle = databaseRef.child("RunList").observe(.value, with: { (snapshot) in

        self.count = Int(snapshot.childrenCount)
        print(self.count!)

        // more stuff happening here to add data into an object called RunDetail from firebase
        // add RunDetail objects into array called 'run'

    })

    completion(runs)

}

我不确定上面的设置是否正确^。

我仍然无法让完成处理程序正常工作(我真的不明白如何设置它)。有人可以帮助我并让我知道我是否正确设置了吗?谢谢。

【问题讨论】:

  • 我修正了我的尝试,忘记在编辑原因中添加它。

标签: ios swift firebase swift3 firebase-realtime-database


【解决方案1】:

您需要将completion(region) 移动到Firebase 完成块内,并在completion: 之后添加@escaping

另外,你不应该强制打开可选选项。很容易检查它们是否不是nil,这样可以防止应用崩溃。

func mapRegion(completion: @escaping (MKCoordinateRegion?) -> Void) {

    let ref = Database.database().reference()

    ref.child("RunList").child(runName).observe(.value, with: { (snapshot) in

        guard

            let runData = snapshot.value as? Dictionary<String,Double>,

            let minLat = runData["startLat"],

            let minLng = runData["startLong"],

            let maxLat = runData["endLat"],

            let maxLng = runData["endLong"]

        else {

            print("Error! - Incomplete Data")

            completion(nil)

            return
        }

        var region = MKCoordinateRegion()

        region.center = CLLocationCoordinate2D(latitude: (minLat + maxLat) / 2, longitude: (minLng + maxLng) / 2)

        region.span = MKCoordinateSpanMake((maxLat - minLat) * 1.1, (maxLng - minLng) * 1.1)

        completion(region)
    })
}

然后将您的代码更新为此。

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    mapRegion { (region) in

        if let region = region {

            self.mapView.setRegion(region, animated: true)
        }
    }
}

为了你的loadRuns

func loadRuns(completion: @escaping (Array<RunDetail>) -> Void) {

    let ref = Database.database().reference()

    ref.child("RunList").observe(.value, with: { (snapshot) in

        var runs = Array<RunDetail>()

        // Populate runs array.

        completion(runs) // This line needs to be inside this closure.
    })
}

【讨论】:

  • 感谢您的回复,但是我了解该示例的工作原理,因为它是另一个用户的答案。在我的问题中,我举了一个我自己尝试返回数组的示例,如果我正确设置完成块,这就是我感到困惑的地方。因此,这个答案对我没有多大帮助..
  • 好吧,这是有道理的,我会以与另一个示例中为“区域”给出的示例相同的方式使用“运行”吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多