Property Wrappers

E45f9c343d90c74554c65c89c6f861bc?s=47 shtnkgm
October 31, 2019

Property Wrappers

Property Wrappers in Swift

E45f9c343d90c74554c65c89c6f861bc?s=128

shtnkgm

October 31, 2019
Tweet

Transcript

  1. Property Wrappers @shtnkgm

  2. Swift5.1ʢXcode11.0ʣ͔Β৽ػೳ Property Wrappers͕࢖͑Δʂ

  3. DocumentΛΈͯΈΔ https://docs.swift.org/swift-book/LanguageGuide/Properties.html#ID617

  4. 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.
  5. !

  6. Property Wrappersͱ͸ • ϓϩύςΟʹڞ௨ػೳΛ௥ՃͰ͖Δ • ϓϩύςΟ΁ͷΞΫηεํ๏ʹؔ͢ΔAttributeΛͭ͘ΔͨΊͷ Attribute

  7. Attribute?

  8. @͕͍ͭͯΔ͜Μͳ΍ͭ

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

    @NSApplicationMain @NSCopying @NSKeyedArchiverClassName @NSManaged @testable @IBAction @IBOutlet @IBDesignable @IBInspectable
  10. @ʙͱهड़͢Δ͜ͱͰɺϓϩύςΟ ΍ܕͷએݴʹิ଍৘ใΛઃఆͰ͖Δ ػೳ

  11. None
  12. None
  13. Swift5.1 @functionBuilder @dynamicCallable @dynamicMemberLookup @inlinable @requiresstoredpropertyinits @usableFromInline @warnunqualifiedaccess

  14. @propertyWrapper

  15. ϓϩύςΟͷ஋ʹΞΫηε͢Δํ๏ ΛAttributeͱͯ͠ڞ௨ͰఆٛͰ͖ Δʂ

  16. AttributeΛͭͬͯ͘ΈΔ // 12ҎԼΛอূ͢Δ @propertyWrapper struct TwelveOrLess { private var number

    = 0 // ඞਢ var wrappedValue: Int { get { return number } set { number = min(newValue, 12) } } }
  17. 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"
  18. 12Ҏ֎ʹ΋ରԠͰ͖ΔΑ͏ʹ֦ு ͍ͨ͠

  19. Property Wrappersʹॳظ஋Λ༩͑ Δ

  20. @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) } }
  21. ॳظ஋Λઃఆ͢Δ 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
  22. Projected Value

  23. projected • ܭը͞Εͨ • ݟੵ΋ΒΕͨ • ౤Ө͞Εͨ

  24. !

  25. Projected ValueΛఆٛ͢Δ

  26. @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 } } } }
  27. 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"
  28. Property Wrapper Composition @A @B var x: Int

  29. Property Wrapperͷ׆༻

  30. CodableͳArrayͷdecode Ұ෦ͷཁૉ͕decodeʹࣦഊ͢Δͱશཁૉࣦഊ͢Δ

  31. 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
  32. Property WrappersΛ࢖Θͳ͍ํ๏

  33. FailableܕΛఆٛ

  34. 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() } } }
  35. 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 } }
  36. Property WrappersΛ࢖ͬͨํ๏

  37. @LossyArray

  38. @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) } }
  39. 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]
  40. CodableͰσϑΥϧτ஋Λઃఆ͢Δ ͷ͸ͪΐͬͱखؒ init(from decoder: Decoder)Λॻ͔ͳ͍ͱ͍͚ͳ͍

  41. 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
  42. @DefaultFalse

  43. @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) } }
  44. 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)
  45. APIϨεϙϯε͕૝ఆͷܕͰͳ͍ or APIʹΑΓܕ͕ҟͳΔ

  46. 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
  47. @LosslessValue

  48. @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) } }
  49. 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)
  50. None
  51. SwiftUIͰఆٛ͞Εͨ Property Wrappers

  52. struct ContentView : View { @State var value: Bool =

    false var body: some View { Toggle(isOn: $value) { Text("The value is " + "\($value)") } } }
  53. @Published @ObservedObjectʢچ: @ObjectBindingʣ @Environment @EnvironmentObject @State @FetchRequest

  54. ·ͱΊ • @͸Attributeɺ@propertyWrapper͸Attributeͷͻͱͭ • ϑϨʔϜϫʔΫͰఆٛ͞ΕͨAttributeͰͳ͍΋ͷ͸ @propertyWrapperͰఆٛ͞ΕͯΔ͔΋ • $ϓϩύςΟ໊ͰProjected ValueʹΞΫηεͰ͖Δ •

    Property WrappersͰϓϩύςΟͷڞ௨ػೳΛͭ͘Ζ͏ʂ
  55. ࢀߟ • https://github.com/marksands/BetterCodable • https://www.hackingwithswift.com/quick-start/swiftui/ understanding-property-wrappers-in-swift-and-swiftui • SwiftUIͷProperty Wrappersͱσʔλ΁ͷΞΫηεํ๏

  56. ͓ΘΓ