DEV Community

Cover image for [Pro] Creating Custom Property Wrappers in Swift: Reduce Boilerplate Code
Karan Pal
Karan Pal

Posted on • Originally published at Medium

[Pro] Creating Custom Property Wrappers in Swift: Reduce Boilerplate Code

Stop Writing the Same Swift Code Over and Over Again

Look, we've all been there. You're scrolling through a codebase and suddenly you're hit with that déjà vu feeling. Wait, didn't I just see this exact same pattern three files ago?

Swift developers are notorious for writing the same boilerplate code repeatedly. UserDefaults access, validation logic, thread safety patterns - the same getter/setter dance everywhere.

The Problem: Repetitive Code Hell

UserDefaults Nightmare:

var username: String { get { UserDefaults.standard.string(forKey: "username") ?? "" } set { UserDefaults.standard.set(newValue, forKey: "username") } } var isNotificationsEnabled: Bool { get { UserDefaults.standard.bool(forKey: "isNotificationsEnabled") } set { UserDefaults.standard.set(newValue, forKey: "isNotificationsEnabled") } } // ... and 15 more properties just like this 😩 
Enter fullscreen mode Exit fullscreen mode

Validation Copy-Paste Fest:

private var _email: String = "" var email: String { get { _email } set { guard !newValue.isEmpty else { return } guard newValue.contains("@") else { return } _email = newValue } } // Same pattern, different rules, everywhere... 
Enter fullscreen mode Exit fullscreen mode

The Solution: Property Wrappers

Property wrappers aren't magic - they're persistent middleman objects that intercept property access. When you understand this, everything clicks.

Here's how that UserDefaults mess becomes clean:

@propertyWrapper struct UserDefault<T> { let key: String let defaultValue: T var wrappedValue: T { get { UserDefaults.standard.object(forKey: key) as? T ?? defaultValue } set { UserDefaults.standard.set(newValue, forKey: key) } } } // Now your settings become: @UserDefault(key: "username", defaultValue: "") var username: String @UserDefault(key: "isNotificationsEnabled", defaultValue: false) var isNotificationsEnabled: Bool 
Enter fullscreen mode Exit fullscreen mode

Real-World Examples That Actually Matter

Thread-Safe Properties:

@ThreadSafe var cache: [String: Any] = [:] @ThreadSafe var isLoading: Bool = false 
Enter fullscreen mode Exit fullscreen mode

Validation with Status Reporting:

@Validated(validator: { $0.contains("@") }) var email: String = "" user.email = "invalid" // Sets value print(user.$email.isValid) // Checks validation status 
Enter fullscreen mode Exit fullscreen mode

Debounced Search:

@Debounced(delay: 0.5, action: { query in SearchAPI.search(query: query) }) var searchQuery: String = "" 
Enter fullscreen mode Exit fullscreen mode

What You'll Learn in the Full Article

✅ How property wrappers actually work under the hood (they're code generators!)
✅ Build thread-safe properties without queue management everywhere
✅ Create validation wrappers with projected values for status reporting
✅ Handle async operations and escaping closures properly
✅ When to use reference vs value semantics in wrappers
✅ Real debugging stories and common pitfalls

The Key Insight

Property wrappers become part of your personal Swift toolkit. Write them once, use them everywhere.

Start with simple patterns like UserDefaults, then move to complex ones like debouncing and validation.

Ready to transform your repetitive code into clean, reusable patterns?

👉 Read the complete guide with working examples:
https://medium.com/swift-pal/pro-creating-custom-property-wrappers-in-swift-reduce-boilerplate-code-6190f0e3c6d8


Follow me for more practical Swift content:

Top comments (0)