Property Wrapper initialising and making _ variable accessible

Overview:

I have a Property Wrapper Coordinates. In the class Car I am using the Coordinates Property Wrapper.

Questions:

  1. How can I initialise Coordinates using Car's initialiser ?
  2. How to make _coordinates accessible ? (I am not sure what is responsible for _coordinates)
  3. Is there any documentation on the above ? Most examples I saw were using struct where the compiler generates / synthesises initialisers for the properties.

Note: I have added comments with the compilation errors that I am getting while attempting to do the above.

Code:

@propertyWrapper
struct Coordinates {
    
    private var point : CGPoint

    var wrappedValue : (Double, Double) {
        
        get {
            return (Double(point.x), Double(point.y))
        }
        
        set {
            
            point = .init(x:newValue.0, y:newValue.1)
        }
    }
    
    var value : CGPoint {
        return point
    }
    
    init(_ tuple: (Double, Double) = (0, 0)) {
        self.point = .init(x: tuple.0, y: tuple.1)
    }
}


class Car {
    @Coordinates var coordinates : (Double, Double)
    
    init(tuple: (Double, Double)) {
        //Compilation Error: 'self' used in property access 'coordinates' before all stored properties are initialized
        self.coordinates = tuple
    }
}

let car1 = Car(tuple: (10.22, 20.33))

//Compilation Error: _coordinates' is inaccessible due to 'private' protection level
print(car1._coordinates)

How can I initialise Coordinates using Car 's initialiser ?

init(tuple: (Double, Double)) {
  self._coordinates = _Coordinates(tuple)
}

How to make _coordinates accessible ? (I am not sure what is responsible for _coordinates)

Accessible from where? Currently it's not possible to access the property wrapper outside the type because it automatically is a private stored property.
You could however write a different property and expose the wrapper.

var exposedCoordinates: Coordinates {
  _coordinates
}

Is there any documentation on the above ? Most examples I saw were using struct where the compiler generates / synthesises initialisers for the properties.

The best documentation is the proposal itself: https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md


Alternative solution, just make everything manually!

class Car {
  // FIXME: Change to `internal(wrapper) @Coordinates` when possible
  internal var _coordinates: Coordinates
  var coordinates: (Double, Double) {
    get {
      _coordinates.wrappedValue
    }
    set {
      _coordinates.wrappedValue = newValue
    }
  }

  init(tuple: (Double, Double)) {
    self._coordinates = Coordinates(tuple)
  }
}
1 Like

@DevAndArtist Thank you so much for the detailed explanation and reference link.

I suppose the reason for the initialisation compilation error was because I was trying to set a computed property (wrappedValue) in the initialiser before self.point was set.

The following code from the proposal documentation explains the use of _ variable:

@Lazy var foo = 1738 translates to:

private var _foo: Lazy<Int> = Lazy<Int>(wrappedValue: 1738)
var foo: Int {
  get { return _foo.wrappedValue }
  set { _foo.wrappedValue = newValue }
}

There is a small typo in the initialiser _Coordinates(tuple), I suppose it was Coordinates(tuple)

1 Like

@DevAndArtist

Just wondering if it would be possible for the compiler to infer the data type while declaring a property that uses a non-generic property wrapper.

Example 1 (works ok):

//(Double, Double) has to match the data type of the `wrappedValue`
@Coordinates var coordinates : (Double, Double)

Example 2 (compilation error):

//Compilation Error: 
//Property type 'Int' does not match that of the 'wrappedValue' property of its wrapper type 'Coordinate'
@Coordinate var coordinates : Int

Based on the compilation error message, it seems like the compiler is expecting wrappedValue's data type.

It could, in theory, though I am not sure I would like to see

@Wrapper var x 

in a type definition.

1 Like

Thanks @Avi for the explanation.

I just realised it is possible to do the following:

@Coordinates((0, 0)) var coordinates

As this is in the Using Swift category ... in this particular case Iā€™m not really sure why are you using a property wrapper at all? It might be worth taking a moment to double-check this is what you want.

I think the code would be cleaner and clearer if Coordinates was just a simple struct with two properties, or you could consider using CGPoint directly.

1 Like

Thanks @bzamayo, you are right, may be the example wasn't good.

I am learning to use property wrapper, so thought I would test it with an example.

1 Like