Solving Small Problems with Small Tuples
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 ✌️