Communicating Model with UIViewController


#1

Hello Ben,

How would you recommend to communicate changes in a model to a view controller to keep unidirectional data-flow in the context of Apple’s MVC?

Where would you recommend to put networking code? For the project at hand a request is made and handled in the model. In that scenario, what would be the best way to launch / hide an activity indicator screen in an app when a network request is in progress? Currently, I believe I need to reach out every time to view controller via the delegate to launch and hide the activity indicator.

Just to give a rought idea, an example code:

protocol AModelDelegate: AnyObject {
    func modelDidChange(_ data: AModelDataProtocol)
}

class Model: AModelProtocol, AServiceHandlerProtocol {
   var modelData: AModelDataProtocol!

    weak var delegate: AModelDelegate? {
        didSet {
            delegate?.modelDidChange(modelData)
        }
    }

   func downloadData() {
     ServiceInvoker.invokeService(service)
  }
   
   // AServiceHandlerProtocol
  func handleSuccess(_ rsp: RspType) { 
    delegate.modelDidChange(data)
  }

  func handlerError(_ error: ErrorType) {

  }
}


final class AViewController: UIViewController, AModelDelegate {
   func modelDidChange(_ data: AModelDataProtocol) {
      // populate view with data etc. 
   }
}

Generally, I wonder what is the most optimal way to communicate model changes in vc and update model with changes that come from vc. Would you recommend a screencast where I could take a look at one of your solutions to learn from it?

Warm regards

P.S. This is the first time I have subscribed to NSScreencast. I wanted to see the value of it. It is valuable. Just wanted to say “Thanks for your work”.


#2

Thanks for the kind words.

I tend to view models as the entities in your system (the important domain concepts that have relationships and rules around them). For this reason I never put networking code in models.

In my apps, networking code is separated, and generally interacts with an endpoint and returns JSON-modeled objects. These are then converted into model objects (or updated if they exist already). I often do this with Operations (see the episode series on Advanced NSOperations).

The high level overview looks like this:

ViewController -> Operation -> Networking -> Models -> delegate/completion block

The view controller kicks off the flow, and either implements a delegate method to be notified of completion (or error), or sets a completion block on the operation.

You don’t necessary need to use Operations here either, you could replace this with a DataController of sorts and have the same overall structure, just the internal threading mechanics might be a little different (Dispatch async, etc)


#3

Oh and for this, I think it could be done 2 ways. First (the simplest) is to have the startAnimating() happen in the view controller before it kicks off the operation. Then in the completion you call stopAnimation().

In a recent client project I had my requests behind a caching layer, so I only wanted the network indicator to load if it was actually making a network request (and not serving from the cache), so I added separate delegate methods (didStartLoading, didStopLoading) and my view controller implemented those.