BuildDSL is a Swift package that offers a robust Domain-Specific Language (DSL) for crafting intuitive builder APIs for Swift structs. It streamlines the creation of complex objects with a clean, type-safe syntax, utilizing Swift's @resultBuilder, protocols, and generics, along with an auto-generated Builder pattern.
- Type-Safe Builder Pattern: Auto-generate builders for Swift structs with compile-time type checks.
- Declarative Syntax: Employ a succinct DSL to outline your object construction.
- Automatic Code Generation: Minimize boilerplate with auto-generated builder code.
- Nested Builders: Seamlessly construct complex objects using nested builders.
- Error Handling: Utilize
Resulttypes for comprehensive error handling. - Customizable Defaults: Specify default values for immutable fields with
@Default. - Property Exclusion: Omit properties from the builder with
@Ignore.
- Swift 5.9+
- iOS 13.0+ / macOS 11.0+ / tvOS 13.0+ / watchOS 6.0+ / visionOS 1.0+
- Xcode 15.0+
Add BuildDSL to your project with Swift Package Manager by including the following in your Package.swift:
dependencies: [
.package(url: "https://github.com/Ahmed-Ali/BuildDSL.git", from: "1.0.0")
]Or add it through Xcode by going to File > Add Package Dependencies and enter:
https://github.com/Ahmed-Ali/BuildDSL.git
Annotate your struct with @Builder and use the generated builder API:
import BuildDSL
@Builder
struct Person {
let name: String
let email: String
@Default(Date())
let createdAt: Date
@Ignore
var isVerified: Bool = false
}
// Create instances using the fluent builder API
let result = Person.build { $0
.name("John Doe")
.email("[email protected]")
}
switch result {
case .success(let person):
print("Created: \(person)")
case .failure(let error):
print("Build failed: \(error)")
}The full API documentation is available online, including:
- Getting Started Guide: Learn the basics with step-by-step examples
- API Reference: Complete documentation for all macros and types
- Advanced Patterns: Sophisticated usage scenarios and best practices
- Migration Guide: How to integrate BuildDSL into existing projects
@Builder: The core macro for generating builder patterns@Default: Setting default values for properties@Ignore: Excluding properties from builders@Escaping: Handling closure propertiesBuilderError: Error handling in builders
For comprehensive examples, see:
- Sources/BuildDSLClient/main.swift - Real-world usage examples
- Tests/BuildDSLTests/MacroUsageTests.swift - Test cases covering all features
-
Why use
@resultBuilderinstead of a closure?
@resultBuilderensures the closure is solely used for object construction, preventing arbitrary code execution and potential misuse. -
Are there benefits to using Builder pattern +
@resultBuilderover just a Builder?
Yes, it enhances discoverability and IDE assistance, making it easier to understand how to initialize objects with complex configurations.
- Initialization: Structs must have a memberwise initializer. Exclude properties with
@Ignoreand provide default values or implement the initializer yourself. - Buildable Dependencies: Declare dependent structs before dependees to ensure proper macro execution and avoid compilation errors.
- Autocomplete: While macros and Swift's Generics with
@resultBuilderare powerful, IDE autocomplete may be less helpful with complex nested types. Patience and manual code entry may be required at times.
Contributions are welcomed! Here's how you can help:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Add tests for your changes
- Ensure all tests pass (
swift test) - Run SwiftLint (
swiftlint) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Create a Pull Request
Please make sure to:
- Write tests for any new functionality
- Follow the existing code style and conventions
- Update documentation if needed
- Ensure CI passes
BuildDSL is released under the MIT License. See LICENSE for details.
Happy building with BuildDSL! 🚀