[SC]()

iOS. Apple. Indies. Plus Things.

Solving Small Problems with Small Tuples

// Written by Jordan Morgan // Mar 20th, 2022 // Read it in about 2 minutes // RE: SwiftUI

This post is brought to you by Emerge Tools, the best way to build on mobile.

One thing that has always miffed me in my decade plus of writing Objective-C is the amount of friction it felt like I encountered when I had to throw together a one-off type. Software always changes, but sometimes there are situations where it feels like you’ve got an incredibly deliberate pack of data to return to a caller, and it’ll never be seen or heard from by anybody else.

Please indulge me with a NSRunLoop down memory lane for just a second.

I had a spot recently, where in an individual control, I had three buttons. The last two buttons had a few things injected into them, based on the first button’s type. In pseudo code, something like this:

if buttonOne.design == .someTypeA {
    buttonTwo.design == .someTypeB
    buttonThree.design == .someTypeC
} // and so on

The point isn’t to dissect that code, or recommend a switch or anything - it’s simply to show I’ve got two compartmentalized things that only this control needs to ever know about internally. And that is, I have three buttons and depending on their index and what that first button has set, the last two will have this thing set.

So in Objective-C, where did that leave me? An NSDictionary with magic values, accessed by index? A subclass of NSObject? The thought of making those things inline or reaching for File -> Add New File always felt so heavy for such a tiny little piece of code that nobody else would care about.

What I really wanted to do was cozy up with struct types, before Swift made them come back in style:

typedef struct {
    SJCButtonDesignType middleDesign;
    SJCButtonDesignType trailingDesign;
} SJCTrailingButtonTypes;

And then maybe so something like this:

- (SJCTrailingButtonTypes)trailingButtonConfigurations {
    // Code to figure out what the second and third button should show
    SJCTrailingButtonTypes buttonTypes;
    buttonTypes.middleDesign = SJCButtonDesignWillDo;
    buttonTypes.trailingDesign = SJCButtonDesignDone;

    return buttonTypes;
}

Then, I’d apply those where I called that method from. Good enough, even though it looks a little funky.

Tuple Time

All of this is a longer winded way of me saying I was happy to embrace Swift’s flexibility and its type system, especially for one-off scenarios like these. And, there are times where I just love me a good ol’ Tuple type, just for this. Here’s how I do it in Swift:

private func statusForTrailingButtons() -> (middleButton: WorkItem.Status, lastButton: WorkItem.Status) {
    // Code to figure out what the second and third button should show
    return (.WillDo, .Done)
}

And then, I can get the benefit of type safety, readability and that lightweight-idness (that’s a word, right?) that I’m after:

let trailingData = statusForTrailingButtons()

// In a SwiftUI View down the road...
MyAwesomeButton(type: .ToDo)
MyAwesomeButton(type: trailingData.middleButton)
MyAwesomeButton(type: trailingData.lastButton)

I like that a little better than this:

private func statusForTrailingButtons() -> ([WorkItem.Status]) {
    return [.WillDo, .Done]
}

// Then later on
let trailingData = statusForTrailingButtons()

// In a SwiftUI View down the road...
MyAwesomeButton(type: .ToDo)
MyAwesomeButton(type: trailingData.first ?? .Unset)
MyAwesomeButton(type: trailingData.last ?? .Unset)

And this, for some reason that I can’t really put my finger on, feels a little too heavy for such a small thing:

struct TrailingButtonConfigurations {
    let middleButton: ButtonDesign
    let trailingButton: ButtonDesign
}

Here, this one just doesn’t look as nice to me:

private func trailingButtonDesignFor(index: Int) -> ButtonDesign {
    // Code
}

// In a SwiftUI View down the road...
MyAwesomeButton(type: .ToDo)
MyAwesomeButton(type: trailingButtonDesignFor(index: 1))
MyAwesomeButton(type: trailingButtonDesignFor(index: 2))

Albeit, all of this could just be moved into a constructor - but then you’d have two more properties to carry around, but at least at the call site you’d make things prettier:

init(primaryButtonDesign: ButtonDesign) {
    middleButtonType = /* Code to figure that out */
    trailingButtonType = /* Code to figure that out */
}

// In a SwiftUI View down the road...
MyAwesomeButton(type: .ToDo)
MyAwesomeButton(type: middleButtonType)
MyAwesomeButton(type: trailingButtonType)

So, yeah. I love Tuples for tiny, small and centralized tasks. Or hey, maybe there was a better way to do all of this in the bigger picture. Probably, but I’ve always been much better at creating products than writing code. And the code I do write? I want it to be predictable and expressive.

Until next time ✌️

···

Spot an issue, anything to add?

Reach Out.