2

Is there a way to avoid open variables when using segues (or not segues)? Everybody saw code like this:

if segue.identifier == ListViewController.className()
    {
        guard let indexPath = tableView.indexPathForSelectedRow else { return }
        let destinationVC = segue.destination as? ListViewController

        var data: CategoryModel

        data = filteredData[indexPath.row]

        destinationVC?.passedData = data
    }
}

But in ListViewController now we have a var that open for access.

class ListViewController: UIViewController
{
    //MARK: - DataSource
    var passedData: CategoryModel?

Maybe exist way to avoid this?

I was thinking about dependency injection with init(data: data), but how to initiate this vc right?

Edited.

Using segue it's not a main goal. Main is to make var private. If there exist nice way to not to use segues and push data private I will glad to know.

I was trying to use init() and navigationController?.pushViewController(ListViewController(data: data), animated: true) but Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value on line:

self.tableView.register(ListTableViewCell.nib(), forCellReuseIdentifier: ListTableViewCell.identifier())
  • These links might be helpful https://stackoverflow.com/questions/35315404/custom-init-for-uiviewcontroller-in-swift-with-interface-setup-in-storyboard – ielyamani Sep 29 '18 at 17:45
  • And this one https://stackoverflow.com/questions/26207846/pass-data-through-segue – ielyamani Sep 29 '18 at 17:46

2 Answers2

1

You can't actually make Interface builder use a custom init for your view controller, it will always use init?(coder:).
So the easiest way to pass data to your view controller is to use a non-private property.

But if you really don't want to use an internal or public var you can always try something with a Singleton or Notifications but I don't think it would be wise

AnderCover
  • 2,488
  • 3
  • 23
  • 42
  • Maybe I'm just bothering to make this variable private, but how does this affect the code's testability? It turns out any method can put anything there. This worries me. I do not like singletones, but I see that any deviation from segues only complicates the code. – Igor Kandaurov Sep 29 '18 at 17:10
  • I'm not sure testability is an issue here. If you're interested with testability and good architecture you may want to try Clean Swift or VIPER. And yes, I would use a var and keep the code simple – AnderCover Sep 29 '18 at 18:10
0

You could do it like so

class ListViewController {

    private var passedData: CategoryModel?

    private init () {
    }

    public convenience init (passedData: CategoryModel?) {
        self.init()
        self.passedData = passedData
    }
}

And in tableView(_:didSelectRowAt:) of your initial table view controller:

let data: CategoryModel = filteredData[indexPath.row]
let destinationVC = ListViewController(passedData: data)
self.present(destinationVC, animated: true, completion: nil)
ielyamani
  • 17,807
  • 10
  • 55
  • 90
  • I believe the goal is to make `passedData` private but still somehow use a segue. – rmaddy Sep 29 '18 at 16:38
  • @rmaddy is it impossible to make it let or private when use segue? – Igor Kandaurov Sep 29 '18 at 16:42
  • @Carpsen90 i have an error that `tableView` outlet is nil when `self.tableView.register(ListTableViewCell.nib(), forCellReuseIdentifier: ListTableViewCell.identifier())` after init. – Igor Kandaurov Sep 29 '18 at 16:45
  • Who is supposed to call the convenience initializer? And it doesn‘t compile incase you make the class derive from `UIViewController`. And `destinationVC` isn‘t used at all. – Fabian Sep 29 '18 at 16:49
  • @Carpsen90 `let destinationVC = ListViewController(passedData: data)` unused. perform just takes identifier. – Igor Kandaurov Sep 29 '18 at 16:51
  • It‘s not using segues anymore, which the question is about. – Fabian Sep 29 '18 at 16:54
  • @Fabian if there is now way to make it private i'll glad to see how to make it right. – Igor Kandaurov Sep 29 '18 at 16:58
  • @IgorKandaurov The problem is that on segue the vc is initialized from storyboard, which uses `required init(coder:)`, so you can‘t pass things in through the initializer. If you can‘t put it through the initializer, then you have to give sb outside ability to set sth on it later if the vc requires data. You can do it with a setter, maybe one which works only once else throws, or with a public variable as usual. Or have a singleton which the vc can ask for data. Or use some kind of notification system. If you avoid segues, you can construct programmatically like Carpsen or use a factory nethod – Fabian Sep 29 '18 at 17:01
  • @Fabian I'll glad to use factory method, maybe you have something to read about it? – Igor Kandaurov Sep 29 '18 at 17:13
  • @IgorKandaurov The idea is to construct the vc from a `UIStoryboard` instance, set data on it and then return the vc and present it as you want (means you can‘t use it with segues). You can make the factory method `static func make(data: MyData) -> MyVC` public and leave the formerly-public variable on the vc fileprivate/private, so only the factory-method residing in the same file/same class can set it. – Fabian Sep 29 '18 at 17:16