SwiftUI: Modifiers order matter, mainly if you are dealing with dimensional ones
I am relatively new to SwiftUI, and since I am someone who adopted UIKit framework for years, some concepts in the declarative framework resisted to enter my head. One of them is the idea behind the modifiers, cause since we are not dealing with reference types anymore, the way we change or interface is totally different.
The message I want to pass in this article is simple: Be careful with the way you declare your component modifiers, you may face different outcomes!
UIKit former way
It doesn't matter how did you use to build your interfaces before SwiftUI's release, what matters is the way you thought when assigning your components attributes. Let's say you are about to declare some UILabel and then assign some properties to it. It doesn't matter if you customize its border color, border width or background color first or last. What you simply thought is that you were listing, describing how it should appear, and when running your app it rendered the independent properties for that view:
In UIKit, you have only one reference to that label instance, and when assigning a new value to some property, you were like building or fixing a house: no matter what you were changing, the place is the same.
The same is valid for when you are building your interface with storyboards and xibs. You are just defining some attributes to a single object.
It only matters the content, not the order.
Now that you understand(or should already have) the concepts about building some custom interface in UIKit, let's now take a look on how SwiftUI works behind the scenes.
The first thing you need to know is that SwiftUI framework totally relies on structs, which are value types. What does it mean? It means that there is no reference to a View in SwiftUI in the entire application. There is no way to assign more than one property to a single view because it only describes how the interface is. If some update is needed, the whole interface is re-rendered as it was a new UI.
"TA DA!", I bet you didn't know it was that way. Taking our house previous example, it's like our house needed to be broken down and rebuilt instead of just fixing some specific points. Pretty radical way right? But that's SwiftUI.
SwiftUI modifiers are just methods applied to a View that return a new instance of that, but with some new properties that may bring a new look-and-feel:
In our last example, we create a new instance of Text, which is like a UILabel. When we apply a new font, we are saying: "Throw this old-fashioned text away and let's bring a new one just like the previous with a new custom font". Now we have a new instance. When we ask for a foreground color, a new background and a padding, we are actually discarding the previous Text and raising a new one step by step.
Now I have a secret to tell you: The order you define each new attribute via modifiers to your View matter and you may face different outcomes depending on that order.
SwiftUI's View is a packet
When you are crafting some physical item, you must take care of the steps order to achieve the desired result, because a step input is the resulted output from the previous one. In SwiftUI, since modifiers are functions, the input for a modifier is the direct outcome from the previous function.
The example I shall provide to you is a play dough, which children love to play with. Let's suppose you have some play dough at your hands:
Now you want to add more mass to your play dough, so it becomes bigger:
The f function transforms our play dough into a bigger one. Think of that like we sent that into a machine that returned a bigger mass to us.
Now we want to paint that play do into red:
The g function mapped our bigger play dough into a new one with the same size but now filled in red.
What am I trying to illustrate? The default color of our play dough is green and if we just applied the red color before expanding it, the outcome would be a red play dough with an extra mass in green, because expanding it assumes the new mass will be green. That's what the function does.
The same happens with views in SwiftUI. If we had a text and applied a padding, the entire dimension is around the content is increased, and when applied the red background, it's applied to the whole content:
But if we applied the padding just after the background color, we would have a blank extra space:
This article taught about the true idea behind SwiftUI's View modifier and how the interface can be different depending on the order you apply them. The input for the next modifier is the output from the previous one, different from the UIKit classes where the order didn't matter. This is just the main example of that, but you should keep in mind that the secret for visually debugging your View's body is to add the modifiers one by one and check the results in your preview for each step. In SwiftUI, it doesn't cost your time since you can see the outcomes in real time and it's totally readable! I hope you enjoyed ;)