SwiftUI Sugar Cookies

Robert D
CodeX
Published in
5 min readJul 2, 2021

Getting to use this framework is really breath of fresh air. It’s nice to put things in a more visually intuitive format, however there are still some things I found lacking.

It’s difficult to navigate through the modifiers
Photo by Ethan Sykes on Unsplash

As I built the forest

I found myself in looking up into the abyss of a dark starless sky

These structures had been modified by mine hand to the point where I can barely see the compass in such a hand

Pontification is required.

Ah yes, I recall the moment I realized I had lost my way. I looked up at the tangled overgrown modifiers and thought, what can I cut out without hurting these beautiful tree-like structures. Suddenly my eyes started to adjust. I was able to see the patterns of these modifiers showing up multiple times in the canopy above.

The ugly truth is these modifiers can grow beyond our own comprehension and cause complexity along with dissonance in the ether. The issue is their limitation as a function.

struct Tree: View {
var viewModel: TreeVM
var body: some View {
Image("A_nice_pic")
if viewModel.isScaledToFit {
.scaledToFit()
}
}
}

The above code example is just me messing around by showing a silly fix to the issue with modifiers. I’m not actually able to call this function within the conditional, and I advise against trying to to this at home.

That’s what I need to figure out then; how do I conditionally break up the modifiers so I can call this function only when isScaledToFit is true?

Oh, there’s a faint light in the corner of my eye. I start heading towards it because I’m sure I know where this will end!

my industrial kitchen
Photo by Rohan G on Unsplash

Finally ORDER ORDER ORDER!

I started to open cabinets and poking around at the various ingredients Swift has to offer. hmmm conditionals, we have the classic if-else statements and then switch which does take a value then look at a bunch of possibilities at once. Also I could set up an enum and then what?

It’s funny how these modifiers look like they could be put into a switch statement. What if I have an enum that looks like this?

enum TreeOption {
case scaledToFit
case resizable
}

I can definitely work with this code. I think what would be really interesting is if I could have an array of this type then it can be used with the switch statement.

struct TreeImage: View {    let options: [TreeOption]
// more variables and init
...
var body: some View {
// some beautiful code here!
...
}
}

Huzzah! The recipe is coming together: one part enum, one part view, but there’s something else needed here to complete everything. Now I can use a switch statement with the options and have the view modified then return it!

Here’s a function!

func modifiedByOption(_ view: Image, with type: TreeOption) -> Image {
switch type {
case .scaledToFit:
return view.scaledToFit()
case .resizable:
return view.resizable()
}
}

Now TreeImage can run through the options then call modifiedByOption and that modifier can be called and returned to pretty much mimic the notion of multiple modifier chained on a view using a custom modifier. (it could be made private for the view, or better yet a default implementation on the view protocol)

Now I can do a couple things. First I can use the view and set the options in an array [.resizable] (with more case this looks a lot nicer), or I can set this array into a constant variable and use that (which again would clean it up even more with more complex cases). So it looks a little something like TreeImage("image_name", ImageConstants.resizableImage).

The beauty here is with enumerator’s associated values suddenly the array can be set up to reflect any changes to the UI. Perhaps this array could be set into a state as well such that it can prevent being instantiated on each paint if no changes were observed.

Photo by Denis Degioanni on Unsplash

The structures get bundled up and suddenly these redundancies clear up!

I can once again see the majesty in the sky and I’m able to navigate my own creations with ease. My biggest hope is others have such an easy time to as well and can make strides to strengthen SwiftUI.

For now this is meant for compacting certain views and having cleaner native views that can be set with constants to create ubiquitous styles and friendlier code to share. However this is only is a surface recipe that can help cope with these conditional issues and tighten up redundancies. As I see it, we have this stack of views and for each there’s a chain of modifiers that I would would say gets translated into paint instructions that get run in the stack the struct likes to build into, but rather than creating these instructions and copying the view (as I believe a modifier does under the hood). We instantiate the structure with the options dictionary and each type is mapped to an api call along with whatever else needs to be set that requires a copy to take place.

Sadly it doesn’t help with an issue of order though. Maybe someday types could carry weights that automatically sort into a better order for that. A good example of what I’m talking about here. Take a Text view, modify the frame height to a noticeable size, change the background color (nicely fills out the frames height right?), now reverse those functions. You should see they produce two completely different outcomes. I think this is due part to the modifiers copying itself though.

--

--