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

Property Wrappers

shtnkgm
October 31, 2019

Property Wrappers

Property Wrappers in Swift

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