Box Pattern in Swift

Hello, my brilliant readers, Leo here. Today we will learn about the Box Pattern in Swift.

We’ll explore one of the oldest reactive techniques that you can easily implement in pure Swift.

But why would you do that if you have Combine or RxSwift. Well, maybe you don’t want to add all the complexity of Combine or RxSwift to your code base but still want to have a reactive code. Reactive programming [is a declarative programming paradigm concerned with data streams and the propagation of change.](https://en.wikipedia.org/wiki/Reactive_programming) This is a special fit for mobile developers because all that we do is wait for the user to do something and react to that.

Let’s code!

 

Problem

You want the properties of your objects could have custom closure-based reactions.

The technique we’ll go through today is called Boxing. But what is it? Let’s examine the code above:

struct User {
    let name: String
    let age: Int
}

class UserController {
    var users: [User] = [] // 1
    
    func getUsersFromNetwork() { // 2
        // get from network stuff
        users = [User(name: "Leo", age: 30),User(name: "Ana", age: 26)]
    }
}

Imagine that you have this very common structure of Controller. You have a function (2) that gets users from the network and adds to a local variable the result of it. Generally, the UserController would use some Closure based completion, that we can inject into the method what we want to do after it completes processing, something like this:

func getUsersFromNetwork(completion: @escaping (Result<[User],Error>)->()) { // 2
    // mock a completion handler
    let users = [User(name: "Leo", age: 30),User(name: "Ana", age: 26)]
    self.users = users
    completion(.success(users))
}

And use it like this:

class UserViewController {
    
    let userController = UserController()
    
    init() {
        userController.getUsersFromNetwork {
            switch $0 {
            case .success(let users):
                print(users)
            case .failure(_):
                print("yeap was an error")
            }
        }
    }
    
}

let userVC = UserViewController()

Getting the printed result in the init method:

first result of using Box Pattern in Swift

And yes, this works just fine. The problem here is that our function is reactive based on his response we aren’t getting reactively the users from the controller. You could have more methods calling API and all of them must implement the completion handler, this is not a very comfortable API to work with, so we can use boxing to react each time the user is set, this way we only deal with ONE closure for all network calls of UserController.

 

The Box

To do the box struct it’s pretty straightforward. We just need a struct that wraps the value and also could receive a closure that is executed each time the value changes. First, we start adding the properties:

struct UsersBox {
    
    typealias Action = ([User]) -> Void
    
    private var value: [User] { // 1
        didSet {
            action?(value)
        }
    }
    
    private var action : Action? // 2
    
    init(value: [User]) {
        self.value = value
    }
}

This is interesting because the design of this struct on 1 and 2 marks has conformity with the open-close principle that says “the software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification”. We making the properties private in Swift tells the compiler that no one could modify this behavior, what is intended.

And now we will add the functions that handle the bind of the closure and with new values.

struct UsersBox {
    
    private var value: [User] {
        didSet {
            action?(value)
        }
    }
    
    typealias Action = ([User]) -> Void
    
    private var action : Action?
    
    init(value: [User] = []) {
        self.value = value
    }
    
    mutating func set(value: [User]) { // 1
        self.value = value
    }
    
    mutating func bind(action: @escaping Action) { // 2
        self.action = action
        action(value)
    }
}

The 1 and 2 marks are functions that help us with the box. 1 mark just set new values and mark 2 is where we get the reactive behavior of it. So now, every time the list of users changes, we can react to that, independently of what function triggered that change. That’s great, isn’t it?

Now we came back to the UserController and use the UsersBox instead of the plain array of users:

class UserController {
    var users = UsersBox()
    
    func getTwoUsersFromNetwork() { // 2
        // get from network stuff
        let users = [User(name: "Leo", age: 30),User(name: "Ana", age: 26)]
        self.users.set(value: users)
    }
    
    func getOneUserFromNetwork() { // 2
        // get from network stuff
        let users = [User(name: "Leo", age: 30)]
        self.users.set(value: users)
    }
    
    func getThreeUsersFromNetwork() { // 2
        // get from network stuff
        let users = [User(name: "Leo", age: 30),User(name: "Leo", age: 30),User(name: "Leo", age: 30)]
        self.users.set(value: users)
    }
    
    
    func configureUsersBox(completion: @escaping ([User]) -> Void) {
        users.bind(action: completion)
    }
}

Here I add other mock methods just to test and prove that they all are responding to changes reactively. In the UserViewController we can now just do one bind, and all the network calls will be reactively fulfilled:

class UserViewController {
    
    let userController = UserController()
    
    init() {
        userController.configureUsersBox {
            print("Users: \($0)")
            
        }
        userController.getTwoUsersFromNetwork()
        userController.getOneUserFromNetwork()
        userController.getThreeUsersFromNetwork()
        print("update table/collections views whatever you want")
    }
}

And we get the result of calling UserViewController:

 

let userVC = UserViewController()

In the console:

second result of using Box Pattern in Swift

 

Generic Box Pattern in Swift

One interesting thing that you can do is to turn the UserBox into a Generic Box like this:

struct Box<T> {
    
    private var value: [T] {
        didSet {
            action?(value)
        }
    }
    
    typealias Action = ([T]) -> Void
    
    private var action : Action?
    
    init(value: [T] = []) {
        self.value = value
    }
    
    mutating func set(value: [T]) {
        self.value = value
    }
    
    mutating func bind(action: @escaping Action) {
        self.action = action
    }
}

And modifying the user controller to use the generic type:

var users = Box<User>()

 

Summary – Box Pattern in Swift

Today we went through some aspects of reactive programming, had a chance to see a very basic implementation of it using generics and closures, and how our code could be more reactive simply by adding a helper structure called Box.

Any thoughts or comments please don’t hesitate to share below.

That’s all my people, I hope you liked reading this article as much as I enjoyed writing it. If you want to support this blog you can Buy Me a Coffee or leave a comment saying hello. You can also sponsor posts and I’m open to freelance writing! You can reach me on LinkedIn or Twitter and send me an e-mail through the contact page.

Thanks for reading and… That’s all folks.

image

Share this post:

Related posts

Sponsor