Building an indie app, architectural problems and design patterns

Subscribe to my newsletter and never miss my upcoming articles

Hello, there! Hope you're doing fine. In this blog post I want to talk about obstacles I can't help thinking about that I may come across while developing my first independent iOS application. I've been trying to transform my ideas into prototypes lately. I'm not familiar with UI design terms so my works may be got bigger than simple prototypes. I'm taking it slow in purpose, trying not to burn out in this process. Although turning ideas and into reality is any developer's dream, it is really hard to do the all the stuff by yourself if you are passionate about creating something useful that people may like and something that could turn into passive income someday.

While designing user interface of my first indie app, I also think about how to handle the data and the business logic. For example, there is this screen that have connection with multiple screens kind of a root of the app. I didn't even write single line of code for it but I could easily see that it may become another example of the joke iOS developers constantly use while talking about the cons of MVC architectural design pattern which is "massive view controller".

I almost always try to write my view code in its own files by subclassing related object from iOS SDK. If you are doing things programmatically this is a big help for avoiding the massive view controller issue. Although it helps, you still have to handle presentation logic or business logic in our poor friend view controller. Well some of you may start to yell at me now: "You supposed to handle business logic in the model not in the view controller, you are doing it wrong!" and I believe you are right but I just didn't come across a good use of MVC in our lovely iOS community so far. Every book I read, every course I take they all do business logic in the view controller. If you start to doing that way, you never see the model files as a place that you can handle the business logic beside that structuring simple data models.

On the other hand, Apple really encouraged us to use MVC so far, I got a feeling that it is changed after the SwiftUI. Just because you can use binding so easy with the little help of Combine, Apple's first party reactive programming framework, MVVM start to become de facto choice of a lot of developers. SwiftUI bring a lot of problems with itself but that's an another blog post material. Anyway, I loved the MVVM approach which turned out that I was trying to use this approach while developing web apps with React unconsciously. But there is a big problem in the UIKit land that there is no binding feature!

Binding is may be the most important part of MVVM design pattern according to my researches. You have to to find your own solution to use this pattern in the UIKit land and I think another problem is everyone approach this problem differently. I personally like to have a single way of doing things, it clears a lot of confusion and help us not to think so much about how to solve the problem. Everyday we face a lot of complexity while writing software and I think having a common way of doing things helps us a lot. Physically and of course mentally. To be honest, there is a true disease called "imposter syndrome" that we face everyday as developers.

Anyway, after doing a lot of research in few days I found using the boxing approach more easy to grasp and apply MVVM design pattern, at least for now. If I become more SOLID in mind developer(!) in the future don't be surprise that I could find another approach better. For now I don't want to handle with coordinator pattern or protocol-oriented programming or third party reactive framework as an extra, I just want to minimize my view controllers' responsibilities. That's it. To do that, I need that binding feature so that I can easily maintain the communication between our view controllers and view models. Let's create a generic class to be able to have that kind of functionality in our project.

import Foundation

class Observable<T> {

    var value: T? {
        didSet {
            observer?(value)
        }
    }

    var observer: ((T?) -> Void)?

    func bind(observer: @escaping (T?) -> Void) {
        self.observer = observer
    }
}

With this generic class we somehow created a data type that have a value, can be bind and observe the changes in that value. T represents the standart data types we'll be using with this generic type. For example, T might be a String or Bool if we need it. With the help of closures we can listen the changes in two place at the same time by calling it. We have a superpower named two way binding now 🚀

P.S. I started to write this blog post by just talking about what bothers me lately then it got bigger than I was planned it to be and suddenly turned into a tutorial. I really didn't want this post to be that kind of article so I cut it off. If you want to see how this pattern can be applied you can just get in touch with me!

Cover photo by Ali Yahya on Unsplash

Comments (3)

MACs's photo

Great article! Could you please provide an example of how you use the observale class? ViewController <-> ViewModel (using that observale).

Ufuk Canlı's photo

Thanks for the comment. Explaining how to use this generic object can easily be a whole another article itself but let me try to do it with few words here.

Let's say you have a table view controller that you list things that you requested from external rest api. You decode the coming response and turn into array of swift objects. This array of objects represent your current state for your view controller now. Instead of keeping this data and its related logic in the view controller you could easily create a view model like this:

class ListViewModel {
    var list = Observable<[Item]>()

    func performRequest(_:completion:) {
        ...
        list.value = responseObject
        ...
    }
}

Now you keep array of "Item" inside an observable object named "list". You can create another observable object keep to track of whether your request is over or not. As you know, this is an asynchronous process.

class ListViewModel {
    var list = Observable<[Item]>()
    var isLoading = Observable<Bool>()

    func performRequest(_:completion:) {
        self.isLoading.value = true
        ...
        self.list.value = responseObject
        ...
        self.isLoading.value = false
    }
}

Now back to our good old view controller. You can easily initialize your view model inside your view controller class.

class ListViewController: UITableViewController {

    var viewModel = ListViewModel()

    override func viewDidLoad() {
        super.viewDidLoad()

        viewModel.performRequest { [unowned self] _ in
             DispatchQueue.main.async {
                  self.tableView.reloadData()
             }
        }
    }
}

To listen the changes you can bind your "isLoading" object like this:

func bindLoading() {
   viewModel.isLoading.bind { [unowned self] isLoading in
       isLoading ? self.showLoading() : self.hideLoading()
   }
}

You should call the function above in your viewDidLoad file and you are ready to go. You could populate your table view using your view model instance like this:

override func tableView(_:numberOfRowsInSection:) -> Int {
      return viewModel.list.value.count
}

I didn't compile this code but I believe it works and I think you get the idea 🤓 just be careful with the optionals and make sure not to get into the retain cycle by using [unowned self] or [weak self]✌️

P.S. If you want initial values for observable objects in your view model you could add an init method inside the generic class like this:

init(_ value: T?) {
   self.value = value
}
MACs's photo

Ufuk Canlı Amazing! Thank you very much for the response. It is more clearer now. And waiting for more articles to come 👍