Slide 1

Slide 1 text

Property Wrappers From IDEA to INDUCTION G ABHISEK Product Engineer @ GoJek

Slide 2

Slide 2 text

Scope of Discussion • What are Property wrappers • Motivation behind Property wrappers • Glimpse on the evolution • Some uses of Property wrappers • Frameworks where they are used today • Future proposals

Slide 3

Slide 3 text

Property Wrapper? • Property Wrapper is a generic data structure that wraps a property and specifies its access patterns. • Intended to remove boiler plate code that we write for custom getters and setters.

Slide 4

Slide 4 text

Story behind Property Wrappers

Slide 5

Slide 5 text

Proposal • Proposed as Property Behaviours. • Remove boiler plate code written for access patterns. • Remove inconsistencies of @NSCopying. • Aimed at addressing problem of repeated property patterns like lazy. Dec, 2015

Slide 6

Slide 6 text

Our code Third party Frameworks and Libraries Standard Library Language Compiler

Slide 7

Slide 7 text

• Proposal rejected sighting immaturity of Swift. • Swift was changing rapidly with changes in core APIs. Feb, 2016

Slide 8

Slide 8 text

• Pitch #2 was submitted in March 2019. • Inspired and refined with inspiration from Delegated Properties in Kotlin. • Known as Property Delegates. • Pitch #3 was submitted in May 2019. • Initializer renamed. • Name was changed to Property Wrappers. • Syntax refined.

Slide 9

Slide 9 text

Syntax @propertyWrapper struct PropertywrapperName { var projectedValue = Something // Additional API that can exposed additional data var wrappedValue: T { get { // Custom logic for getter } set { // Custom logic for setter } } init(wrappedValue: T) {...} init(wrappedValue: T = Initial Value, additional params) {...} }

Slide 10

Slide 10 text

Properties Setter/Getter Wrapper Structure

Slide 11

Slide 11 text

• Minimum Value • Current value • Flexi interest Current value = current value + (interest)% * current value Growth = (current value > min value) ? Appreciating : depreciating

Slide 12

Slide 12 text

struct WalmartFoodBond { var bondValue: Int { set { bondCurrentWorth = newValue + (newValue * flexiInterest/100) growth = bondCurrentWorth < minimumValue ? .depriciating : .appreciating } get { bondCurrentWorth } } private (set ) var bondName: String private (set ) var growth: BondGrowth = .appreciating private var bondCurrentWorth: Int private var flexiInterest: Int private var minimumValue: Int init(bondName: String, minimumValue: Int, flexiInterest: Int, value: Int) { self.bondName = bondName self.minimumValue = minimumValue self.bondCurrentWorth = value self.flexiInterest = flexiInterest self.wrappedValue = value } }

Slide 13

Slide 13 text

@propertyWrapper struct BondValue { private var bondCurrentWorth: Int private var minimumValue: Int private var flexiInterest: Int var projectedValue: BondGrowth = .appreciating // The projected value projects the growth for the bond var wrappedValue: Int { get { return bondCurrentWorth } set { bondCurrentWorth = newValue + (newValue * flexiInterest/100) projectedValue = bondCurrentWorth < minimumValue ? .depriciating : .appreciating } } init(wrappedValue: Int = 0, minimumValue: Int, flexiInterest: Int) { self.minimumValue = minimumValue self.flexiInterest = flexiInterest self.bondCurrentWorth = wrappedValue } }

Slide 14

Slide 14 text

struct WalmartFoodBond { var bondName: String @BondValue(minimumValue: 0, flexiInterest: 0, value: 0) var bondValue = 0 init(bondName: String, minimumValue: Int, flexiInterest: Int, value: Int) { self.bondName = bondName _bondValue = BondValue(minimumValue: minimumValue, flexiInterest: flexiInterest) } } struct WalmartCommerceBond { var bondName: String @BondValue(minimumValue: 0, flexiInterest: 0, value: 0) var bondValue = 0 init(bondName: String, minimumValue: Int, flexiInterest: Int, value: Int) { self.bondName = bondName _bondValue = BondValue(minimumValue: minimumValue, flexiInterest: flexiInterest) } }

Slide 15

Slide 15 text

var foodBond = WalmartFoodBond(bondName: "Walmart Food Bond", minimumValue: 200, flexiInterest: 10, value: 200) print("Walmart Food Bond current worth - \(foodBond.bondValue)") print("Walmart Food Bond growth - \(foodBond.$bondValue)”) var commerceBond = WalmartCommerceBond(bondName: "Walmart Growth Bond", minimumValue: 200, flexiInterest: 10, value: 200) print("Walmart Commerce Bond current worth - \(commerceBond.bondValue)") print("Walmart Commerce Bond growth - \(commerceBond.$bondValue)”) commerceBond.bondValue = 50 print("Walmart Commerce Bond final growth - \(commerceBond.$bondValue)”) OUTPUT: Walmart Food Bond current worth - 220 Walmart Food Bond growth - appreciating Walmart Commerce Bond current worth - 220 Walmart Commerce Bond growth - appreciating Walmart Commerce Bond final growth - depriciating

Slide 16

Slide 16 text

Use cases of Property Wrappers • Accessing user default values. • Implementing custom lazy and NSCopying wrappers. • Guaranteeing access of properties from a particular thread. • Lots many ……

Slide 17

Slide 17 text

Current uses in Swift • Swift UI • Combine

Slide 18

Slide 18 text

Swift UI • @State • @Binding Combine • @Published • @ObservedObject • @Environment

Slide 19

Slide 19 text

Alternate Proposals • Single protocol 
 protocol PropertyWrapper { associatedtype Value var wrappedValue: Value { get } } • Kotlin like by- syntax. • Alternative spellings for the $projection property.

Slide 20

Slide 20 text

Cavets • Code base can be messy with lot of “@“ syntaxes. • “@“ is not a Swifty convention. • Not a developer friendly readable format.

Slide 21

Slide 21 text

Future Proposals • Finer grained access control
 @SomeWrapper public public(storage) private(projection) var foo: Int = 1738 • Referencing the enclosing `self` in a wrapper type. • Delegating to an existing property.

Slide 22

Slide 22 text

Thank you