jmur216
jmur216

Reputation: 11

Passing Arrays in Swift

I'm working on an app and I need to send data in an array from one view controller to another. I'm pulling a String from a label into a variable and appending it into the array.

var time:String = timeLabel.text!
timeArray.append(time)
print("add data")

I then have a prepareForSegue function where I want to pass data from firstViewController to SecondViewController

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
    let nvc = segue.destinationViewController as! SecondViewController
    nvc.timeArray2 = timeArray
}

In my secondViewController, I have all of the necessary functions for a tableView, but my tableView never populates with any data, as the timeArray2 is empty and will either result in a crash or in an empty tableView.

override func viewWillAppear(animated: Bool)
{
    scrambleTimeTableView.reloadData()
}

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
    return timeArray2.count
}


func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
    let cell = scrambleTimeTableView.dequeueReusableCellWithIdentifier("myCell", forIndexPath: indexPath)
    
    cell.textLabel?.text = timeArray2[indexPath.row]
    cell.textLabel?.font = UIFont(name: "Arial", size: 18)
    cell.textLabel?.textColor = UIColor.blueColor()
    
    print("Populate tableview")
    
    return cell
}

Anything I'm missing?

EDIT:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
    
    let cell = scrambleTimeTableView.dequeueReusableCellWithIdentifier("myCell", forIndexPath: indexPath)
    
    cell.textLabel?.text = timeArray2[indexPath.row]
    cell.textLabel?.font = UIFont(name: "Arial", size: 18)
    cell.textLabel?.textColor = UIColor.blueColor()
    
    print("Populate tableview")
    
    return cell
}

I get a crash on cell.textLabel?.text = timeArray2[indexPath.row] because the array in the secondViewController is empty. The error reads:

Thread 1:EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)

The output says:

empty

[]

fatal error: Index out of range

(lldb)

I had it print the array and if it was full/empty, along with if the array was filling in the firstViewController, which looks like this:

add data

["01.12"]

add data

["01.12", "01.48"]

So I know that the 1st array is filling, and its not able to send any data to the 2nd controller.

Upvotes: 1

Views: 791

Answers (1)

Anna Dickinson
Anna Dickinson

Reputation: 3347

Passing info between view controllers can be tricky, especially when one of them is a tableview. The timing of the events is kinda counterintuitive. To avoid problems, you shouldn't assume things load in any particular order.

In your specific case, it's possible tableView(tableView: UITableView, numberOfRowsInSection section: Int) is being called before the segue happens (the API doesn't guarantee this won't happen, so we need to handle that case).

The fix: first, never use force-unwrapped optionals (the ! operator). Optionals exist for exactly this reason: if something is optional, you need to check if it's valid before you use it -- it's a "hint" that it's possible for the data not to be valid at some point in time.

Second: use a didSet in your timeArray to trigger your reloadData()

Something like this:

var timeArray: [MyTimeType] = [] {
   didSet {
      tableView.reloadData()
   }
}

Also, your segue function should be more like this:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
    if let nvc = segue.destinationViewController as? SecondViewController {
        nvc.timeArray2 = timeArray
    }
}

The destinationViewController may or may not be the one you expect -- you should always check.

Hope that helps!

Upvotes: 2

Related Questions