swiftpropertieswrapper

Missing argument for parameter 'initialValue' in property wrapper initializer; add 'wrappedValue' and 'initialValue' arguments in


I'd like to create a property wrapper to accommodate the well known precision issues. However, when I use the @PropertyWrapper as I understand it to be used and demonstrated here, I get the following errors:

Extra argument in call
Missing argument for parameter 'initialValue' in property wrapper initializer; add 'wrappedValue' and 'initialValue' arguments in '@MantissaClamping(...)'

I don't see how I have an "extra argument in call," I assign the decimal as the wrapped value, and I provide the integer literal as the mantissa argument.

After saying I have an extra argument, the other error says I'm missing an argument. And suggesting I think suggesting that I literally add them as arguments to the property wrapper...That would defeat the whole purpose of property wrappers in my eyes, because it would require redundant code like this...But even this doesn't work.

struct MantissaClampTestStruct {
    @MantissaClamping(Decimal("0.000000000001")!, 14) var small: Decimal = Decimal("0.000000000001")!
}

How can I assign a literal value to the property, and let that apply to the property wrapper? While providing the int value that directly applies to the property wrapper?

Here is my reproducible code you can put in a playground.

extension Decimal {
    /// Rounds a value
    /// - Parameters:
    ///   - roundingMode: up down plain or bankers
    ///   - scale: the number of digits result can have after its decimal point
    /// - Returns: the rounded number
    func rounded(_ roundingMode: NSDecimalNumber.RoundingMode = .bankers, scale: Int = 0) -> Self {
        var result = Self()
        var number = self
        NSDecimalRound(&result, &number, scale, roundingMode)
        return result
    }
}

@propertyWrapper
struct MantissaClamping {
    var value: Decimal
    let mantissaCount: Int
    
    init(initialValue value: Decimal, _ mantissaCount: Int) {
        precondition(mantissaCount < 19 && mantissaCount >= 0)
        self.value = value
        self.mantissaCount = mantissaCount
    }
    
    var wrappedValue: Decimal {
        get { value }
        set { value = newValue.rounded(.down, scale: mantissaCount)}
    }
}

struct MantissaClampTestStruct {
    @MantissaClamping(14) var small: Decimal = Decimal("0.000000000001")!
}

Solution

  • According to the docs:

    When you include property wrapper arguments, you can also specify an initial value using assignment. Swift treats the assignment like a wrappedValue argument and uses the initializer that accepts the arguments you include.

    So it translates your property declaration into something like:

    var small = MantissaClamping(wrappedValue: Decimal("0.000000000001")!, 14)
    

    Obviously, this doesn't match any of your initialisers.

    Just rename the parameter label to wrappedValue:

    init(wrappedValue value: Decimal, _ mantissaCount: Int) {
    

    And also add the string: label to the Decimal initialiser, which you have missed:

    @MantissaClamping(14) var small: Decimal = Decimal(string: "0.000000000001")!
    

    You might also want to round the initial value too:

    init(wrappedValue value: Decimal, _ mantissaCount: Int) {
        precondition(mantissaCount < 19 && mantissaCount >= 0)
    
        // here
        self.value = value.rounded(.down, scale: mantissaCount)
        self.mantissaCount = mantissaCount
    }