Better Models

This is a preview of one of the new features in the upcoming version 2 of my Clean Swift Xcode templates.

The VIP cycle isolates your code dependency, while the models that are passed through the VIP cycle isolate your data dependency.

The underscore way

In version 1 of my templates, you create the Request, Response, and View Model as follows:

The underscores separate the three different roles taken on by the names of the structs. The basic form is:

This naming convention already works very well. But we can do better.

Add a little nested-ness

Thanks to Ismail for pointing it out, Swift allows you to nest structs within structs. In version 2, you can take advantage of nested structs to create your models:

This is how you create them:

If underscores already work so well, why am I changing it? There are several benefits which I personally encountered in my own projects.

Share formatted struct for multiple view models

Sometimes, you’ll want to share a single formatted struct with multiple view models.

Let’s say you have an account screen. The requirement is to show the user’s currently open orders, if any, and up to three past orders.

The old way:

The FormattedOrder struct in both Account_FetchOpenOrders_ViewModel and Account_FetchOrderHistory_ViewModel are identical. Wouldn’t it be nice if you can DRY it up?

The new way:

The FormattedOrder struct is now moved out of the view model structs but remain inside the Account scene. It only needs to be defined once.

Isolate data dependency. Avoid namespace collision

I hear ya. But you could just move the FormattedOrder struct to the global namespace so it can be shared.

However, like global variables, global types should be avoided.

First, FormattedOrder is intended to be shared by ONLY FetchOpenOrders and FetchOrderHistory. Nothing else. Unnecessary sharing because of convenience is a code smell.

Second, if you have a OrderDetails scene, it’s going to need its own FormattedOrder. And it’ll be vastly different. Showing a single order’s details may also require you to provide the order date, individual line items, payment info, and shipping address, …etc.

You could certainly lump all these order details onto the same FormattedOrder struct, so it can be shared throughout your app.

Such a fat model is confusing and hard to use. You may need to make all your variables optional, simply because they’re returned by your API. You’ll then have to deal with a lot of if let.

The most evil of all. You’ve just coupled the data dependency between three use cases across two scenes.

If you continue to go down this wrong path, you could name the structs differently – FormattedOrder1 and FormattedOrder2. Or whatever creative juice you drink. Yes, you avoid data dependency, but you further contaminate the global scope. Keeping the same name would cause a collision in the global namespace.

By separating the Account and OrderDetails scenes into their own respective namespace, you can use the same name for your FormattedOrder struct while having the exact data you need for display.

Best of both worlds. Voila.

Less cluttered auto completion

The auto completion feature in Xcode is great. Faster typing. Fewer typos.

The auto completion suggestion list for the old underscore way looks like:

Underscore

If you have many scenes and use cases, it can get long very quickly. It becomes harder to pick the one you want.

For the new and improved nested struct, it looks like:

Nested Struct 1Nested Struct 2

It takes you step by step. First, pick your scene. Second, pick your use case. Third, pick your model. More focus. Less clutter.

Finally, it’s just more conventional. First, Objective-C. Now, Swift. Entity tokens use the upper camel case naming convention. Underscores are popular in other languages such as Ruby and C. Not so much in the iOS world. Now, you can go back to being a purist.

If you’ve downloaded my templates from Gumroad, you’ll get an email when version 2 becomes available. If not, sign up below to download it now.

Raymond
I've been developing in iOS since the iPhone debuted, jumped on Swift when it was announced. Writing well-tested apps with a clean architecture has been my goal.

17 Comments

  1. I like the update! However, I must say I was expecting you to give some credit to Ismail, who commented the idea in “Clean Swift iOS Architecture for Fixing Massive View Controller”. I’ve been using it since he commented that and it’s an amazing improvement.

        1. I’m fortunate enough to work with Ismail, the dude’s brilliant. Always nice to see someone of his calibre contributing something to the wider ecosystem.

  2. @Raymond Hey, since you are changing the templates, will you also be rearranging your site on clean-swift? Since new dudes may be reading up on old resources, and comments.

    1. I’ll likely do a ‘Get Started Here’ page to group the posts under different categories. There are some posts that are prerequisites before others.

      1. That would be nice. Right now the blog’s not too big to able to start from the beginning and work forward but it could get real messy real quick.

  3. Very nice update!
    Well done 🙂
    One Swifty improvement for structs that are simply containers of other structs, you could transform them to enums, because an enum with no cases cannot be instantiated like ˋOrderDetails()ˋ which should not be possible 😉

  4. How are you handling date fields when the interactor gets them as Date objects and it’s the presenter’s responsibility to format it into a String? I see in all the models above they are Strings. Does this you are converting them into Strings in your domain layer??

    1. Hi jody,

      You models should look like:

      Your interactor passes a Date object to your presenter, which converts it into a String object.

      1. Thanks Raymond. I also took a look at your latest sample project and noticed there as well. You are passing domain entities into the presenter. I’ve been debating doing the same. I’ve watched uncle bob’s videos a number of times and was left with the impression that we shouldn’t be doing that, i.e. the response model is a different model than the domain model. But I think this is fine since it’s an outer layer depending on an inner layer.

        1. Raymond, after thinking about what you suggested above for a model, it seems to violate the clean architecture.
          The use case’s request and response DTOs are part of the business logic (they form the boundary between View and the Use Cases) and the view model belongs to the view layer. In the above model the use case struct contains both DTOs and the ViewModel.

          1. Hi jody,

            You could define an extra model layer between the interactor and presenter to further isolate the data dependency. But I haven’t found any benefit in doing so.

  5. What if, instead of separate structs, a “Global Model” can just conform into specific protocols in which we can pass on different view models? Is this not a good practice? I’m extremely new to CleanSwift, and I have a lot of questions. But Imma start with this. Thank you.

Leave a Comment

Your email address will not be published. Required fields are marked *