Slide 1

Slide 1 text

Swi$ UIͰצҧ͍ͨ͠࿩ Yuta Koshizawa @koher

Slide 2

Slide 2 text

@koher • try! Swi* Tokyo • 2016, 2018 • iOSDC Japan • 2017, 2018, 2019 • Qiita • "Swi*ͷOp?onalܕΛۃΊΔ" • "JavaϓϩάϥϚͷͨΊͷKotlinೖ໳" • ...

Slide 3

Slide 3 text

Swi$ UIͰצҧ͍ͨ͠࿩ ! ๻͕Ͳ͜Ͱצҧ͍͔ͨ͠ ߟ͑ͳ͕Βฉ͍ͯΈͯԼ͍͞

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

struct CounterView: View { @State var count: Int = 0 var body: some View { VStack { Text("\(count)") .font(.largeTitle) Stepper("count", value: $count) .labelsHidden() } } }

Slide 10

Slide 10 text

struct CounterView: View { @State var count: Int = 0 var body: some View { VStack { Text("\(count)") .font(.largeTitle) Stepper("count", value: $count) .labelsHidden() } } }

Slide 11

Slide 11 text

SE-0258: Property Wrappers1 Status: Implemented (Swi. 5.1) • @State • @ObservedObject • @Binding • @Published • ... 1 h$ps:/ /github.com/apple/swi6-evolu9on/blob/master/proposals/0258-property-wrappers.md

Slide 12

Slide 12 text

projectedValue2 $count Property Wrapper projectedValue @State Binding @Binding Binding @Published Published.Publisher 2 h$ps:/ /docs.swi/.org/swi/-book/LanguageGuide/Proper

Slide 13

Slide 13 text

struct CounterView: View { @State var count: Int = 0 var body: some View { VStack { Text("\(count)") .font(.largeTitle) Stepper("count", value: $count) .labelsHidden() } } }

Slide 14

Slide 14 text

struct CounterView: View { @State var count: Int = 0 var body: some View { VStack { Text("\(count)") .font(.largeTitle) Stepper("count", value: $count) .labelsHidden() } } }

Slide 15

Slide 15 text

struct CounterView: View { @State var count: Int = 0 var body: some View { VStack { Text("\(count)") .font(.largeTitle) Stepper("count", value: $count) .labelsHidden() } } }

Slide 16

Slide 16 text

import Combine final class Counter: ObservableObject { @Published var count: Int = 0 }

Slide 17

Slide 17 text

struct CounterView: View { @ObservedObject var counter: Counter var body: some View { VStack { Text("\(counter.count)") .font(.largeTitle) Stepper("count", value: $counter.count) .labelsHidden() } } }

Slide 18

Slide 18 text

struct CounterView: View { @ObservedObject var counter: Counter var body: some View { VStack { Text("\(counter.count)") .font(.largeTitle) Stepper("count", value: $counter.count) .labelsHidden() } } }

Slide 19

Slide 19 text

struct CounterView: View { @ObservedObject var counter: Counter var body: some View { VStack { Text("\(counter.count)") .font(.largeTitle) Stepper("count", value: $counter.count) .labelsHidden() } } }

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

struct NumberDisplay: View { @Binding var number: Int var body: some View { return HStack { ForEach(digits(from: number)) { Image(systemName:"\($0.value).circle.fill") .resizable() .frame(width: 64, height: 64) } } } ... }

Slide 22

Slide 22 text

struct NumberDisplay: View { @Binding var number: Int var body: some View { return HStack { ForEach(digits(from: number)) { Image(systemName:"\($0.value).circle.fill") .resizable() .frame(width: 64, height: 64) } } } ... }

Slide 23

Slide 23 text

struct CounterView: View { @ObservedObject var counter: Counter var body: some View { VStack { NumberDisplay(number: $counter.count) Stepper("count", value: $counter.count) .labelsHidden() } } }

Slide 24

Slide 24 text

struct CounterView: View { @ObservedObject var counter: Counter var body: some View { VStack { NumberDisplay(number: $counter.count) Stepper("count", value: $counter.count) .labelsHidden() } } }

