一组经过良好测试的 Swift 属性包装器。
Xcode 11 & Swift 5
MenuBar
→ File
→ Swift Packages
→ Add Package Dependency...
https://github.com/guillermomuntaner/Burritos
并点击 Next。如果你已经有 Package.swift 或者你正在构建你自己的包,只需添加一个新的依赖项
dependencies: [
.package(url: "https://github.com/guillermomuntaner/Burritos", from: "0.0.3")
]
将 Burritos 添加到你的 Podfile
pod 'Burritos', '~> 0.0.3'
每个包装器都是一个子模块,所以你只需要添加你想要的那个(些)
pod 'Burritos/Copying', '~> 0.0.3'
pod 'Burritos/UndoRedo', '~> 0.0.3'
pod 'Burritos/UserDefault', '~> 0.0.3'
一个属性包装器,授予对包装属性的原子写入访问权限。 读取访问权限不是原子的,但与写入和 mutate 操作是互斥的。 可以使用包装器的 mutate
方法完成原子修改 (read-modify-write)。
@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(range: 0...1)
var alpha: Double = 0.0
alpha = 2.5
print(alpha) // 1.0
alpha = -1.0
print(alpha) // 0.0
一个 NSCopying
的属性包装器,在初始化和重新赋值时都会复制该值。 如果你厌倦了调用 .copy() as! X
,你会喜欢这个。
@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(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
一个围绕 UIColor 的属性包装器,以支持暗黑模式。
默认情况下,在 iOS >= 13 中,它使用新的系统级用户界面样式特征和动态 UIColor 构造函数来支持暗黑模式,而无需任何额外的努力。 在之前的 iOS 版本中,它默认为浅色。
@DynamicUIColor(light: .white, dark: .black)
var backgroundColor: UIColor
// The color will automatically update when traits change
view.backgroundColor = backgroundColor
为了支持较旧的 iOS 版本和自定义逻辑(例如,应用程序设置中的开关),构造函数可以采用额外的 style
闭包,该闭包动态地指示要使用哪种颜色。 返回 nil
值会导致之前的默认行为。 此逻辑通过执行以下操作可以更轻松地实现向后兼容性
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
}
}
最初的想法由 @bardonadam 贡献
一个用于设置和获取系统环境变量值的属性包装器。
@EnvironmentVariable(name: "PATH")
var path: String?
// You can set the environment variable directly:
path = "~/opt/bin:" + path!
一个围绕可以过期值的属性包装器。 在给定的持续时间或到期日期之后获取该值将返回 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)
使用属性包装器重新实现的 Swift 隐式解包可选类型。
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!"
一个延迟实例化直到第一次读取访问的属性包装器。 它是使用属性包装器重新实现的 Swift lazy
修饰符。
@Lazy var result = expensiveOperation()
...
print(result) // expensiveOperation() is executed at this point
作为 lazy
的额外补充,它提供了将包装器重置为其“未初始化”状态的功能。
与 @Lazy 相同 + 阻止更改或修改其包装的值。
@LazyConstant var result = expensiveOperation()
...
print(result) // expensiveOperation() is executed at this point
result = newResult // Compiler error
注意: 此包装器阻止重新分配包装的属性值,但不阻止重新分配包装器本身。 重新分配包装器 _value = LazyConstant(wrappedValue: "Hola!")
是可能的,并且由于包装器本身需要声明为变量,因此无法阻止它。
一个在初始化和重新赋值时自动修剪字符串的包装器。
@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 var text = ""
text = "Hello"
text = "Hello, World!"
_text.canUndo // true
_text.undo() // text == "Hello"
_text.canRedo // true
_text.redo() // text == "Hello, World!"
你可以随时使用 canUndo
& canRedo
属性检查是否存在撤消或重做堆栈,这对于启用/禁用用户界面按钮特别有用。
最初的想法由 @JeffHurray 提供
类型安全地访问 UserDefaults
,并支持默认值。
@UserDefault("test", defaultValue: "Hello, World!")
var test: String
默认情况下,它使用标准的用户默认值。 你可以通过其构造函数传递你想要使用的 UserDefaults
的任何其他实例,例如,当你使用应用程序组时
let userDefaults = UserDefaults(suiteName: "your.app.group")
@UserDefault("test", defaultValue: "Hello, World!", userDefaults: userDefaults)
var test: String
待办事项
待办事项
待办事项
待办事项
TODO: 重新实现
TODO: 一个属性包装器,用于打印/记录任何设置的值。
引用 属性包装器提案 的描述
属性包装器是一种抽象重复出现的属性实现模式的机制。
👉 你知道吗:属性包装器由 Apple 在 WWDC 2019 期间宣布。 它们是 SwiftUI 语法糖的一个基本组成部分,因此 Apple 将它们推入了最初的 Swift 5.1 beta 版,跳过了正常的 Swift Evolution 过程。 在 WWDC 之后,这个过程仍在继续,并且经过 3 次审查才在 Xcode 11 beta 4 上达到最终形式。
有趣的读物
其他语言中的等价物
Burritos 在 MIT 许可 下发布。