Each wrapper is a submodule, so you add just the one(s) you want
pod 'Burritos/Copying', '~> 0.0.3'
pod 'Burritos/UndoRedo', '~> 0.0.3'
pod 'Burritos/UserDefault', '~> 0.0.3'
@AtomicWrite
A property wrapper granting atomic write access to the wrapped property.
Reading access is not atomic but is exclusive with write & mutate operations.
Atomic mutation (read-modify-write) can be done using the wrapper mutate method.
@Atomic var count = 0
// You can atomically write (non-derived) values directly:
count = 99
// To mutate (read-modify-write) always use the wrapper method:
DispatchQueue.concurrentPerform(iterations: 1000) { index in
_count.mutate { $0 += 1 }
}
print(count) // 1099
@Clamping
A property wrapper that automatically clamps its wrapped value in a range.
A property wrapper arround NSCopying that copies the value both on initialization and reassignment.
If you are tired of calling .copy() as! X you will love this one.
@Copying var path: UIBezierPath = .someInitialValue
public func updatePath(_ path: UIBezierPath) {
self.path = path
// You don't need to worry whoever called this method mutates the passed by reference path.
// Your stored self.path contains a copy.
}
@DefaultValue
A property wrapper arround an implicitly unwrapped optional value which fallbacks to a given default value.
@DefaultValue(default: 0)
var count
count = 100
// or
@DefaultValue(default: 0)
var count = 100
// Assigning nil resets to the default value
print(count) // 100
count = nil
print(count) // 0
@DynamicUIColor
A property wrapper arround UIColor to support dark mode.
By default in iOS >= 13 it uses the new system wide user interface style trait and dynamic UIColor constructor to support dark mode without any extra effort. On prior iOS versions it defaults to light.
@DynamicUIColor(light: .white, dark: .black)
var backgroundColor: UIColor
// The color will automatically update when traits change
view.backgroundColor = backgroundColor
To support older iOS versions and custom logics (e.g. a switch in your app settings) the constructor can take an extra style closure that dynamically dictates which color to use. Returning a nil value results in the prior default behaviour. This logic allows easier backwards compatiblity by doing:
let color = DynamicUIColor(light: .white, dark: .black) {
if #available(iOS 13.0, *) { return nil }
else { return Settings.isDarkMode ? .dark : .light }
}
view.backgroundColor = color.value
// On iOS <13 you might need to manually observe your custom dark
// mode settings & re-bind your colors on changes:
if #available(iOS 13.0, *) {} else {
Settings.onDarkModeChange { [weak self] in
self?.view.backgroundColor = self?.color.value
}
}
A property wrapper to set and get system environment variables values.
@EnvironmentVariable(name: "PATH")
var path: String?
// You can set the environment variable directly:
path = "~/opt/bin:" + path!
@Expirable
A property wrapper arround a value that can expire. Getting the value after given duration or expiration date will return nil.
@Expirable(duration: 60)
var apiToken: String?
// New values will be valid for 60s
apiToken = "123456abcd"
print(apiToken) // "123456abcd"
sleep(61)
print(apiToken) // nil
// You can also construct an expirable with an initial value and expiration date:
@Expirable(wrappedValue: "zyx987", expirationDate: date, duration: 60)
var apiToken: String?
// or just update an existing one:
_apiToken.set("zyx987", expirationDate: date)
A reimplementation of Swift Implicitly Unwrapped Optional using a property wrapper.
var text: String!
// or
@LateInit var text: String
// Note: Accessing it before initializing will result in a fatal error:
// print(text) // -> fatalError("Trying to access LateInit.value before setting it.")
// Later in your code:
text = "Hello, World!"
@Lazy
A property wrapper which delays instantiation until first read access.
It is a reimplementation of Swift lazy modifier using a property wrapper.
@Lazy var result = expensiveOperation()
...
print(result) // expensiveOperation() is executed at this point
As an extra on top of lazy it offers reseting the wrapper to its “uninitialized” state.
@LazyConstant
Same as @Lazy + prevents changing or mutating its wrapped value.
@LazyConstant var result = expensiveOperation()
...
print(result) // expensiveOperation() is executed at this point
result = newResult // Compiler error
Note: This wrapper prevents reassigning the wrapped property value but NOT the wrapper itself. Reassigning the wrapper _value = LazyConstant(wrappedValue: "Hola!") is possible and since wrappers themselves need to be declared variable there is no way to prevent it.
@Trimmed
A wrapper that automatically trims strings both on initialization and reassignment.
@Trimmed
var text = " \n Hello, World! \n\n "
print(text) // "Hello, World!"
// By default trims white spaces and new lines, but it also supports any character set
@Trimmed(characterSet: .whitespaces)
var text = " \n Hello, World! \n\n "
print(text) // "\n Hello, World! \n\n"
@UndoRedo
A property wrapper that automatically stores history and supports undo and redo operations.
@UndoRedo var text = ""
text = "Hello"
text = "Hello, World!"
_text.canUndo // true
_text.undo() // text == "Hello"
_text.canRedo // true
_text.redo() // text == "Hello, World!"
You can check at any time if there is an undo or a redo stack using canUndo & canRedo
properties, which might be particularly usefull to enable/disable user interface buttons.
Type safe access to UserDefaults with support for default values.
@UserDefault("test", defaultValue: "Hello, World!")
var test: String
By default it uses the standard user defauls. You can pass any other instance of UserDefaults you want to use via its constructor, e.g. when you use app groups:
let userDefaults = UserDefaults(suiteName: "your.app.group")
@UserDefault("test", defaultValue: "Hello, World!", userDefaults: userDefaults)
var test: String
@Cached
TODO
@Dependency (Service locator pattern)
TODO
Thread safety
TODO
Command line parameters
TODO
Property observer -> willSet, didSet !
TODO: Reimplement
Print/Log
TODO: A property wrapper that prints/logs any value set.
A property wrapper is a mechanism to abstract property implementation patterns that come up repeatedly.
👉 Did you know: Property Wrappers were announced by Apple during WWDC 2019.
They are a fundamental component in SwiftUI syntax sugar hence Apple pushed them into the initial Swift 5.1 beta, skipping the normal Swift Evolution process.
This process continued after WWDC and it took 3 reviews to reach their final form on Xcode 11 beta 4.
🌯🌯 Burritos
A collection of well tested Swift Property Wrappers.
Requirements
Xcode 11 & Swift 5
Installation
Swift Package Manager
Xcode 11+ integration
MenuBar
→File
→Swift Packages
→Add Package Dependency...
https://github.com/guillermomuntaner/Burritos
and hit Next.Package.swift
If you already have a Package.swift or you are building your own package simply add a new dependency:
Cocoapods
Add Burritos to your Podfile:
Each wrapper is a submodule, so you add just the one(s) you want
@AtomicWrite
A property wrapper granting atomic write access to the wrapped property. Reading access is not atomic but is exclusive with write & mutate operations. Atomic mutation (read-modify-write) can be done using the wrapper
mutate
method.@Clamping
A property wrapper that automatically clamps its wrapped value in a range.
@Copying
A property wrapper arround
NSCopying
that copies the value both on initialization and reassignment. If you are tired of calling.copy() as! X
you will love this one.@DefaultValue
A property wrapper arround an implicitly unwrapped optional value which fallbacks to a given default value.
@DynamicUIColor
A property wrapper arround UIColor to support dark mode.
By default in iOS >= 13 it uses the new system wide user interface style trait and dynamic UIColor constructor to support dark mode without any extra effort. On prior iOS versions it defaults to light.
To support older iOS versions and custom logics (e.g. a switch in your app settings) the constructor can take an extra
style
closure that dynamically dictates which color to use. Returning anil
value results in the prior default behaviour. This logic allows easier backwards compatiblity by doing:Original idea courtesy of @bardonadam
@EnvironmentVariable
A property wrapper to set and get system environment variables values.
@Expirable
A property wrapper arround a value that can expire. Getting the value after given duration or expiration date will return nil.
Courtesy of @v_pradeilles
@LateInit
A reimplementation of Swift Implicitly Unwrapped Optional using a property wrapper.
@Lazy
A property wrapper which delays instantiation until first read access. It is a reimplementation of Swift
lazy
modifier using a property wrapper.As an extra on top of
lazy
it offers reseting the wrapper to its “uninitialized” state.@LazyConstant
Same as @Lazy + prevents changing or mutating its wrapped value.
Note: This wrapper prevents reassigning the wrapped property value but NOT the wrapper itself. Reassigning the wrapper
_value = LazyConstant(wrappedValue: "Hola!")
is possible and since wrappers themselves need to be declared variable there is no way to prevent it.@Trimmed
A wrapper that automatically trims strings both on initialization and reassignment.
@UndoRedo
A property wrapper that automatically stores history and supports undo and redo operations.
You can check at any time if there is an undo or a redo stack using
canUndo
&canRedo
properties, which might be particularly usefull to enable/disable user interface buttons.Original idea by @JeffHurray
@UserDefault
Type safe access to
UserDefaults
with support for default values.By default it uses the standard user defauls. You can pass any other instance of
UserDefaults
you want to use via its constructor, e.g. when you use app groups:@Cached
TODO
@Dependency (Service locator pattern)
TODO
Thread safety
TODO
Command line parameters
TODO
Property observer -> willSet, didSet !
TODO: Reimplement
Print/Log
TODO: A property wrapper that prints/logs any value set.
About Property Wrappers
Quoting the Property Wrappers Proposal description:
👉 Did you know: Property Wrappers were announced by Apple during WWDC 2019. They are a fundamental component in SwiftUI syntax sugar hence Apple pushed them into the initial Swift 5.1 beta, skipping the normal Swift Evolution process. This process continued after WWDC and it took 3 reviews to reach their final form on Xcode 11 beta 4.
Interesting reads:
Equivalents in other languages:
License
Burritos is released under the MIT license.