Not sure how to append array outside of async call

ios swift

62 观看

1回复

444 作者的声誉

I'm trying to get certain child nodes named City from Firebase using observeSingleEvent but I am having issues trying to pull it into the main thread. I have used a combination of completion handlers and dispatch calls but I am not sure what I am doing wrong, in addition to not being that great in async stuff. In viewDidLoad I'm trying to append my keys from the setupSavedLocations function and return it back to savedLocations I feel like I am close. What am I missing?

Edit: Clarity on question

import UIKit
import Firebase

class SavedLocationsViewController: UIViewController {

    var userID: String?
    var savedLocations: [String] = []

    override func viewDidLoad() {
        super.viewDidLoad()

        setupSavedLocations() { (savedData) in
            DispatchQueue.main.async(execute: {
                self.savedLocations = savedData
                print("inside", self.savedLocations)
            })
        }
        print("outside",savedLocations)
    }

        func setupSavedLocations(completion: @escaping ([String]) -> ()) {
        guard let user = userID else { return }
        let databaseRef = Database.database().reference(fromURL: "https://************/City")
        var dataTest : [String] = []
        databaseRef.observeSingleEvent(of: .value, with: {(snapshot) in
            let childString = "Users/" + user + "/City"
            for child in snapshot.children {
                let snap = child as! DataSnapshot
                let key = snap.key
                dataTest.append(key)
            }
            completion(dataTest)
        })
    }

sample output

outside []
inside ["New York City", "San Francisco"]
作者: LampPost 的来源 发布者: 2017 年 12 月 27 日

回应 1


1

1042 作者的声誉

决定

The call to setupSavedLocations is asynchronous and takes longer to run than it does for the cpu to finish viewDidLoad that is why your data is not being shown. You can also notice from your output that outside is called before inside demonstrating that. The proper way to handle this scenario is to show the user that they need to wait for an IO call to be made and then show them the relevant information when you have it like below.

class SavedLocationsViewController: UIViewController {

    var myActivityIndicator: UIActivityIndicatorView?

    override func viewDidLoad() {
        super.viewDidLoad()

        setupSavedLocations() { (savedData) in
            DispatchQueue.main.async(execute: {
                showSavedLocations(locations: savedData)
            })
        }
        // We don't have any data here yet from the IO call
        // so we show the user an indicator that the call is 
        // being made and they have to wait

        let myActivityIndicator = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.gray)        
        myActivityIndicator.center = view.center        
        myActivityIndicator.startAnimating()
        self.view.addSubview(myActivityIndicator)
        self.myActivityIndicator = myActivityIndicator
    }

    func showSavedLocations(locations: [String]) {
        // This function has now been called and the data is passed in.
        // Indicate to the user that the loading has finished by 
        // removing the activity indicator
        myActivityIndicator?.stopAnimating()
        myActivityIndicator?.removeFromSuperview()

        // Now that we have the data you can do whatever you want with it here
        print("Show updated locations: \(locations)")
    }
作者: Nordeast 发布者: 2017 年 12 月 27 日
32x32