Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Property Wrappers

Avatar for shtnkgm shtnkgm
October 31, 2019

Property Wrappers

Property Wrappers in Swift

Avatar for shtnkgm

shtnkgm

October 31, 2019
Tweet

More Decks by shtnkgm

Other Decks in Programming

Transcript

  1. Property Wrappers A property wrapper adds a layer of separation

    between code that manages how a property is stored and the code that defines a property.
  2. !

  3. @autoclosure @escaping @convention @available @discardableResult @objc @nonobjc @objcMembers @GKInspectable @UIApplicationMain

    @NSApplicationMain @NSCopying @NSKeyedArchiverClassName @NSManaged @testable @IBAction @IBOutlet @IBDesignable @IBInspectable
  4. AttributeΛͭͬͯ͘ΈΔ // 12ҎԼΛอূ͢Δ @propertyWrapper struct TwelveOrLess { private var number

    = 0 // ඞਢ var wrappedValue: Int { get { return number } set { number = min(newValue, 12) } } }
  5. AttributeΛ͔ͭͬͯΈΔ struct SmallRectangle { @TwelveOrLess var height: Int @TwelveOrLess var

    width: Int } var rectangle = SmallRectangle() print(rectangle.height) // "0" rectangle.height = 13 print(rectangle.height) // "12"
  6. @propertyWrapper struct SmallNumber { private var maximum: Int private var

    number: Int var wrappedValue: Int { get { return number } set { number = min(newValue, maximum) } } init() { maximum = Int.max number = 0 } init(wrappedValue: Int) { maximum = Int.max number = min(wrappedValue, maximum) } init(wrappedValue: Int, maximum: Int) { self.maximum = maximum number = min(wrappedValue, maximum) } }
  7. ॳظ஋Λઃఆ͢Δ struct Rectangle { @SmallNumber var height: Int @SmallNumber(wrappedValue: 0,

    maximum: 10) var width: Int } var rectangle = Rectangle() rectangle.width = 20 print(rectangle.height, rectangle.width) // 0 10
  8. !

  9. @propertyWrapper struct SmallNumber { private var number = 0 var

    projectedValue = false var wrappedValue: Int { get { return number } set { if newValue > 12 { number = 12 projectedValue = true } else { number = newValue projectedValue = false } } } }
  10. Projected ValueΛࢀর͢Δʢ$ϓϩύςΟ ໊ʣ struct SomeStructure { @SmallNumber var someNumber: Int

    } var someStructure = SomeStructure() someStructure.someNumber = 4 print(someStructure.$someNumber) // Prints "false" someStructure.someNumber = 55 print(someStructure.$someNumber) // Prints "true"
  11. struct Response: Codable { var values: [Int] } let json

    = #"{ "values": [1, 2, null, 4, 5, null] }"#.data(using: .utf8)! let result = try JSONDecoder().decode(Response.self, from: json) // nil
  12. public struct Failable<Wrapped: Codable>: Codable { public let value: Wrapped?

    public init(from decoder: Decoder) throws { do { let container = try decoder.singleValueContainer() value = try container.decode(Wrapped.self) } catch { value = nil } } public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() if let value = self.value { try container.encode(value) } else { try container.encodeNil() } } }
  13. public struct Response: Codable { public let query: String public

    let items: [Item] public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.query = try container.decode(String.self, forKey: .query) self.items = try container.decode([Failable<Item>].self, forKey: .suggests).compactMap { $0.value } } public init(query: String, items: [Item]) { self.query = query self.items = items } }
  14. @propertyWrapper public struct LossyArray<T: Codable>: Codable { private struct AnyDecodableValue:

    Codable {} private struct LossyDecodableValue<Value: Codable>: Codable { let value: Value public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() value = try container.decode(Value.self) } } public var wrappedValue: [T] public init(wrappedValue: [T]) { self.wrappedValue = wrappedValue } public init(from decoder: Decoder) throws { var container = try decoder.unkeyedContainer() var elements: [T] = [] while !container.isAtEnd { do { let value = try container.decode(LossyDecodableValue<T>.self).value elements.append(value) } catch { _ = try? container.decode(AnyDecodableValue.self) } } self.wrappedValue = elements } public func encode(to encoder: Encoder) throws { try wrappedValue.encode(to: encoder) } }
  15. struct Response: Codable { @LossyArray var values: [Int] } let

    json = #"{ "values": [1, 2, null, 4, 5, null] }"#.data(using: .utf8)! let result = try JSONDecoder().decode(Response.self, from: json) print(result) // [1, 2, 4, 5]
  16. struct User: Codable { var isAdmin: Bool } let json

    = #"{ "isAdmin": null }"#.data(using: .utf8)! let user = try? JSONDecoder().decode(User.self, from: json) print(user) // nil
  17. @propertyWrapper public struct DefaultFalse: Codable, Equatable, Hashable { public var

    wrappedValue: Bool public init(wrappedValue: Bool) { self.wrappedValue = wrappedValue } public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() self.wrappedValue = (try? container.decode(Bool.self)) ?? false } public func encode(to encoder: Encoder) throws { try wrappedValue.encode(to: encoder) } }
  18. struct User: Codable { @DefaultFalse var isAdmin: Bool } let

    json = #"{ "isAdmin": null }"#.data(using: .utf8)! let user = try JSONDecoder().decode(User.self, from: json) print(user) // User(isAdmin: false)
  19. struct Response: Codable { var price: String var isDiscount: Bool

    } let json = #"{ "price": 12345, "isDiscount": "true" }"#.data(using: .utf8)! let result = try? JSONDecoder().decode(Response.self, from: json) // nil
  20. @propertyWrapper public struct LosslessValue<T: LosslessStringCodable>: Codable { private let type:

    LosslessStringCodable.Type public var wrappedValue: T public init(from decoder: Decoder) throws { do { self.wrappedValue = try T.init(from: decoder) self.type = T.self } catch let error { func decode<T: LosslessStringCodable>(_: T.Type) -> (Decoder) -> LosslessStringCodable? { return { try? T.init(from: $0) } } let types: [(Decoder) -> LosslessStringCodable?] = [ decode(String.self),decode(Bool.self),decode(Int.self), decode(Int8.self),decode(Int16.self),decode(Int64.self), decode(UInt.self),decode(UInt8.self),decode(UInt16.self), decode(UInt64.self),decode(Double.self),decode(Float.self), ] guard let rawValue = types.lazy.compactMap({ $0(decoder) }).first, let value = T.init("\(rawValue)") else { throw error } self.wrappedValue = value self.type = Swift.type(of: rawValue) } } public func encode(to encoder: Encoder) throws { let string = String(describing: wrappedValue) guard let original = type.init(string) else { let description = "Unable to encode '\(wrappedValue)' back to source type '\(type)'" throw EncodingError.invalidValue(string, .init(codingPath: [], debugDescription: description)) } try original.encode(to: encoder) } }
  21. struct Response: Codable { @LosslessValue var price: String @LosslessValue var

    isDiscount: Bool } let json = #"{ "price": 12345, "isDiscount": "true" }"#.data(using: .utf8)! let result = try? JSONDecoder().decode(Response.self, from: json) print(result) // Response(price: "12355", isDiscount: true)
  22. struct ContentView : View { @State var value: Bool =

    false var body: some View { Toggle(isOn: $value) { Text("The value is " + "\($value)") } } }