Last active
August 7, 2021 07:52
-
-
Save JarvisTheAvenger/570133a1d7bc169595548a729d32aa3f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
// Property Wrapper | |
// A property wrapper adds a layer of separation between code that manages how a property is stored and | |
// the code that defines a property. For example, if you have properties that provide thread-safety checks or store | |
// their underlying data in a database, you have to write that code on every property. When you use a property wrapper, | |
// you write the management code once when you define the wrapper, and then reuse that management code by applying it to multiple properties. | |
@propertyWrapper | |
struct ValidEmail { | |
var email : String? | |
var wrappedValue : String? { | |
get { return email } | |
set { email = validate(email: newValue) ? newValue : nil } | |
} | |
init(wrappedValue value: String?) { | |
self.email = value | |
} | |
private func validate(email: String?) -> Bool { | |
guard let email = email else { return false } | |
let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}" | |
let emailPred = NSPredicate(format:"SELF MATCHES %@", emailRegEx) | |
return emailPred.evaluate(with: email) | |
} | |
} | |
struct User { | |
@ValidEmail var email : String? | |
} | |
var user = User() | |
user.email = "d" | |
print(user.email ?? "invalid email") | |
user.email = "d@d.com" | |
print(user.email ?? "invalid email") | |
// Configurable Wrappers | |
@propertyWrapper | |
struct Scores { | |
private let minValue = 0 | |
private let maxValue = 100 | |
private var value: Int | |
init(wrappedValue value: Int) { | |
self.value = value | |
} | |
var wrappedValue: Int { | |
get { | |
return max(min(value, maxValue), minValue) | |
} | |
set { | |
value = newValue | |
} | |
} | |
} | |
@propertyWrapper | |
struct Constrained<Value: Comparable> { | |
private var range: ClosedRange<Value> | |
private var value: Value | |
init(wrappedValue value: Value, _ range: ClosedRange<Value>) { | |
self.value = value | |
self.range = range | |
} | |
var wrappedValue: Value { | |
get { | |
return max(min(value, range.upperBound), range.lowerBound) | |
} | |
set { | |
value = newValue | |
} | |
} | |
} | |
struct Player { | |
@Scores var score : Int = 0 | |
@Constrained(0...100) var customScore : Int = 0 | |
} | |
// Gaining Access to the Wrapper Itself | |
// If you need access to the wrapper itself (not the wrapped value), | |
// you need to add an underscore before the property name. For instance, let’s take our Account structure. | |
struct Account { | |
var firstName: String | |
var lastName: String | |
@ValidEmail | |
var email: String? | |
} | |
let account = Account(firstName: "Test", | |
lastName: "Test", | |
email: "test@test.com") | |
account.email // Wrapped value (String) | |
//account._email // Wrapper(Email<String>) | |
// Projected Value | |
//The @propertyWrapper annotation provides one more syntax sugar - a projected value. | |
//This property can have any type you want. To access this property, | |
//you need to add a $ prefix to the property name. To explain how it works, | |
//we use an example from the Combine framework. | |
// The @Published property wrapper creates a publisher for the property and returns it as a projected value. | |
class Message { | |
@Published | |
var message: String = "" | |
func sendMessage() { | |
print(message) // Print the wrapped value | |
$message.sink { print($0) } // Subscribe to the publisher | |
} | |
} | |
// Limitations | |
// 1. Applying multiple wrappers to the property is not allowed | |
// 2. They can’t participate in error handling. The wrapped value is a property (not a method), and we can’t mark the getter or setter as throws |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment