MVVM Pattern - Where to Fetch Data

I’m trying to implement the Model-View-ViewModel design pattern in my Swift 4 app. Everything I’ve read about this pattern (and MVC) says to keep non-UI code out of the ViewController. However, most tutorials have the fetching of data inside the ViewController.

When the app starts it needs to fetch data from a .plist file (and eventually a JSON file). I have the method for fetching and decoding this data in its own file (NetworkClient.swift), but I just don’t know where to put its callback. Currently it’s in my ViewController’s viewDidLoad method:

networkClient.readInData { data in
    guard let data = data else { fatalError("error getting data") }
    self.myViewModels = data.map { MyViewModel(data: $0) }
    self.tableView.reloadData()
}

Is this the right way to go? Is there a better way? I thought about putting it in MyViewModel but I would still have to call this method from my ViewController and that seems like a lot of excess. I’m trying to learn the best Swift practices so any advice would be helpful. Thanks!

@jstrawn Can you please help with this when you get a chance? Thank you - much appreciated! :]

@sprouse5 Thanks very much for your question!

One suggestion I have for you is to have your viewDidLoad method, CALL a function inside NetworkClient.swift which RETURNS the data RECEIVED from your REST API and parsed into appropriate model objects. Keep your networking code, and all JSON parsing OUTSIDE of your ViewController. Your ViewController should have a property (which should be an array), and to which you would assign the data received from NetworkClient.swift (which also should be in the form of an array). All networking, and JSON parsing code should be inside your NetworkClient.swift class. This ensures a cleaner separation of your code. Always make sure no networking, nor JSON parsing is done in your ViewController. Your ViewController should simply call a function which in turn returns the final, finished product. When you order a pizza, you expect the pizza to be cooked. You shouldn’t be expected to do any additional cooking after it has been delivered :slight_smile:

I hope this helps!

All the best!

1 Like

It’s totally fine to call data fetching methods inside a view controller, but you want to keep the actual networking logic separate.

Like @syedfa said, your view controller can have an array property that stores the data received. You could also have a public static array property in the networking client, and access it within the view controller.

Your current method called within the view controller is a bit dense, if you wanted to shorten it, you could move the closure to the networking client. Something that may help you get started is the Multicast Delegate Pattern (chapter 15), which would help you with reloading the tableView only after the data is complete. :slight_smile: Let me know if this helps!

1 Like

@syedfa Thanks for the analogy! It makes this design pattern make a lot more sense :slight_smile:

@jstrawn Thank you for the detailed answer, I ended up moving the closure to NetworkClient so that the ViewController only gets the returned data in its final form via a delegate.

Thanks for your quick and detailed responses!

@sprouse5 No problem at all! I’m happy to help :slight_smile:

All the best!

@jstrawn Very minor correction, the Multicast Delegate Pattern is chapter 16 in the book, not 15 :slight_smile:

Hi @sprouse5,
if it helps, one simple way to look at MVVM with swift and ensure strict separation is

Model          --> Do not include UIKit
View           -->  Ensure that you don't use the Model
ViewModel      --> connect the view to the model and other logic

You can also add helper functions that can download data from URL’s and encode/decode JSON, however your Model would conform to the Decodable protocol

cheers,

jayant

2 Likes

This topic was automatically closed after 166 days. New replies are no longer allowed.