user7219266
user7219266

Reputation:

How to pass Model data from ViewController to ViewModel

I am modifying my App in order to implement the MVVM pattern.

Introduction:

I use WebServices to get all my data.

At the start of the App, during the loading screen, I make a request to all WebServices so that all my data are available.

For example, when I will tap on the Menu in the App to access a specific Scene, I already have all the data needed, and I pass the model to the corresponding ViewController with the Segue.

The Question:

The real question is, what is a good way to pass this model data to my ViewModel?

I am using a struct that implements the Codable protocol to obtain my model after requesting the web service and parsing the json data.

Here is how my ViewController looks like :

class VigilanceViewController: UIViewController {

    @IBOutlet var outletTableView: UITableView!

    var vigilance: Vigilance?

    fileprivate let viewModel = VigilanceViewModel()

    override func viewDidLoad() {

        super.viewDidLoad()

        if let vigilance = vigilance {
            // pass data to the viewModel
        }

        outletTableView.dataSource = viewModel
    }
}

In the ViewModel, I don't know how to declare the Vigilance model or how to properly initialize the ViewModel.

Here is the ViewModel :

class VigilanceViewModel: NSObject {

    var items = [VigilanceViewModelItem]()
    var maxVigilance: Int = 0
    var vigilance: Vigilance? = nil

    override init() {
        super.init()

        // Append objects in my items array from the vigilance Model so that the dataSource is ready to go and clean

    }
}

extension VigilanceViewModel: UITableViewDataSource {

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

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return items[section].rowCount
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = UITableViewCell()

        // TODO

        return cell
    }
}

Because my Model contains a lot of informations that I currently don't need in this ViewController, I am making an items array so that it is easier to use in my DataSource.

Here is how my Vigilance Model looks like :

struct Vigilance: Codable {

    let result: Result

    struct Result: Codable {
        let startDate: Int
        let endDate: Int
        let sendDate: Int
        let advice: String?
        let comment: String?
        let colorMax: Int
        let details: [VigilanceDetails]?

        struct VigilanceDetails: Codable {
            let dptNumber: String
            let dptName: String
            let color: String
            let risks: [Risks]?

            struct Risks: Codable {
                let code: String
                let label: String
            }
        }
    }
}

My first guess on why I can't declare my model without optional is because I don't have custom initializer right ?

But this is not the major issue here.

I would like to know how to pass this model data (previously obtained in this VC from prepareForSegue) to the ViewModel, and correctly initialize the ViewModel.

If someone can guide me here it would be really appreciated.

I am exploring some obscur parts of the OS and design patterns, trying my best to understand and apply what I am learning.

Upvotes: 1

Views: 3511

Answers (2)

Bence Pattogato
Bence Pattogato

Reputation: 3900

And what about the same declaration but in the ViewModel?

class VigilanceViewModel: NSObject {
    let items: [VigilanceViewModelItem]
    let maxVigilance: Int
    let vigilance: Vigilance

    init(items: [VigilanceViewModelItem], vigilance: Vigilance) {
        self.items = items
        self.vigilance = vigilance
        maxVigilance = vigilance.max
    }
}

Upvotes: 0

Bence Pattogato
Bence Pattogato

Reputation: 3900

My first guess on why I can't declare my model without optional is because I don't have custom initializer right ?

As long as you are initiating your UIViewControllers with segues, you won't be able to use a custom initialiser in which you can pass the desired object. If you want to avoid optional variables, I recommend to write an init function, something like this (and move your VC to a xib file):

class ViewController: UIViewController {
    let param: String

    init(param: String) {
        self.param = param
        super.init(nibName: "YourViewControllerNibsName", bundle: Bundle.main)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

The real question is, what is a good way to pass this model data to my ViewModel?

Once you are initiating your view controller from code instead of segues, you can write some helper functions, usually I put it in a Router (or name how you like) class, like:

class AppRouter {
    // Return UIViewController not to expose the type of ViewController
    static func getViewController(with param: String) -> UIViewController {
        return ViewController(param: param)
    }
}

// Using it from an another VC
let actualVC: UIViewController!
let vcToPresent = AppRouter.getViewController(with: "yourParam")
actualVC.present(vcToPresent, animated: true, completion: nil)

Upvotes: 1

Related Questions