Then: one extension turns every Swift initializer into a configuring block
✨ Super sweet syntactic sugar for Swift initializers
At a glance
- What is it?
- Then is a small Swift package that adds then(), with() and do() so you can build and configure an object in a single expression. It covers NSObject subclasses out of the box, and needs an empty protocol conformance for your own types.
- Who is it for?
- Adopt Then when a view controller or model layer is full of throwaway immediately invoked closures and you want type safety over them. Do not adopt it if your team already settled on a builder type or a configuration helper, since Then covers one narrow formatting problem and offers no injection layer, no defaults and no stored configuration.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 27 days ago.
- What is it written in?
- Mainly Swift, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.
Star history
- devxoul/Then4,397 ★ · +4
| Repository | 2026-09-23 | 2026-11-14 |
|---|---|---|
| devxoul/Then | 4.4k | 4.4k |
4 Stars in the last 52 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
What Then replaces: the immediately invoked closure around every object
Swift has no built-in builder syntax, so configuring a freshly created object usually costs a local variable and an extra statement. The pattern Then targets is the immediately invoked closure, where an anonymous function is declared, mutated inside, and then called so its return value is assigned. That works, but it pushes the object onto a line of its own before any of its properties are set.
Then removes the boilerplate. The library adds a method called then() that takes a closure receiving the instance, lets you mutate it, and returns it. The result is a single expression where construction and configuration sit together, which matters most in UIKit code where every property and subview has to be set before the view is used.
The audience is narrow and easy to name: Swift developers writing view controllers, view models or any code that assembles objects line by line. It is not a general purpose utility library. It has no logging, no HTTP client, no date helpers. It does one thing, and the value of that one thing depends entirely on how much object assembly your codebase contains.
then() on NSObject subclasses, and the empty conformance for everything else
The scope of then() is the interesting design decision. Rather than extending every type in the standard library, Then confines its conformance to NSObject subclasses. A UILabel, a UITableView or an OperationQueue can be configured immediately because they already inherit from NSObject.
Your own types get there differently. Then exposes a protocol, and conforming is an empty declaration with no methods to implement. Once a type conforms, its then() behaves like the built-in one.
extension MyType: Then {}
let instance = MyType().then {
$0.really = "awesome!"
}That protocol is the whole extension mechanism. There is no generic constraint, no builder type parameter, and no code generation. The cost is that a plain Swift struct does not get then() until you write the conformance yourself, which is one line, but it is a line you must know to write.
with() and do() cover value copies and throwing away the result
then() returns the object it configures, which is right for reference types you intend to hold. For value types, Swift mutates a copy, so mutating inside then() would not write back to the variable you assigned. Then handles this with a separate method called with(), which takes the value, applies the mutations to its own copy, and returns that copy.
let newFrame = oldFrame.with {
$0.size.width = 200
$0.size.height = 100
}
newFrame.width // 200
newFrame.height // 100The original frame is untouched. If you reach for with() on a class, the semantics still work, but you are copying semantics you do not need.
A third method, do(), exists for the case where you do not want the result at all. It configures an object and returns nothing, which suits a sequence of calls on a shared instance where capturing a return value would be noise.
UserDefaults.standard.do {
$0.set("devxoul", forKey: "username")
$0.set("[email protected]", forKey: "email")
$0.synchronize()
}Three methods is the entire public surface as documented: then(), with() and do(), plus the empty Then protocol. Nothing else is promised.
Installing through CocoaPods or Swift Package Manager
Then publishes as a CocoaPod. Add the pod name to your Podfile and run pod install as usual.
pod 'Then'The Swift Package Manager route is the one to look at twice. The manifest shown in the README declares the dependency at major version 2, and that line has not been refreshed to the current major version. A team that copies the snippet verbatim is pinning itself to the older line without being told.
import PackageDescription
let package = Package(
name: "MyAwesomeApp",
dependencies: [
.Package(url: "https://github.com/devxoul/Then", majorVersion: 2),
]
)The manifest style itself is the older Swift tools format, where dependencies are declared as Package(url:majorVersion:). Newer tooling writes the package(url:from:) form with an explicit version range instead. Expect to rewrite that snippet to match whatever your Xcode version generates, because the README does not document a supported Swift version range and will not tell you which form to use.
Where then() changes nothing: type inference, reuse and concurrency
The sugar is a formatting win, and there are places where it buys nothing. First, closure inference. In a chain of statements, the compiler still has to infer each property's type from the closure parameter, so an ambiguous literal inside then() produces the same error it would in an immediately invoked closure. Then adds no overloads to disambiguate.
Second, reuse. then() is a one-shot configuration expression. Once the object is configured, there is no stored configuration to re-apply later, so a view controller that needs to rebuild a subview on rotation has to re-run the same block or extract it into its own function. There is no equivalent of a declarative layout description in this package.
Third, concurrency. Calling them on a value that a task mutates concurrently is as unsafe as calling any other method, and Then does nothing to make it safer or to flag it. The package contains no async or await support in its documented API.
How Then differs from a builder struct or a configure method
The standard alternative is a builder type, which wraps the object and exposes chainable setters returning Self. That approach gives you named configuration methods, and a configuration object you can store and pass around. It costs a wrapper type, and every property you want to configure becomes a method.
Then takes the opposite route. It adds one method to the type rather than wrapping it, so the value you end up with is the original UILabel, not a builder. Nothing about the returned object is then-specific, and the calls you write are Swift property assignments rather than invented method names.
A configure method on your own class is the third option, and for a single type in a single project it is often the cheapest. You write the configuration inside the initializer. The difference is that Then applies the same shape across every NSObject subclass in the codebase, including the ones you did not write, which is what makes it stick in view controller code.
Package.swift, Tuist.swift and a README that stops at the happy path
The repository layout is conventional for a Swift package: Package.swift at the root, source and test directories under Sources and Tests, a Then.podspec for CocoaPods, and Project.swift together with Tuist.swift for generating the Xcode project. Both Sources and Tests directories are present.
What the documentation does not cover is the part that decides whether you can adopt it. There is no changelog, no release cadence, no stated Swift version range, and no note about which Swift versions the current line requires. The package is MIT licensed and the last push was on 2026-10-18, while the newest release listed is 3.0.0 from 2022-04-26, so the commit history has moved without a matching tagged version; the repository is not archived. Read Sources/ and the Package.swift manifest yourself before pinning a version, because the README will not settle those questions for you.
Editorial conclusion
Adopt Then when a view controller or model layer is full of throwaway immediately invoked closures and you want type safety over them. Do not adopt it if your team already settled on a builder type or a configuration helper, since Then covers one narrow formatting problem and offers no injection layer, no defaults and no stored configuration. Verify first that your own types can take the empty Then conformance, and read the Package.swift manifest in the repository, because the README pins an older major version in its install snippet and names no Swift requirement.
Frequently asked questions
How do I install Then in a Swift project?
Add the pod through CocoaPods with pod 'Then', or declare the GitHub URL as a dependency in your Package.swift. The README shows a CocoaPods line and a Swift Package Manager manifest for the library.
Does Then work with my own Swift types, or only with Apple classes?
then() is available on NSObject subclasses out of the box. For your own types you write an empty conformance, extension MyType: Then {}, and then() becomes available on them.
What is the difference between then(), with() and do() in Then?
then() returns the object it configures, which suits reference types you keep a reference to. with() applies the mutations and returns a new copy for value types such as CGRect, and do() configures an object without returning it at all.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/devxoul-then)Star history chart for your README
[](https://hysenlabs.com/projects/devxoul-then)Was this useful?