Slide 1

Slide 1 text

Property Wrappers @shtnkgm

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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.

Slide 5

Slide 5 text

!

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

Attribute?

Slide 8

Slide 8 text

@͕͍ͭͯΔ͜Μͳ΍ͭ

Slide 9

Slide 9 text

@autoclosure @escaping @convention @available @discardableResult @objc @nonobjc @objcMembers @GKInspectable @UIApplicationMain @NSApplicationMain @NSCopying @NSKeyedArchiverClassName @NSManaged @testable @IBAction @IBOutlet @IBDesignable @IBInspectable

Slide 10

Slide 10 text

@ʙͱهड़͢Δ͜ͱͰɺϓϩύςΟ ΍ܕͷએݴʹิ଍৘ใΛઃఆͰ͖Δ ػೳ

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

Swift5.1 @functionBuilder @dynamicCallable @dynamicMemberLookup @inlinable @requiresstoredpropertyinits @usableFromInline @warnunqualifiedaccess

Slide 14

Slide 14 text

@propertyWrapper

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

AttributeΛͭͬͯ͘ΈΔ // 12ҎԼΛอূ͢Δ @propertyWrapper struct TwelveOrLess { private var number = 0 // ඞਢ var wrappedValue: Int { get { return number } set { number = min(newValue, 12) } } }

Slide 17

Slide 17 text

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"

Slide 18

Slide 18 text

12Ҏ֎ʹ΋ରԠͰ͖ΔΑ͏ʹ֦ு ͍ͨ͠

Slide 19

Slide 19 text

Property Wrappersʹॳظ஋Λ༩͑ Δ

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

ॳظ஋Λઃఆ͢Δ 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

Slide 22

Slide 22 text

Projected Value

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

!

Slide 25

Slide 25 text

Projected ValueΛఆٛ͢Δ

Slide 26

Slide 26 text

@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 } } } }

Slide 27

Slide 27 text

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"

Slide 28

Slide 28 text

Property Wrapper Composition @A @B var x: Int

Slide 29

Slide 29 text

Property Wrapperͷ׆༻

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

Property WrappersΛ࢖Θͳ͍ํ๏

Slide 33

Slide 33 text

FailableܕΛఆٛ

Slide 34

Slide 34 text

public struct Failable: 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() } } }

Slide 35

Slide 35 text

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].self, forKey: .suggests).compactMap { $0.value } } public init(query: String, items: [Item]) { self.query = query self.items = items } }

Slide 36

Slide 36 text

Property WrappersΛ࢖ͬͨํ๏

Slide 37

Slide 37 text

@LossyArray

Slide 38

Slide 38 text

@propertyWrapper public struct LossyArray: Codable { private struct AnyDecodableValue: Codable {} private struct LossyDecodableValue: 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.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) } }

Slide 39

Slide 39 text

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]

Slide 40

Slide 40 text

CodableͰσϑΥϧτ஋Λઃఆ͢Δ ͷ͸ͪΐͬͱखؒ init(from decoder: Decoder)Λॻ͔ͳ͍ͱ͍͚ͳ͍

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

@DefaultFalse

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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)

Slide 45

Slide 45 text

APIϨεϙϯε͕૝ఆͷܕͰͳ͍ or APIʹΑΓܕ͕ҟͳΔ

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

@LosslessValue

Slide 48

Slide 48 text

@propertyWrapper public struct LosslessValue: 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.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) } }

Slide 49

Slide 49 text

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)

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

SwiftUIͰఆٛ͞Εͨ Property Wrappers

Slide 52

Slide 52 text

struct ContentView : View { @State var value: Bool = false var body: some View { Toggle(isOn: $value) { Text("The value is " + "\($value)") } } }

Slide 53

Slide 53 text

@Published @ObservedObjectʢچ: @ObjectBindingʣ @Environment @EnvironmentObject @State @FetchRequest

Slide 54

Slide 54 text

·ͱΊ • @͸Attributeɺ@propertyWrapper͸Attributeͷͻͱͭ • ϑϨʔϜϫʔΫͰఆٛ͞ΕͨAttributeͰͳ͍΋ͷ͸ @propertyWrapperͰఆٛ͞ΕͯΔ͔΋ • $ϓϩύςΟ໊ͰProjected ValueʹΞΫηεͰ͖Δ • Property WrappersͰϓϩύςΟͷڞ௨ػೳΛͭ͘Ζ͏ʂ

Slide 55

Slide 55 text

ࢀߟ • https://github.com/marksands/BetterCodable • https://www.hackingwithswift.com/quick-start/swiftui/ understanding-property-wrappers-in-swift-and-swiftui • SwiftUIͷProperty Wrappersͱσʔλ΁ͷΞΫηεํ๏

Slide 56

Slide 56 text

͓ΘΓ