cosmos cosmos - 2 months ago 16
Swift Question

Firebase - Save unique ID in a class variable and send its data to another controller - Swift

I created a unique ID with firebase using childByAutoID. I am able to print this ID from within the reference function used to create it, but when I assign the value to a class variable and print it in the console it comes out nil.

I am trying to send this data to another view controller using the override func prepareForSegue method. It works when the data is a string but it does not work when it is the variable holding the uniqueIDKey.

Here is the code:

class MainViewController: UIViewController, CLLocationManagerDelegate{

var ref: FIRDatabaseReference!
var refHandle: UInt!
let locationManager = CLLocationManager()
let regionRadius: CLLocationDistance = 1000
var currentLocation: CLLocation!
var location: CLLocationCoordinate2D!
var latitude: Double!
var longitude: Double!
let geoCoder = CLGeocoder()
var placemark: CLPlacemark?
let date = NSDate()
var currentOrderIDKey = ""



@IBOutlet weak var mapView: MKMapView!
@IBOutlet weak var userEmailLabel: UILabel!
@IBOutlet weak var pickUpAddress: UITextField!
@IBOutlet weak var deliveryAddress: UITextField!

override func viewDidLoad() {
ref = FIRDatabase.database().reference()



locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
locationManager.startUpdatingLocation()
locationManager.desiredAccuracy = kCLLocationAccuracyBest




refHandle = ref.observeEventType(FIRDataEventType.Value, withBlock: { (snapshot) in
let dataDict = snapshot.value as! [String: AnyObject]

print((dataDict))
})
let userID: String = FIRAuth.auth()!.currentUser!.uid
ref.child("users").child(userID).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
let userEmail = snapshot.value!["email"] as! String
self.userEmailLabel.text = userEmail
})


super.viewDidLoad()
print("\(currentLocation)")
}

func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("didFailWithError \(error)")

if error.code == CLError.LocationUnknown.rawValue {
return
}
}

func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

currentLocation = locations.last!
latitude = currentLocation.coordinate.latitude
longitude = currentLocation.coordinate.longitude
location = CLLocationCoordinate2DMake(latitude, longitude)

print("didUpdateLocations \(currentLocation.coordinate)")

if currentLocation.timestamp.timeIntervalSinceNow < -10 {
return
}
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location, regionRadius * 2.0, regionRadius * 2.0)
mapView.setRegion(coordinateRegion, animated: false)
}





@IBAction func requestPickUpButton(sender: AnyObject) {
ref = FIRDatabase.database().reference()
let userID: String = FIRAuth.auth()!.currentUser!.uid
ref.child("users").child(userID).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
let orderRef = self.ref.child("users").child(userID).child("orders")
let origin = self.pickUpAddress.text!
let destination = self.deliveryAddress.text!
let orderID = orderRef.childByAutoId()
let formatter = NSDateFormatter();
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ";
let defaultTimeZoneStr = formatter.stringFromDate(self.date)
let order = ["date": defaultTimeZoneStr, "origin": origin, "destination": destination]
orderID.setValue(order)
self.currentOrderIDKey = orderID.key as String
print(self.currentOrderIDKey) ///This works!
self.performSegueWithIdentifier("ServiceConfirmation", sender: self)
self.locationManager.stopUpdatingLocation()
})

print(currentOrderIDKey) //this doesnt
}

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ServiceConfirmation" {
let destinationConroller = segue.destinationViewController as! UINavigationController
let targetController = destinationConroller.topViewController as! ServiceConfirmationViewController
targetController.currentOrderIDKey = "this works"
//this does not : targetController.currentOrderIDKey = currentOrderIDKey
}
}


@IBAction func signOutButton(sender: AnyObject) {
try! FIRAuth.auth()!.signOut()
if let storyboard = self.storyboard {
let viewController = storyboard.instantiateViewControllerWithIdentifier("LoginViewController")
self.presentViewController(viewController, animated: false, completion: nil)
}
}
}


As a bonus question, I get this warning in the console every time I run the app:

<UILayoutContainerView: ...; frame = (0 0; 414 736); autoresize = W+H; gestureRecognizers = <NSArray: ....>; layer = <CALayer: ....>>'s window is not equal to <UINavigationController: ....>'s view's window!


Thanks in advance!

Answer

This is another classic case of Asynchrounous calls, you are accessing the value of currentOrderIDKey in print(currentOrderIDKey) even before it has been assigned.

Your print(currentOrderIDKey) line gets called even before

self.currentOrderIDKey = orderID.key as String
    print(self.currentOrderIDKey) ///This works!

gets called.

ref.child("users").child(userID).observeSingleEventOfType(.Value,.. sends a Asynchronous call to your backend which takes some time to retrieve the data, but even before you data could be retrieved your print(currentOrderIDKey) gets called resulting in null

Instead of segueing through performSegue.... use instantiation:-

let secondScene = self.navigationController?.storyboard?.instantiateViewControllerWithIdentifier("ServiceConfirmationViewControllerVC_ID") as! ServiceConfirmationViewController

  secondScene.valueTranfered = self. currentOrderIDKey
  self.navigationController?.pushViewController(secondScene, animated: true)

Where ServiceConfirmationViewControllerVC_ID is your secondScene storyBoardID

Might i suggest reading this:- Wikipedia : Asynchronous calls & http://stackoverflow.com/a/748189/6297658