Slide 25

Slide 25 text

import Combine final class Counter: ObservableObject { @Published var count: Int = 0 }

Slide 26

Slide 26 text

import Combine final class Counter: ObservableObject { @Published private(set) var count: Int = 0 func increment() { count += 1 } func reset() { count = 0 } }

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

struct CounterView: View { @ObservedObject var counter: Counter var body: some View { VStack { NumberDisplay(number: $counter.count) // HStack { Button("Reset") { self.counter.reset() } Button("Increment") { self.counter.increment() } } } } }

Slide 32

Slide 32 text

struct CounterView: View { @ObservedObject var counter: Counter var body: some View { VStack { NumberDisplay(number: $counter.count) // HStack { Button("Reset") { self.counter.reset() } Button("Increment") { self.counter.increment() } } } } }

Slide 33

Slide 33 text

struct CounterView: View { @ObservedObject var counter: Counter var body: some View { VStack { NumberDisplay(number: $counter.count) // HStack { Button("Reset") { self.counter.reset() } Button("Increment") { self.counter.increment() } } } } }

Slide 34

Slide 34 text

$counter.count ͳΜͰ Counter ͷϓϩύςΟʹ ΞΫηεͰ͖Δͷʁ

Slide 35

Slide 35 text

ObservedObject.projectedValue3 var projectedValue: ObservedObject.Wrapper 3 h$ps:/ /developer.apple.com/documenta6on/swi9ui/observedobject/projectedvalue

Slide 36

Slide 36 text

ObservedObject.Wrapper .subscript(dynamicMember:)4 subscript( dynamicMember keyPath: ReferenceWritableKeyPath ) -> Binding 4 h$ps:/ /developer.apple.com/documenta6on/swi9ui/observedobject/wrapper/subscript(dynamicmember:)

Slide 37

Slide 37 text

ObservedObject.Wrapper .subscript(dynamicMember:)4 subscript( dynamicMember keyPath: ReferenceWritableKeyPath ) -> Binding 4 h$ps:/ /developer.apple.com/documenta6on/swi9ui/observedobject/wrapper/subscript(dynamicmember:)

Slide 38

Slide 38 text

SE-0195: Introduce User-defined "Dynamic Member Lookup" Types 5 Status: Implemented (Swi. 4.2) @dynamicMemberLookup enum JSON { ... subscript(dynamicMember member: String) -> JSON? { ... } } 5 h$ps:/ /github.com/apple/swi6-evolu9on/blob/master/proposals/0195-dynamic-member-lookup.md

Slide 39

Slide 39 text

SE-0252: Key Path Member Lookup6 Status: Implemented (Swi. 5.1) @dynamicMemberLookup struct Lens { ... subscript(dynamicMember keyPath: WritableKeyPath) -> Lens { ... } } 6 h$ps:/ /github.com/apple/swi6-evolu9on/blob/master/proposals/0252-keypath-dynamic-member-lookup.md

Slide 40

Slide 40 text

ObservedObject.Wrapper subscript( dynamicMember keyPath: ReferenceWritableKeyPath ) -> Binding

Slide 41

Slide 41 text

ObservedObject.Wrapper subscript( dynamicMember keyPath: ReferenceWritableKeyPath ) -> Binding

Slide 42

Slide 42 text

૒ํ޲όΠϯσΟϯά ( Stepper ) ↓ Ұํ޲όΠϯσΟϯά ( NumberDisplay )

Slide 43

Slide 43 text

ObservedObject.Wrapper subscript( dynamicMember keyPath: ReferenceWritableKeyPath ) -> Binding

Slide 44

Slide 44 text

ReadOnly subscript( dynamicMember keyPath: KeyPath ) -> Binding

Slide 45

Slide 45 text

struct CounterView: View { @ObservedObject var counter: Counter var body: some View { VStack { NumberDisplay(number: $counter.count) // HStack { Button("Reset") { self.counter.reset() } Button("Increment") { self.counter.increment() } } } } }

Slide 46

Slide 46 text

struct CounterView: View { @ObservedObject var counter: Counter var body: some View { VStack { NumberDisplay(number: $counter.readOnly // .count) HStack { Button("Reset") { self.counter.reset() } Button("Increment") { self.counter.increment() } } } } }

Slide 47

Slide 47 text

ObservedObject.Wrapper.ReadOnly extension ObservedObject.Wrapper { var readOnly: ReadOnly { return ReadOnly(unsafeBitCast(self, to: ObjectType.self)) // } @dynamicMemberLookup struct ReadOnly { private let object: ObjectType init(_ object: ObjectType) { self.object = object } subscript(dynamicMember keyPath: KeyPath) -> Binding { Binding( get: { self.object[keyPath: keyPath] }, set: { _ in assertionFailure("Read-only") } ) } } }

Slide 48

Slide 48 text

One-way data binding from @ObservedObject with Swi9UI7 Is it possible to achieve one-way data binding with Swi6UI in situa9ons like below? import Combine final class Counter: ObservableObject { @Published private(set) var count: Int = 0 func increment() { count += 1 } func reset() { count = 0 } } ... 7 h$ps:/ /forums.swi1.org/t/one-way-data-binding-from-observedobject-with-swi1ui/37547

Slide 49

Slide 49 text

struct NumberDisplay: View { @Binding var number: Int var body: some View { return HStack { ForEach(digits(from: number)) { Image(systemName:"\($0.value).circle.fill") .resizable() .frame(width: 64, height: 64) } } } ... }

Slide 50

Slide 50 text

struct NumberDisplay: View { let number: Int var body: some View { return HStack { ForEach(digits(from: number)) { Image(systemName:"\($0.value).circle.fill") .resizable() .frame(width: 64, height: 64) } } } ... }

Slide 51

Slide 51 text

struct CounterView: View { @ObservedObject var counter: Counter var body: some View { VStack { NumberDisplay(number: $counter.readOnly .count) HStack { Button("Reset") { self.counter.reset() } Button("Increment") { self.counter.increment() } } } } }

Slide 52

Slide 52 text

struct CounterView: View { @ObservedObject var counter: Counter var body: some View { VStack { NumberDisplay(number: counter.count) HStack { Button("Reset") { self.counter.reset() } Button("Increment") { self.counter.increment() } } } } }

Slide 53

Slide 53 text

όΠϯσΟϯά͠ͳ͍ͱ มߋ͕൓ө͞Εͳ͘ͳ͍ʁ

Slide 54

Slide 54 text

Swi$UI ͷ View ͸ Ծ૝ View ͳͷͰ͜ΕͰ OK

Slide 55

Slide 55 text

Swi$UI ͷجૅதͷجૅ

Slide 56

Slide 56 text

ͳΜͰצҧ͍ͨ͠ʁ • Ծ૝ View ͷߟ͑ํΛཧղͯ͠Δͭ΋Γ͚ͩͬͨͲ׳Ε͕଍Γ ͳ͔ͬͨ • ૒ํ޲όΠϯσΟϯά → Ұํ޲όΠϯσΟϯά ͷॱʹߟ͑ͨ • Swi)UI ͸·ͩ·ͩػೳ͕଍Γͳ͍ͱ͍͏ઌೖ؍͕͋ͬͨ

Slide 57

Slide 57 text

ஏ͔͍ͣ͠ࢥ͍Λ͚ͨ͠ΕͲ΋ 3 ෼Ͱ໰୊͕ղܾͨ͠

Slide 58

Slide 58 text

Θ͔Βͳ͍͜ͱΛஏ͔͕ͣ͠Βͣ ࣭໰͢Δ͜ͱ͸େࣄ

Slide 59

Slide 59 text

Ͱ΋ɺ΍ͬͺΓஏ͔͍ͣ͠

Slide 60

Slide 60 text

Swi$ Zoomin' #3 ಗ໊࣭໰ձ ໌೔ 21:00- h"ps:/ /swi)-tweets.connpass.com/event/176665/

Slide 61

Slide 61 text

No content