Press enter to see results or esc to cancel.

Protocol for Modularity in Swift

We often develop an app in a short time but it is a very complex process. To meet the delivery target, it would be better to use some pod dependencies. SJProgressHUD is one of the modular dependencies available for displaying popup or loading processes which is available on Cocoapods. The alternatives are SVProgressHUD, MBProgressHUD, JQProgressHUD.

Let us choose SJProgressHUD as an example. We can attach SJProgressHUD.show() everywhere such as in UIViewController classes, AppDelegate class, Presenter or Controller (if you are using MVP/MVC) or maybe inside Model classes. It works fine, but we are not DRY (don’t repeat yourself). If we put it on model, it will be a single responsibility violation. The model should not be responsible for showing a progress view. If we fail to define the boundary between the model class and view class, we will get potential serious problems in the future because of tightly coupled parts.

Your Spaghetti Code Should Be Refactored

In order to solve the issues above, we should refactor the code so that it follows the ‘correct’ architecture and designs. There are many ways to refactor it.

Make Extensions

A possible first solution is to make an extension class from UIViewController. In the extension files, add some functions which will be used to handle loading view such as showLoading(), showError(), and showSuccess().

import SJProgressHUD

class UIViewControllerExtension {
      
      extension UIViewController {
          func showWaiting(message: String){
            SJProgressHUD.showWaiting(message)
          }
          func showError(message: String){
            SJProgressHUD.showError(message)
          }
      }
}

After creating an extension, we are allowed to call showWating(“getting doctors, please wait...”) from anywhere as long as we are on UIViewController. However, not all derivatives of UIViewController actually need to know about that. For example, we have AboutVC which only render the application version.

//no need to import SJProgressHUD
class AboutVC:UIViewController{
      func viewDidLoad(){

      }
      func viewWillAppear(){
         //magic here... wa can call it any where on each UIViewController.
         //but unfortunetly the AboutVC no need to know about it.
        showWaiting("please wait...")
      }
}

Modularizing the Code Base

The second solution is to make a new component which is responsible to control the SJProgressViewHUD. Let’s say UILoadingView. It should be easy to plug and remove from UIViewController. Let it meet the Single Responsibility principle so that if we somehow need to replace the SJProgressViewHUD with SVProgressViewHUD, we only need to make changes on the UILoadingView part.

protocol UILoadingView {
    func showLoading()
    func showLoadingWithLabel(title:String?, subtitle:String)
    func showErrorWithLabel(message: String)
    func showSuccessWithLabel(message:String)
    func hideLoading()
}

Let’s make the UILoadingView only attachable into UIViewController by using where Self: UIViewController. In this part, we also make the default implementation. In the future, we only need to refactor this when we have to make changes on SJProgressViewHUD.

import SJProgressViewHUD
extension UILoadingView where Self: UIViewController {
    
    func hideLoading(){
        HUD.hide()
    }
    func showLoading(){
        HUD.show(.progress)
    }
    func showLoadingWithLabel(title:String? = "Waiting", subtitle:String){
        HUD.show(.labeledProgress(title: title, subtitle: subtitle))
    }
    func showErrorWithLabel(message: String){
        HUD.flash(.labeledError(title: "Failure", subtitle: message), delay: 1.5)
    }
    func showSuccessWithLabel(message:String){
        HUD.flash(.labeledError(title: "Success", subtitle: message), delay: 1.5)
    }
}

By attaching the UILoadingView into UIViewController, now we can call showLoading(“please wait…”) from this class. We do not need to attach the UILoadingView if the UIViewController doesn’t have any requirements to show progress view.

class LoginVC: UIViewController, UILoadingView{
    
    func anyProcessNeedToShowLoadingView(){
        showLoadingWithLabel(subtitle: "please wait...")
    }  
}

Done! Simple right? We have been implementing this concept in many of our products. By localising the dependencies, it will be easier for future maintenances.

Comments

Leave a Comment

Show Buttons
Hide Buttons