Kind of stuck on the same problem now for a couple of days.
I have a ViewControllerA class that serves as a CollectionView delegate and datasource. The CollectionView is hooked up through a storyboard. ViewControllerA class contains the data model that feeds the CollectionView.
Whenever ViewControllerA loads for the first time, it connects to a database, and loads information as needed into a data model (userDictionary) . Once the controller loads all data, it reloads the CollectionView, and fires syncing methods that listen for any changes in the database, to update the userDictionary accordingly, and reload the respective items in the collectionView.
Everything works fine up to this point.
When I transition to any different ViewController class, say ViewControllerB class, I’m passing a copy of the userDictionary from ViewControllerA to ViewControllerB in prepareForSenderA, and passing it back to ViewControllerA through prepareForSenderB.
Here’s the strange behavior. When I transition back to ViewControllerA class, the CollectionView loads fine with the same data that was passed to ViewControllerB, but fails to load any new changes that the syncing method observes in ViewControllerA.
I know that the syncing methods are still working fine, because any new data is showing up in the debugger when I print it out as it’s loading in ViewControllerA. And I know that my userDictionary data model in the same controller class is receiving those updates because of a didSet observer that’s printing out the most up-to-date state of userDictionary.
And the odd thing is that whenever I print out the contents of the data model within ViewControllerA class, it prints out the old state of the model as it existed when it was passed to ViewControllerB class. Even though the didSet observer just proved that the model was actually updated!
It’s almost as if ViewControllerA class is somehow retaining a reference to the data model as it existed when it was passed over to ViewControllerB, and somehow “loses” its reference to the data model in “self” when that model gets passed back.
Another note: If I stay in ViewControllerA the whole time, and don’t pass the userDictionary back and forth, I don’t get this problem any more.
The code below sums up how I'm passing data back and forth:
View Controller A Class:
class ViewControllerA: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
//MARK: Data Structures
var userDictionary = [[String: String]]() { didSet { print("userDictionary.count from DIDSET,", userDictionary.count)}}
//prints out: 9
//MARK: Initialization
override func viewDidLoad() {
self.loadUserDictionaryFromDatabase()
}
func loadUserDictionaryFromDatabase() {
//code that loads information from a database
//var objectInstance["name"] = firstName
//self.userDictionary.append(objectInstance)
print("loader observed numberOfItems", numberOfItems)
//...once data is fully loaded
self.syncDataFromDatabase()
}
func syncDataFromDatabase() {
//sync new data from database
//var newObjectInstance["newName"] = newFirstName
//self.userDictionary.append(newName)
print("syncer observed newNumberOfItems", newNumberOfItems)
}
//MARK: View Controller Transitions
@IBAction func segueAtoB(_ sender: Any) {
self.performSegue(withIdentifier: "segueAtoB", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//Segue from VCA to VCB
if segue.identifier == "segueAtoB" {
let controller = segue.destination as! ViewControllerA
//Pass initialized userDictionary from VCA to VCB
controller.userDictionary = self.userDictionary
}
}
//MARK: Dummy Test Number of Items in UserDictionary
@IBAction func printMostUpToDateNumberofItemsInDictionary(_ sender: Any) {
print("userDictionary.count from TEST,", userDictionary.count)
}
}
View Controller B Class:
class ViewControllerB: UIViewController {
//MARK: Data Structures
var userDictionary = [[String: String]]()
//MARK: View Controller Transitions
@IBAction func segueBtoA(_ sender: Any) {
self.performSegue(withIdentifier: "segueBtoA", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//1. Segue to Home-Screen Controller
if segue.identifier == "segueBtoA" {
let controller = segue.destination as! ViewControllerB
controller.userDictionary = self.userDictionary
}
}
}
Result of Prints:
Step 1: Start in View Controller A
Step 2: Segue From A to B
viewControllerB.userDictArray = viewControllerA.userDictArray
Step 3: Segue From B to A
viewControllerA.userDictArray = viewControllerB.userDictArray
Step 4: Observe Debugger Output
Debugger output using UUID:
Step 1: Start in View Controller A
Step 4: Observe Debugger Output
I really think you need / want to use an "unwind" segue.
In ViewControllerA
add this function:
@IBAction func returnFromB(_ segue: UIStoryboardSegue) {
if let vcDest = segue.destination as? ViewControllerA,
let vcSource = segue.source as? ViewControllerB {
// pass userDictionary from VCB back to VCA
vcDest.userDictionary = vcSource.userDictionary
print("unwind segue from B")
}
}
Then, connect it by Ctrl-Drag from the View Controller icon to the Exit icon at the top of ViewControllerA
in your storyboard. Give it a segue identifier.
Now, in ViewControllerB
, you can call:
self.performSegue(withIdentifier: "unwindSegueBtoA", sender: self)
and the returnFromB()
function in ViewControllerA
will handle transferring back your data.
Here is a basic example: https://github.com/DonMag/DataPass
Another (probably better) option would be to create a "data manager" class, and set/get your data from that class from each controller, as opposed to passing it back and forth.