Swift-将模型暂时切换为只读

问题描述

我的电话号码模型如下:

import UIKit
import Foundation

struct PhoneValidation : OptionSet {
    let rawValue: Int
    
    static let phoneInValid = PhoneValidation(rawValue: 1 << 0)
    static let phoneValid = PhoneValidation(rawValue: 1 << 1)
    static let smsValidationAttempted = PhoneValidation(rawValue: 1 << 2)
    static let smsValidationFailed = PhoneValidation(rawValue: 1 << 3)
    static let smsValidationSuccessful = PhoneValidation(rawValue: 1 << 4)      // OTP is successfully validated in backend. The field should be non-editable in this duration
    static let smsValidationOTPTriggered = PhoneValidation(rawValue: 1 << 5)    // OTP validation triggered. The field should be non-editable in this duration
}

class PhonesViewModel: NSCopying {

    public var phoneType: PhoneNumberType = PhoneNumberType.mobile
    
    public var phone: String?
    
    public var code: String?
    
    public var countryCode: String?
    
    public var isValid : PhoneValidation?
    
    func copy(with zone: NSZone? = nil) -> Any {
        let copy = PhonesViewModel()
        copy.phoneType = phoneType
        copy.phone = phone
        copy.code = code
        copy.countryCode = countryCode
        copy.isValid = isValid
        return copy
    }
}

如上所示,电话型号可以在不同状态之间转换。 SMS验证仅在少数国家/地区可用,并且在少数国家/地区不适用。因此,我计划在SMS验证适用于某个国家或正在进行验证时设置smsValidationOTPTriggered状态。

我这里需要的是,设置状态smsValidationOTPTriggeredsmsValidationSuccessful时,我不希望应用程序的任何模块修改模型的值(phoneType,phone,代码,countryCode) 。换句话说,我希望在模型中设置这两个状态时模型切换到只读模式,并希望在尝试修改时以错误或异常通知模块。

我在这里想要达到的最佳实践已经存在吗?在提出这个问题之前,我已经进行了搜索,但没有找到任何问题。我该如何实现?

谢谢, 拉吉·帕万·冈达尔

解决方法

这样的事情怎么样,我认为最好为您的情况使用属性包装器!以下不是确切的解决方案,但可以修改/更改以满足您的需求 导入UIKit

 enum PhoneNumberType {
    case mobile
}

enum PhoneValidation {
    case phoneInValid
    case phoneValid
    case smsValidationAttempted
    case smsValidationFailed
    case smsValidationSuccessful
    case smsValidationOTPTriggered
}

struct PhonesViewModel {
    public var phoneType: PhoneNumberType = PhoneNumberType.mobile
    public var phone: String?
    public var code: String?
    public var countryCode: String?
    public var phoneValidation : PhoneValidation?
    
    func validate(value: [PhoneValidation]) -> Bool {
        //add proper check here
        return false 
    }
}


@propertyWrapper
struct Wrapper {
    private(set) var value: PhonesViewModel? = nil
    var validators: [PhoneValidation] = []
    
    var wrappedValue: PhonesViewModel? {
        get { value }
        set {
            if let model = newValue,model.validate(value: validators) {
                value = newValue
                print("Value assigned")
            } else {
                print("Value not assigned")
            }
        }
    }
}

struct SomeOtherClass {
    @Wrapper(validators: [PhoneValidation.phoneInValid])
    var model: PhonesViewModel?
}

var a = SomeOtherClass()
a.model = PhonesViewModel()
a.model = PhonesViewModel()
,

您可以使用名称为“冰棍不可变性”的技术。对象最初是可变的,但可以被“冻结”。禁止修改冻结的对象。在您的情况下,当PhonesViewModel属性的值为isValidsmsValidationOTPTriggered时,smsValidationSuccessful将被冻结。

让我们添加Freezable协议来满足对对象的需求,这些对象可能变得不可变并符合PhonesViewModel

protocol Freezable: class {
    var isFrozen: Bool { get }
}

extension PhonesViewModel: Freezable {
    var isFrozen: Bool {
        isValid == .smsValidationOTPTriggered || isValid == .smsValidationSuccessful
    }
}

现在,分配属性后,我们必须为isFrozen值添加验证。可以将其添加到属性观察器中,例如:

...
public var phone: String? {
    didSet {
        validate()
    }
}
...
private func validate() {
    assert(!isFrozen)
}

或使用属性包装器:

@propertyWrapper
struct Guarded<Value> {
    private var value: Value

    init(wrappedValue: Value) {
        value = wrappedValue
    }

    @available(*,unavailable)
    var wrappedValue: Value {
        get { fatalError("only works on instance properties of classes that conforms to Freezable protocol") }
        set { fatalError("only works on instance properties of classes that conforms to Freezable protocol") }
    }

    static subscript<EnclosingSelf: Freezable>(
        _enclosingInstance object: EnclosingSelf,wrapped wrappedKeyPath: ReferenceWritableKeyPath<EnclosingSelf,Value>,storage storageKeyPath: ReferenceWritableKeyPath<EnclosingSelf,Self>
    ) -> Value {
        get {
            object[keyPath: storageKeyPath].value
        }
        set {
            precondition(!object.isFrozen,"Object \(object) is frozen! Modifications are forbidden")
            object[keyPath: storageKeyPath].value = newValue
        }
    }
}

因此您的班级将如下所示:

class PhonesViewModel: NSCopying {

    @Guarded
    public var phoneType: PhoneNumberType = PhoneNumberType.mobile

    @Guarded
    public var phone: String?

    @Guarded
    public var code: String?

    @Guarded
    public var countryCode: String?

    @Guarded
    public var isValid : PhoneValidation?

    func copy(with zone: NSZone? = nil) -> Any {
        let copy = PhonesViewModel()
        copy.phoneType = phoneType
        copy.phone = phone
        copy.code = code
        copy.countryCode = countryCode
        copy.isValid = isValid
        return copy
    }
}

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...