ivy_p ivy_p - 2 months ago 11
iOS Question

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

Answer

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
  }

}
Comments