Search code examples
iosswiftuitableviewtransfer

Swift Transferred Data between View Controllers stays empty


I am new to Swift. I am trying to pass data between two View Controllers. The value I pass through from the first view controller is nil when used by the second view controller. I do not understand why this is happening.

This is my tableView function call in the first view controller. It gets the selected table cell's user information.

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

    let user = User()


    if(messages.count > 0) {
        let message = self.messages[indexPath.row]

        guard let chatPartnerID = message.chatPartnerID() else {
            return
        }


        let ref = FIRDatabase.database().reference().child("users").child(chatPartnerID)
        ref.observeSingleEventOfType(.Value, withBlock: { (snapshot) in

            guard let dictionary = snapshot.value as? [String: AnyObject]
                else {
                    return
            }

            user.id = chatPartnerID
            user.setValuesForKeysWithDictionary(dictionary)
            self.setupChatLogControllerForUser(user)

            }, withCancelBlock: nil)
    }else {
        let user = users[indexPath.row]
        self.setupChatLogControllerForUser(user)
    }


}

User in an NSObject which I declare like so:

class User: NSObject {

  var id: String?
  var name: String?
  var email: String?
  var profileImageUrl: String?
}

The setupChatLogControllerForUser(user) method in the first view controller takes in the user parameter that is given in the tableView method and passed onto the second view controller (ChatLogController) like this:

 func setupChatLogControllerForUser(user: User) {
    let chatLogController = ChatLogController()
    chatLogController.selectedUserID = user.id!
    chatLogController.user = user
}

My second View Controller method receives the data with this:

var selectedUserID = String()
var user = User()

The second view controller also has a handleSend method which is linked to a button on my storyboard through IBAction.

func handleSend() {

    let ref = FIRDatabase.database().reference().child("messages")
    let childRef = ref.childByAutoId()
    let toID = self.selectedUserID
    let fromID = FIRAuth.auth()!.currentUser!.uid
    let timestamp: NSNumber = Int(NSDate().timeIntervalSince1970)

    let values = ["text": messageInputField.text!, "toID": toID, "fromID": fromID, "timestamp": timestamp]
    childRef.updateChildValues(values) { (error, ref) in

        if error != nil {
            print(error)
            return
        }

        let userMessagesRef = FIRDatabase.database().reference().child("userMessages").child(fromID)

        let messageID = childRef.key
        userMessagesRef.updateChildValues([messageID: 1])

        let recipientUserMessagesRef = FIRDatabase.database().reference().child("userMessages").child(toID)
        recipientUserMessagesRef.updateChildValues([messageID: 1])

    }

}

This method is where the problem is occurring. I set toID equal to the selectedUserID. In the debugger, I can see the selectedUserID populated with a string when the view controller segues to the ChatLogController. Why is the value of selectedUserID variable nil when the method is called?

I understand that passing in the selectedUserID and the User object is redundant, I just wanted to see if the problem was something with the String variable being passed between the classes, but it stays nil.

Any help would be awesome!

EDIT: I am using a segue to switch between my view controllers


Solution

  • You don't need to instantiate ChatLogController for passing data because if you set up the NavigationController properly, then he is going to instantiate for you.

    func setupChatLogControllerForUser(user: User) {
        let chatLogController = ChatLogController()
        chatLogController.selectedUserID = user.id!
        chatLogController.user = user
    }
    

    So if you want to pass data to ChatLogController, you should use prepareForSegue method for this purpose.


    I am going to provide a basic example how you can mimic this type of functionality:

    This is the result:

    enter image description here


    This is my storyboard:

    Note that the name of the segue is showDetail as you can see in image.

    enter image description here


    This is the code of my TableViewController:

    When I touch a cell on the TableViewController, the didSelectRowAtIndexPath method is called, then I access the cell selected and set the value to dataToSend variable, finally I call performSegueWithIdentifier in order to navigate to the ViewController.

    But right before it goes to ViewController, prepareForSegue method is called so you have the opportunity here to pass any variable that ViewController expects, in this case I am passing dataToSend to ViewController's dataSent variable.

    class TableViewController: UITableViewController {
    
      var dataToSend = ""
    
      override func viewDidLoad() {
        super.viewDidLoad()
      }
    
      override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        let cell = tableView.cellForRowAtIndexPath(indexPath)
    
        dataToSend = cell!.textLabel!.text!
    
        performSegueWithIdentifier("showDetail", sender: nil)
      }
    
      override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if segue.identifier == "showDetail" {
          if let viewController = segue.destinationViewController as? ViewController {
            viewController.dataSent = dataToSend
          }
        }
      }
    
    }
    

    This the code for ViewController:

    for then I can show the value in a label.

    class ViewController: UIViewController {
      @IBOutlet weak var label: UILabel!
    
      var dataSent = String()
    
      override func viewDidLoad() {
        super.viewDidLoad()
    
        label.text = dataSent
      }
    
    }