Tweak it with TweaKit
Yes, I built a little wheel. It’s called TweaKit, which you can read as “Tweak it.”
This post is mainly about why I built this wheel. For more details about TweaKit itself, please head over to GitHub.
In our current project, a lot of places depend on tweaks, including but not limited to:
- Hiding entry points for features that are not ready yet
- Dynamically toggling features on in production
- Letting PMs tweak parameters when they are trying out a feature
- Speeding up development and debugging. For example, if a requirement says “show a red dot after 24 hours,” you can use a tweak to simulate 24 hours instead of really waiting that long.
Before TweaKit, we used SwiftTweaks in the project. But from a developer’s perspective, SwiftTweaks is not particularly pleasant to use:
- It only supports a limited set of types out of the box. For example, if the business logic uses
Float, but the tweak type has to beDouble, you need an extra type conversion everywhere you use the value. - Besides basic types, selection‑style tweaks only support
String, which introduces even more glue code. -
The way you read a tweak’s value is not very elegant. You have to write something like:
button.tintColor = ExampleTweaks.assign(ExampleTweaks.colorTint)To make this less painful, we wrapped it in a getter in our project:
extension Tweak { var value: T { TweakLibrary.assign(self) } } - There is no tweak search. Once the number of tweaks grows, finding the right one becomes annoying.
So I decided to build my own wheel. To be honest, the idea came up about a year ago, but procrastination delayed it until now.
When I started designing TweaKit, I took the issues above as explicit goals:
-
TweaKit natively supports
Int[8|16|32|64],UInt[8|16|32|64],Float,Double,String,Bool, andUIColor, so you can get rid of type‑conversion boilerplate in business code.These types should cover most use cases. But TweaKit doesn’t stop there: it also supports
Arrayas long as theElementtype conforms to theTweakSecondaryViewItemConvertibleprotocol. With that, TweaKit can drive reordering of array elements directly from the UI. TweaKit already provides default conformances for most common types, so in many cases all you need is:extension MyType: TweakSecondaryViewItemConvertible { }Imagine your PM comes one day and says: “We want to change the order of the 4 tabs on the main screen dynamically.” Using SwiftTweaks’
StringOption, you’d have to define a ton of options. With TweaKit, this becomes trivial. -
For single‑selection tweaks, as long as your type conforms to the
Selectableprotocol, you can use it directly as the tweak’s value type. AndSelectableis just a typealias for theTweakSecondaryViewItemConvertibleprotocol mentioned above.Beyond the built‑in types, you can extend TweaKit to support your own types. All you need to do is conform your type to the
Tweakableprotocol and provide the corresponding UI and update logic. TheAppIcontype in the demo app is one such example: by makingAppIconconform toTweakable, we can switch the demo app’s icon directly via TweaKit. -
When it comes to reading tweak values, TweaKit leans on Swift’s property wrapper feature.
A tweak is itself declared as a property wrapper:
@Tweak("My Tweak", defaultValue: false) static var enableMyAwesomeFeatureOnce defined, you can use it like this:
if enableMyAwesomeFeature { // execute my awesome code }As you can see, there’s no extra wrapper or glue involved. The call site becomes immediately cleaner. When you declare a tweak, you specify its
Valuetype, and that’s exactly the type you get when you read it. Thanks to Swift’s type inference, most of the time you don’t even need to spell outValueexplicitly. -
Finally, TweaKit supports searching tweaks. You can configure whether to use fuzzy matching, whether the search is case‑sensitive, and whether to use smart case.
TweaKit can do more than what I’ve listed here. If you’d like to explore further features and usage patterns, please check the repository on GitHub.
Open sourcing is not the end — it’s a new starting point. TweaKit is still at version 1.0, and there are many items waiting on my TODO list. TweaKit will keep evolving in the future.
Last but not least, TweaKit is by no means my solo effort. I received a lot of help along the way:
- All UI/UX work for TweaKit and its demo app was designed by @gggeeeeggge.
- @FrainL gave me a ton of feedback and suggestions on TweaKit’s API design.
TweaKit would not exist in its current form without them. I want to express my sincerest thanks to both of you.