Wrapped property with #propertyWrapper won't return wrapped type - swift5

In this simplified example, I make a propertyWrapper of an UInt to hold a Natural Number (an Integer > 0). My own example uses a more complex filter but this shows the issue. Rather than find a workaround, the point of the question is to shed light on the confusing (to me) error.
Assigning it to a simple UInt brings the error message listed.
Using its wrappedValue property as in the following line works fine. But surely the whole point of the wrapping is to be able to treat it as an UInt as returned by the get?
The error "cannot assign value of type 'NonZero' to type 'UInt" appears to undermine the whole point of the wrapper type. What am I misunderstanding?
Xcode 11.0
import Foundation
#propertyWrapper
struct NonZero {
private let myNumber : UInt
init(n : UInt)
{
if ( n == 0){fatalError(" cannot be 0")}
myNumber = n
}
var wrappedValue: UInt {
get { return myNumber }
}
}
struct Nums {
var num :UInt
init( nz: NonZero){
num = nz //error message "cannot assign value of type 'NonZero' to type 'UInt"
num = nz.wrappedValue //no error
}
}

This is not how property wrappers work. The code:
init(nz: NonZero)
declares nz to have the type NonZero – which is just the struct defined earlier, NonZero here is not being used as a wrapped property.
A wrapped property is used as an attribute on a property declaration, for example:
#NonZero var num : UInt = 1
[Putting that in the code requires changing the parameter label of NonZero's init to be changed to wrappedValue, e.g.:
init(wrappedValue : Uint) { ... wrappedValue ... }
]
You could write the init of struct Nums as:
init(nz: UInt)
{
num = nz // ok *after* you add a `set` to `NonZero`, will `fatalError` if `nz` is zero
let unwrapped : UInt = num // ok
print(unwrapped) // so you see it worked
}
Keep exploring! You might find SE-0259 Property Wrappers (Apple) and Swift Property Wrappers (NSHipster) useful.
HTH

Related

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")!
}
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
}

A Constant Array's Items Can(Cannot) Be Modified If They Are Reference(Value) Types. Why?

Please have a look at the following code and note the compiler error on the last line:
class C {
var value: Int
init( _ value: Int) { self.value = value }
}
let array1 = [C(1), C(2), C(3)]
array1[0].value = 4
struct S {
var value: Int
}
let array2 = [S(value: 1), S(value: 2), S(value: 3)]
array2[0].value = 4 // Error: Cannot assign to property: 'array2' is a 'let' constant
From the compiler error I want to conclude that the item at index 0 is being read from array2, modified, and then written back to array2. What else could produce the result that there is an attempt to modify array2? But, if my reasoning is correct, then why does the same thing not happen with array1?
Classes Are Reference Types
Unlike value types(struct), reference types are not copied when they are assigned to a variable or constant, or when they are passed to a function. Rather than a copy, a reference to the same existing instance is used instead.
Please refer Apple documentation about Class & struct

Swift 3: Convert Array<Double> to Array<Float> extension

extension Array where Element : Double {
public var asArrayOfFloat: [Float] {
return self.map { return Float(other:$0) } // compiler error
}
}
I get a compiler error complaining about Float(other:$0) "Argument labels '(other:)' do not match any available overloads." But, $0 is a Double, and there is a Float.init(other:Double) initializer. What's the problem?
EDIT: Changing to Float($0) creates a different compilation error: "Ambiguous use of 'init'", and has 16 candidates.
EDIT: Float.init(other:Double) originally suggested by compiler, snapshot:
The issue was with where Element : Double ... This needs to be rewritten as where Element == Double (notice the use of == instead of :) because Double is not a protocol but a type. Now compilation works with Float($0) as suggested.
Get rid of the other: label. If there is an init override that uses that label (FWIW, I don't see one), then it's not a required label.

Could not cast value of type 'Swift.UInt32' to 'Swift.Int'

In Tests project I've got extensions with some test helper functions. Like this:
extension Employee {
static func mockDict() -> Dictionary<String, Any>! {
return ["ID": arc4random() % 1000,
"FirstName": "Employee First Name",
...]
}
}
(I've stripped unnecessary code). I've got problem accessing ID from this dictionary for some yet unknown reason. I've got SIGABRT 6 when casting
employeeDict["ID"] as! Int
Xcode debugger console also don't like this particular integer:
Strings work fine. Have you encountered such problem? Any ideas?
EDIT: Just in case anyone will encounter this problem too. CASTING FROM UInt32/Int32 TO Int FAILS BY DESIGN. Even if object was casted into Any or Anyobject inbetween.
Even though
#available(*, message: "Converting UInt32 to Int will always succeed.")
public init?(exactly value: UInt32)
in Int's declaration
public struct Int : SignedInteger, Comparable, Equatable {
...
}
and
public struct Int32 : SignedInteger, Comparable, Equatable {
...
}
EDIT 2 for those who might encounter this behaviour in JSON serialization. Yes, serialization fails with error NSInvalidArgumentException Invalid type in JSON write (_SwiftValue) if asked to serialize UInt32, Int64 or any Integer protocol instance other than Int
Try this:
let a = employeeDict["ID"] as! UInt32
let number = Int(a)
Now you can use number to perform any action.
This works for me:
Int("\(employeeDict["ID"]!)")
Swift "primitive" numeric types are not interchangeable and cannot be cast to each other.
You need to use an initializer.
Since arcRandom() returns UInt32 and you want to use the value as Int, convert it right away in the dictionary declaration:
["ID": Int(arc4random() % 1000), ...
PS: Do not declare a clearly non-optional as implicit unwrapped optional return value, that defeats the strong type system of Swift.
static func mockDict() -> Dictionary<String, Any>

Adding value to Swift dictionary with enum key

I defined Dictionary in Swift, with enum as the key and struct as value. At runtime I want to add value to the dictionary for a given enum key, however I get the following error:
'#lvalue $T9' is not identical to '(MyEnum, MyData)'
enum MyEnum {
case A, B, C
}
struct MyData {
var x : Int
var y : Int
init(x:Int, y: Int) {
self.x = x
self.y = y
}
}
class Tester {
let myDictionary = [MyEnum : MyData]()
func dummy() {
self.myDictionary[MyEnum.A] = MyData(x: 1, y: 2) // <-- error in this line
}
}
Any idea how to do it properly ?
The problem is that you’ve declared myDictionary with let rather than var. Switch it to var and your code will work as expected.
Dictionaries are structs, which are a “value” type. That means when you declare them with let, they are frozen forever with the value they were assigned. You can’t call methods that change their value, including assigning to them via a subscript ([ ]).
Unfortunately the error message isn’t super-helpful. Ideally it would read something like “attempt to modify immutable value myDictionary”.
Don’t let this put you off using let though – it’s a great practice to use let by default unless you know you need to change a value. But in this instance you do, so you need to use var.