propertyWrapper
在Swift语法开发时,会将我们需要的逻辑层封装于函数中,除此之外,对于属性中存在逻辑,也可以进行封装,Swift5.1引入了一种特定的方式,用于存放我们对于属性的各种想法,也就是属性包装器。它封装了对属性的读写访问,也可以对当前属性新增其他行为。并且,由于包装器的通用效果,使它可以复用于多个属性。(如果还需要增加它的应用范围,可以使用泛型+属性包装器的方式)
Example
问题:解决一个购买商品的货量限制问题,数量下限为1,上限为99
应用:钳制一个值的实际范围
@propertyWrapper
struct Clamping<Value: Comparable> {
var value: Value?
let range: ClosedRange<Value>
init(wrappedValue value: Value? = nil, _ range: ClosedRange<Value>) {
// 先决条件: 限制范围内
//precondition(range.contains(value))
self.value = value
self.range = range
}
var wrappedValue: Value? {
get {
guard let value = value else {
return range.lowerBound
}
return max(min(value, range.upperBound), range.lowerBound)
}
set {
value = newValue
}
}
}
调用
@Clamping(1...99) var goodsNumberValue: Int!
正则转换
@propertyWrapper
struct Validation<Value: StringProtocol> {
var value: Value?
var conditions: MatchesType
init(wrappedValue value: Value? = nil, conditions: MatchesType) {
self.value = value
self.conditions = conditions
}
var wrappedValue: Value? {
get {
return matches(value: value, conditions: conditions) ? value : nil
}
set {
value = newValue
}
}
func matches(value: Value?, conditions: MatchesType) -> Bool {
guard let value = value else {
return false
}
// NSPredicate: 限制过滤条件 (包括: 比较字符串 比较运算 逻辑运算等)
let pre = NSPredicate(format: "SELF MATCHES %@", conditions.string)
return pre.evaluate(with: value)
}
}
public enum MatchesType {
case phone
case custom(String)
var string: String {
switch self {
case .phone:
return "^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\\d{8}$"
case .custom(let string):
return string
}
}
}
调用
@Validation(conditions:.phone) var phone: String?
Explain#
在使用propertyWrapper时,需要注意以下问题:
1.在创建一个@propertyWrapper时,需要有一个必要值wrappedValue。它对应我们外部属性包装器包装的属性值,且类型保持一致。
2.当我们需要初始值,需要在init方法中进行初始化赋值
3.可以使用struct、enum、class定义
4.propertyWrapper是可以嵌套且共存的