Why Data is not passed from the View Controller to the Table View Controller?

For some reason, the value is lost when I try to transfer it from ViewController to TableViewController. The scheme seems to be simple, the value is taken from the first TVC (such as a warehouse) and transmitted to the VC (details), and from there to the order basket or cart - to the second TVC. And when I try to pass data to the cart, it pass only one value. I.e. if I try pass next item in Cart I see only one value in the Cart. It seems that append() method doesn't work, array always contains only one item.


What is wrong here?


Model:

import Foundation
import UIKit

struct Bouquet {
    var name: String  
}


First TVC:

import UIKit
class BouquetsTableViewController: UITableViewController { 
  
    var bouquets = [Bouquet]()
    var bouquet: Bouquet?

    override func viewDidLoad() {
        super.viewDidLoad()
       
        bouquets = [           
            Bouquet(name: "Flower 1"),
            Bouquet(name: "Flower 2"),
            Bouquet(name: "Flower 3"),
            Bouquet(name: "Flower 4"),
            Bouquet(name: "Flower 5"),
            Bouquet(name: "Flower 6")
        ]
    }

    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return bouquets.count
    }
   
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: "bouquetsCell", for: indexPath)
        cell.textLabel?.text = bouquets[indexPath.row].name
        return cell
    }
   
    // MARK: - Navigation
  
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        guard segue.identifier == "GoToDetails" else { return }
       
        let indexPath = tableView.indexPathForSelectedRow
        bouquet = bouquets[(indexPath?.row)!]
       
        let detailsVeiwController = segue.destination as! DetailsViewController
       
        detailsVeiwController.bouquet = bouquet       
    }   
   
    @IBAction func unwindToBouquets(_ unwindSegue: UIStoryboardSegue) {      
    }
}


VC:

import UIKit

class DetailsViewController: UIViewController {
   
    var bouquet: Bouquet?
    var itemToCart = ""
   
    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var imageLabel: UIImageView!
   
    @IBAction func addToCartButton(_ sender: UIButton) {
        itemToCart = nameLabel.text!
    }
   
    @IBAction func viewCartButton(_ sender: UIButton) {
    }   

    override func viewDidLoad() {
        super.viewDidLoad()
       
        nameLabel.text = bouquet?.name
    }
   
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        guard segue.identifier == "addToCart" else { return }
       
        let cartTableViewController = segue.destination as! CartTableViewController       
       
        cartTableViewController.cartItem = bouquet!
    }
}


Second TVC:

import UIKit

class CartTableViewController: UITableViewController {
   
    var cartItems = [Bouquet]()    
   
    var cartItem: Bouquet? {
        didSet {
            guard let item = cartItem else { return }
            cartItems.append(item)           
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()        
    }

    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return cartItems.count
    }
   
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: "CartCell", for: indexPath)

        cell.textLabel?.text = cartItems[indexPath.row].name

        return cell
    }
}

Accepted Reply

I suppose cartItems contains only the "last" item. True ?


So append works, but add item to an empty array.


To see this, add the print statements:

    var cartItem: Bouquet? {
        didSet {
            print(cartItems)
            guard let item = cartItem else { return }
            cartItems.append(item)          
            print(cartItems)
        }
    }



If so, you could create a singleton object in your app to keep cartItems

Replies

What is the life cycle of your cart VC? When is it created and destroyed?


It sounds to me like it’s working as it should, except you’re expecting the cart VC to magically keep the contents of the cart persistently somehow even though the VC comes and goes. This is a common pitfall caused by trying to keep your model as part of your VC, when it really needs a separate model class to maintain that persistent state. Then when an instance of your cart VC is created, it just displays whatever is in the model (cart). But it does not own that model.

I have created a separate model and corrected code,


struct Cart {
    var name: String   
}

but alas, nothing has changed.

What is the life cycle of your cart VC? When is it created and destroyed?


Think about who holds the reference to that Cart struct. When is it created and when is it destroyed? Do you really want a struct, since they’re passed by value? (Modifying a copy won’t change the original)

I suppose cartItems contains only the "last" item. True ?


So append works, but add item to an empty array.


To see this, add the print statements:

    var cartItem: Bouquet? {
        didSet {
            print(cartItems)
            guard let item = cartItem else { return }
            cartItems.append(item)          
            print(cartItems)
        }
    }



If so, you could create a singleton object in your app to keep cartItems

Did that solve your issue ?

Thank you, you said everything correctly, I just did not understand it at first.


Can you recommend me a good guide about life cycles and hierarchy of controllers?

Answers in this topic helped me find a solution. I used JSON.


Thank you!


Is there a method that shows that the view controller will now be unloaded? For example, there is a method viewDilLoad, and is there a method to reverse it, just like viewWillUnload?

Great you found a solution. Don‘t forget to close this thread.


For the API, there is no willUnload.


but maybe you can try the property isBeingDismissed.

Thank you again!


How to close this thread?

Simply click on Correct Answer in the post that is the correct answer…