Cakes and ViewModels

A talk that I think went fairly unnoticed, by Jon Sterling, entitled "Modularity à la Taliban" stated the following:

A ViewModel should only be created by another ViewModel. 1


With this idea in mind, let's see it in practise2:

  1. User tries to login via Facebook. Has she registered herself into our backend with FB before?
  2. Yes. We are done. ✅
  3. No. Show a registration screen3 , so she can complete the process. ❌

My LoginViewController has a LoginViewModel that allows the user to either login via the default way (username + password), or Facebook. This LoginViewModel is composed by two inner objects:

  1. LoginFetcher
  2. FacebookLoginFetcher

Code wise, the consumer would call the LoginViewModel like this for the Facebook authentication:

// `self` is a VC, since the FB API takes a VC has input 
// https://developers.facebook.com/docs/facebook-login/ios#custom-login-button
loginViewModel.loginViaFacebook(self)  

Eventually, inside the FacebookLoginFetcher, we check if the user is already registered in our backend via another object (SocialLoginFetcher)4.

As stated initially, the login can fail, if the user never registered. To inform the UI about this failure, I was initially using a .SocialNotRegistered. With this approach, I was making the consumer (the VC) to be the one to decide what to do next. This decision, would then envolve creating a new ViewModel and push a RegistrationViewController.

Instead, I am now passing a .SocialNotRegistered(SocialRegistrable).

The SocialRegistrable is a protocol used to abstract the registration functionality. The protocol's concrete implementation (SocialRegistrationViewModel) is setup with the information retrieved from the FacebookLoginFetcher:

  1. Authorization token.
  2. User's email.

This change, makes the API's semantics much more explicit about the information flow and takes the burden from the consumer.

You can now have your cake and eat it.

  1. Unless it's the very first one.

  2. This is not a made up example: I am currently working on it.

  3. Some of the information, in this registration screen, is already filled (assuming you allowed FB authentication).

  4. The SocialLoginFetcher is needed, because we also allow Google+ authentication, so that logic (login into our backend) is encapsulated in a single place.