released Sun, 24 Feb 2019

Cleaner Classes with Structs and Tuples

An easy way to add more structure to your classes by leveraging structs and enums

Imagine you're working on a social networking app and you have a controller that is responsible for displaying a user image with a follow and a like button. Imagine also, that - in line with the single responsibility principle and view controller composition - the like and follow functionality is handled somewhere else. Our social network does have premium user accounts as well as corporate user accounts (companies) and so there're a couple of configuration options available for your InteractiveUserImageController (naming was never my strong suit). One possible option of our class could look (partially) like this (for demonstration purposes, there're obviously a lot of things that we can change here):

final class InteractiveUserImageController: UIView {
    /// Should the Premium Layout be used
    var isPremium: Bool
    /// What kind of account do we have
    var accountType: AccountType
    /// Tapping the view will highlight it
    var isHighlighted: Bool
    /// The username to display
    var username: String
    /// The profile image to display
    var profileImage: UIImage
    /// Can the current user like this user
    var canLike: Bool
    /// Can the current user follow this user
    var canFollow: Bool
    /// Should the big like button be used
    var bigLikeButton: Bool
    /// For some containers we need a special background color
    var alternativeBackgroundColor: Bool

    init(...) {}
}

Now, these are a lot of properties. As our app grows, we might end up adding even more properties here. Of course, we can refactor and group them by responsibility, but sometimes single responsibility still leads to quite a few properties. So how can we possibly structure this a bit better?

Swift Struct Structure

Swift's struct types can actually be really helpful here. What we can do is group these properties by their type by moving them into one-time structs:

final class InteractiveUserImageController: UIView {
    struct DisplayOptions {
        /// Should the big like button be used
        var bigLikeButton: Bool
        /// For some containers we need a special background color
        var alternativeBackgroundColor: Bool
        /// Should the Premium Layout be used
        var isPremium: Bool
    }
    struct UserOptions {
        /// What kind of account do we have
        var accountType: AccountType
        /// The username to display
        var username: String
        /// The profile image to display
        var profileImage: UIImage
    }
    struct State {
        /// Tapping the view will highlight it
        var isHighlighted: Bool
        /// Can the current user like this user
        var canLike: Bool
        /// Can the current user follow this user
        var canFollow: Bool
    }

    var displayOptions = DisplayOptions(...)
    var userOptions = UserOptions(...)
    var state = State(...)

    init(...) {}
}

As you can see, what we've done here is we moved the state into seperate struct types. Not only does this make the class cleaner, it also makes it easier for new developers to find related options.

This is already a very nice improvement, but we can do even better!

The issue we have here is that there is an additional step involved when looking up a property.

Since we're using one-time struct types we have to define them somewhere, (i.e. struct DisplayOptions), but we also have to instantiate them somewhere (i.e. let displayOptions = DisplayOptions(...). This is in general ok, but in larger classes this might still require an additional lookup to figure out the type of displayOptions. However, there is no way in Swift to create anonymous struct types like this 1:

let displayOptions = struct {
    /// Should the big like button be used
    var bigLikeButton: Bool
    /// For some containers we need a special background color
    var alternativeBackgroundColor: Bool
    /// Should the Premium Layout be used
    var isPremium: Bool
}

Anonymous Structs nee Tuples

Actually, there is such a type in Swift. It is our good old friend, the tuple. See for yourself:

var displayOptions: (
  bigLikeButton: Bool,
  alternativeBackgroundColor: Bool,
  isPremium: Bool
)

This defines a new type displayOptions that has three properties (bigLikeButton, alternativeBackgroundColor, isPremium) and can be accessed just like our struct from earlier:

user.displayOptions.alternativeBackgroundColor = true

Even better, the defintion does not need an additional initialization, so everything is in the same place.

Enforced Immutability

Finally, the whole tuple is either mutable or immutable. You can see that in the first line: We're defining var displayOptions but no var or let bigLikeButton. bigLikeButton is also a var just like displayOptions. The advantage of this is that this enforces moving static constant properties (i.e. line height, header height) into a different (let) group than mutable properties.

Add some data

As a nice addition, you can also use this feature when you need to initialize these properties with values:

var displayOptions = (
  bigLikeButton: true,
  alternativeBackgroundColor: false,
  isPremium: false,
  defaultUsername: "Anonymous"
)

Very similar to the earlier code, this defines a tuple of options but also initializes them with the correct values right away.

Nesting

Naturally, you can also nest these tuple options easily, which makes it even easier compared to our original struct approach:

class UserFollowComponent {
    var displayOptions = (
        likeButton: (
            bigButton: true,
            alternativeBackgroundColor: true
            ),
        imageView: (
            highlightLineWidth: 2.0,
            defaultColor: "#33854"
        )
    )
}

I hope you found this article useful. I'm using this simple pattern quit a lot in order to give my code more structure. Sometimes only for 2-3 properties, but even then it is already beneficial.

1

Unlike C