Slide 1

Slide 1 text

όϦσʔγϣϯ෇͖ϑΥʔϜΛ એݴతʹ࣮૷͢Δ 2024/5/31(Fri) Mobile Act OSAKA 13

Slide 2

Slide 2 text

αϯϓϧίʔυ IUUQTHJUIVCDPNTUVEJPSPPLFSZ7BMJEBUJPO'PSN4BNQMF

Slide 3

Slide 3 text

ͱ͋ΔϓϩδΣΫτʹͯ

Slide 4

Slide 4 text

Α͋͘ΔϑΥʔϜը໘

Slide 5

Slide 5 text

Α͋͘ΔϑΥʔϜը໘ ̍ʙจࣈҎ಺ બ୒ඞਢ ̍ʙจࣈҎ಺ ৗʹԡͤΔ

Slide 6

Slide 6 text

Α͋͘ΔϑΥʔϜը໘ ৚݅Λຬ͍ͨͯ͠ͳ͍ঢ়ଶͰԡ͢ͱ

Slide 7

Slide 7 text

Α͋͘ΔϑΥʔϜը໘ Τϥʔঢ়ଶʹͳΓ Τϥʔϝοηʔδ͕දࣔ͞ΕΔ

Slide 8

Slide 8 text

Α͋͘ΔϑΥʔϜը໘ ஋Λม͑Δͱ࿮͸ফ͑ͯ Τϥʔϝοηʔδ͚͕ͩ࢒Δ

Slide 9

Slide 9 text

Α͋͘ΔϑΥʔϜը໘ ৚݅Λຬ͍ͨͯ͠Δঢ়ଶͰԡ͢ͱ

Slide 10

Slide 10 text

Α͋͘ΔϑΥʔϜը໘ Τϥʔ͕ফ͑Δ

Slide 11

Slide 11 text

ॴײ w ಛผ೉͘͠͸ͳ͍ w Ͱ΋ͪΐͬͱ໘౗ w Ϣʔβʔʹͱͬͯ͸࢖͍΍ͦ͢͏

Slide 12

Slide 12 text

Ұͭେ͖ͳ໰୊͕ʂ

Slide 13

Slide 13 text

͜͏͍͏ը໘PSίϯϙʔωϯτ͕ dը໘΄Ͳ͋Δ ͔ͳΓେ͖ΊͷϓϩδΣΫτͰͨ͠

Slide 14

Slide 14 text

۪௚ʹ࣮૷͢Δͱʜ w ਓʹΑ࣮ͬͯ૷ʹ͹Β͖͕ͭੜͦ͡͏ w ΋ͬͱ೉͍͠ը໘͕ଞʹ΋͋ΔͷͰ͜͜Ͱۤ࿑ͯ͠Δ৔߹Ͱ͸ͳ͍

Slide 15

Slide 15 text

্ख͍΍ΓํΛߟ͑Ͷ͹ʂ

Slide 16

Slide 16 text

ຊൃදͷ಺༰ w ࣅͨΑ͏ͳը໘Λେྔʹ࡞ΔࡍͷઃܭΛͲ͏ߟ͔͑ͨ

Slide 17

Slide 17 text

ཧ૝ͷܗΛߟ͑Δ w ਓʹΑ࣮ͬͯ૷͕όϥόϥʹͳΔͷ͸ආ͚͍ͨ w ͱ͸͍͑࠷ॳ͔Βཧ૝ͷܗΛ࡞Δͷ͸೉͍͠ w ͳͷͰҰ୴۪௚ʹ࣮૷ͯ͠Έͯஈ֊తʹվળ͍ͯ͘͠

Slide 18

Slide 18 text

લఏ৚݅ w 4XJGU6*Ͱ͢ w J04 Λର৅ͱ͍ͯ͠·ͨ͠ʢ౰࣌ʣ w "OESPJE൛͸ଘࡏ͠·ͤΜ

Slide 19

Slide 19 text

͜ΕʹόϦσʔγϣϯॲཧΛ଍͍ͯ͘͠ struct RegisterView: View { @State var id = "" @State var password = "" @State var favoriteFruit: Fruit? var body: some View { ScrollView { VStack(spacing: 16) { MyTextField(name: "ID", text: $id) MyTextField(name: "PW", text: $password) FruitPicker(name: "޷͖ͳϑϧʔπ", selection: $favoriteFruit) Button("ొ࿥") { } .buttonStyle(.myButtonStyle) } .padding() } } }

Slide 20

Slide 20 text

·ͣ͸ঢ়ଶ͔Βߟ͑Δ struct RegisterView: View { @State var id = "" @State var password = "" @State var favoriteFruit: Fruit? var body: some View { ScrollView { VStack(spacing: 16) { MyTextField(name: "ID", text: $id) MyTextField(name: "PW", text: $password) FruitPicker(name: "޷͖ͳϑϧʔπ", selection: $favoriteFruit) Button("ొ࿥") { } .buttonStyle(.myButtonStyle) } .padding() } } }

Slide 21

Slide 21 text

͓͞Β͍ w Τϥʔ͕ൃੜ͢Δͱ੺࿮ ΤϥʔϝοηʔδΛදࣔ w ஋͕มΘΔͱ੺࿮͚ͩফ͑Δ

Slide 22

Slide 22 text

͜Ε͕ @State var id = “"

Slide 23

Slide 23 text

͜͏ͳΔ @State var id = “" @State var idErrorMessage: String? @State var isIDErrorActive = false

Slide 24

Slide 24 text

͜͏ͳΔ @State var id = “" @State var idErrorMessage: String? @State var isIDErrorActive = false Τϥʔϝοηʔδͷ༗ແΛද͢ OJMͳΒΤϥʔϝοηʔδͳ͠ TPNFͳΒΤϥʔϝοηʔδ͋Γ

Slide 25

Slide 25 text

͜͏ͳΔ @State var id = “" @State var idErrorMessage: String? @State var isIDErrorActive = false ࿮ઢͷ༗ແΛද͢

Slide 26

Slide 26 text

͜͏ͳΔ @State var id = “" @State var idErrorMessage: String? @State var isIDErrorActive = false ϑΟʔϧυʹ͖ͭม਺͕ඞཁʂ

Slide 27

Slide 27 text

ͭ·Γ͜Ε͕ @State var id = "" @State var password = "" @State var favoriteFruit: Fruit?

Slide 28

Slide 28 text

͜͏ͳΔ @State var id = "" @State var idErrorMessage: String? @State var isIDErrorActive = false @State var password = "" @State var passwordErrorMessage: String? @State var isPasswordErrorActive = false @State var favoriteFruit: Fruit? @State var favoriteFruitErrorMessage: String? @State var isFavoriteFruitErrorActive = false

Slide 29

Slide 29 text

͜͏ͳΔ @State var id = "" @State var idErrorMessage: String? @State var isIDErrorActive = false @State var password = "" @State var passwordErrorMessage: String? @State var isPasswordErrorActive = false @State var favoriteFruit: Fruit? @State var favoriteFruitErrorMessage: String? @State var isFavoriteFruitErrorActive = false ͢Ͱʹେมͦ͏͚ͩͲ ·ͣ͸۪௚ʹ࡞ΔͷͰ͜ΕͰਐΊΔ

Slide 30

Slide 30 text

࣍͸όϦσʔγϣϯͷॲཧ

Slide 31

Slide 31 text

func register() { idErrorMessage = nil isIDErrorActive = false passwordErrorMessage = nil isPasswordErrorActive = false favoriteFruitErrorMessage = nil isFavoriteFruitErrorActive = false if id.isEmpty || id.count > 20 { idErrorMessage = "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isIDErrorActive = true } if password.isEmpty || password.count > 20 { passwordErrorMessage = "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isPasswordErrorActive = true } if favoriteFruit == nil { favoriteFruitErrorMessage = "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" isFavoriteFruitErrorActive = true } if isIDErrorActive || isPasswordErrorActive || isFavoriteFruitErrorActive { return } let request = RegisterRequest( id: id, password: password, favoriteFruit: favoriteFruit! ) Task { await API.send(request) } }

Slide 32

Slide 32 text

func register() { idErrorMessage = nil isIDErrorActive = false passwordErrorMessage = nil isPasswordErrorActive = false favoriteFruitErrorMessage = nil isFavoriteFruitErrorActive = false if id.isEmpty || id.count > 20 { idErrorMessage = "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isIDErrorActive = true } if password.isEmpty || password.count > 20 { passwordErrorMessage = "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isPasswordErrorActive = true } if favoriteFruit == nil { favoriteFruitErrorMessage = "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" isFavoriteFruitErrorActive = true } if isIDErrorActive || isPasswordErrorActive || isFavoriteFruitErrorActive { return } let request = RegisterRequest( id: id, password: password, favoriteFruit: favoriteFruit! ) Task { await API.send(request) } } Τϥʔͷঢ়ଶΛϦηοτ

Slide 33

Slide 33 text

func register() { idErrorMessage = nil isIDErrorActive = false passwordErrorMessage = nil isPasswordErrorActive = false favoriteFruitErrorMessage = nil isFavoriteFruitErrorActive = false if id.isEmpty || id.count > 20 { idErrorMessage = "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isIDErrorActive = true } if password.isEmpty || password.count > 20 { passwordErrorMessage = "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isPasswordErrorActive = true } if favoriteFruit == nil { favoriteFruitErrorMessage = "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" isFavoriteFruitErrorActive = true } if isIDErrorActive || isPasswordErrorActive || isFavoriteFruitErrorActive { return } let request = RegisterRequest( id: id, password: password, favoriteFruit: favoriteFruit! ) Task { await API.send(request) } } όϦσʔγϣϯΛύε͠ͳ͚Ε͹Τϥʔঢ়ଶʹ͢Δ

Slide 34

Slide 34 text

func register() { idErrorMessage = nil isIDErrorActive = false passwordErrorMessage = nil isPasswordErrorActive = false favoriteFruitErrorMessage = nil isFavoriteFruitErrorActive = false if id.isEmpty || id.count > 20 { idErrorMessage = "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isIDErrorActive = true } if password.isEmpty || password.count > 20 { passwordErrorMessage = "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isPasswordErrorActive = true } if favoriteFruit == nil { favoriteFruitErrorMessage = "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" isFavoriteFruitErrorActive = true } if isIDErrorActive || isPasswordErrorActive || isFavoriteFruitErrorActive { return } let request = RegisterRequest( id: id, password: password, favoriteFruit: favoriteFruit! ) Task { await API.send(request) } } શ෦ύε͚ͨ࣌ͩ͠"1*Λ౤͛Δ

Slide 35

Slide 35 text

࣍͸஋͕มΘͬͨ࣌ʹ Τϥʔͷ࿮ΛऔΓআ͘ॲཧ

Slide 36

Slide 36 text

͜ΕΛ @State var id = ""

Slide 37

Slide 37 text

͜͏͢Δ @State var id = "" { didSet { isIDErrorActive = false } }

Slide 38

Slide 38 text

࢒೦Ͱ͕͢ !4UBUFͷEJE4FU͸ಈ͖·ͤΜ

Slide 39

Slide 39 text

PO$IBOHFΛ࢖͏

Slide 40

Slide 40 text

͜Ε͕ MyTextField(name: "ID", text: $id) MyTextField(name: "PW", text: $password) FruitPicker(name: "޷͖ͳϑϧʔπ", selection: $favoriteFruit)

Slide 41

Slide 41 text

͜͏ͳΔ MyTextField(name: "ID", text: $id) .onChange(of: id) { _ in isIDErrorActive = false } MyTextField(name: "PW", text: $password) .onChange(of: password) { _ in isPasswordErrorActive = false } FruitPicker(name: "޷͖ͳϑϧʔπ", selection: $favoriteFruit) .onChange(of: favoriteFruit) { _ in isFavoriteFruitErrorActive = false }

Slide 42

Slide 42 text

͋ͱ͸Τϥʔͷ6*Λදࣔ͢Δ ͚ͩ

Slide 43

Slide 43 text

.PEJ fi FSʹΤϥʔঢ়ଶΛ౉͚ͩ͢ MyTextField(name: "ID", text: $id) .errorStyled(isActive: isIDErrorActive, errorMessage: idErrorMessage) .onChange(of: id) { _ in isIDErrorActive = false } MyTextField(name: "PW", text: $password) .errorStyled(isActive: isPasswordErrorActive, errorMessage: passwordErrorMessage) .onChange(of: password) { _ in isPasswordErrorActive = false } FruitPicker(name: "޷͖ͳϑϧʔπ", selection: $favoriteFruit) .errorStyled(isActive: isFavoriteFruitErrorActive, errorMessage: favoriteFruitErrorMessage) .onChange(of: favoriteFruit) { _ in isFavoriteFruitErrorActive = false } ৄࡉ͸ׂѪ

Slide 44

Slide 44 text

ؾʹͳΔͱ͜ΖͩΒ͚͕ͩ ͱΓ͋͑ͣͰ͖ͨ

Slide 45

Slide 45 text

struct RegisterView: View { @State var id = "" @State var idErrorMessage: String? @State var isIDErrorActive = false @State var password = "" @State var passwordErrorMessage: String? @State var isPasswordErrorActive = false @State var favoriteFruit: Fruit? @State var favoriteFruitErrorMessage: String? @State var isFavoriteFruitErrorActive = false var body: some View { ScrollView { VStack(spacing: 16) { MyTextField(name: "ID", text: $id) .errorStyled(isActive: isIDErrorActive, errorMessage: idErrorMessage) .onChange(of: id) { _ in isIDErrorActive = false } MyTextField(name: "PW", text: $password) .errorStyled(isActive: isPasswordErrorActive, errorMessage: passwordErrorMessage) .onChange(of: password) { _ in isPasswordErrorActive = false } FruitPicker(name: "޷͖ͳϑϧʔπ", selection: $favoriteFruit) .errorStyled(isActive: isFavoriteFruitErrorActive, errorMessage: favoriteFruitErrorMessage) .onChange(of: favoriteFruit) { _ in isFavoriteFruitErrorActive = false } Button("ొ࿥") { register() } .buttonStyle(.myButtonStyle) } .padding() } } func register() { idErrorMessage = nil isIDErrorActive = false passwordErrorMessage = nil isPasswordErrorActive = false favoriteFruitErrorMessage = nil isFavoriteFruitErrorActive = false if id.isEmpty || id.count > 20 { idErrorMessage = "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isIDErrorActive = true } if password.isEmpty || password.count > 20 { passwordErrorMessage = "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isPasswordErrorActive = true } if favoriteFruit == nil { favoriteFruitErrorMessage = "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" isFavoriteFruitErrorActive = true } if isIDErrorActive || isPasswordErrorActive || isFavoriteFruitErrorActive { return } let request = RegisterRequest( id: id, password: password, favoriteFruit: favoriteFruit! ) Task { await API.send(request) } } }

Slide 46

Slide 46 text

w ποίϛͲ͜Ζ͸͋Δ͕ϑΥʔϜܥͷը໘͕ ը໘͙Β͍ͳΒେ͖ͳ໰ ୊ʹ͸ͳΒͳͦ͞͏ w ͔͠͠ࠓճ͸ͱʹ͔͘਺͕ଟ͍ͷͰ਺͕ଟ͘ͳͬͯ΋͍͍Α͏ʹؤுΔ

Slide 47

Slide 47 text

ؾʹͳΔϙΠϯτ ʮ7JFXʹϕλ͕͖͍ͯ͠Δʯ

Slide 48

Slide 48 text

struct RegisterView_ver1: View { @State var id = "" @State var idErrorMessage: String? @State var isIDErrorActive = false @State var password = "" @State var passwordErrorMessage: String? @State var isPasswordErrorActive = false @State var favoriteFruit: Fruit? @State var favoriteFruitErrorMessage: String? @State var isFavoriteFruitErrorActive = false var body: some View { ScrollView { VStack(spacing: 16) { MyTextField(name: "ID", text: $id) .errorStyled(isActive: isIDErrorActive, errorMessage: idErrorMessage) .onChange(of: id) { _ in isIDErrorActive = false } MyTextField(name: "PW", text: $password) .errorStyled(isActive: isPasswordErrorActive, errorMessage: passwordErrorMessage) .onChange(of: password) { _ in isPasswordErrorActive = false } FruitPicker(name: "޷͖ͳϑϧʔπ", selection: $favoriteFruit) .errorStyled(isActive: isFavoriteFruitErrorActive, errorMessage: favoriteFruitErrorMessage) .onChange(of: favoriteFruit) { _ in isFavoriteFruitErrorActive = false } Button("ొ࿥") { register() } .buttonStyle(.myButtonStyle) } .padding() } } func register() { idErrorMessage = nil isIDErrorActive = false passwordErrorMessage = nil isPasswordErrorActive = false favoriteFruitErrorMessage = nil isFavoriteFruitErrorActive = false if id.isEmpty || id.count > 20 { idErrorMessage = "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isIDErrorActive = true } if password.isEmpty || password.count > 20 { passwordErrorMessage = "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isPasswordErrorActive = true } if favoriteFruit == nil { favoriteFruitErrorMessage = "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" isFavoriteFruitErrorActive = true } if isIDErrorActive || isPasswordErrorActive || isFavoriteFruitErrorActive { return } let request = RegisterRequest( id: id, password: password, favoriteFruit: favoriteFruit! ) Task { await API.send(request) } } } !4UBUFଟ͗͢  !4UBUFΛ࢖͍ͬͯΔͱςετॻ͚ͳ͍

Slide 49

Slide 49 text

struct RegisterView_ver1: View { @State var id = "" @State var idErrorMessage: String? @State var isIDErrorActive = false @State var password = "" @State var passwordErrorMessage: String? @State var isPasswordErrorActive = false @State var favoriteFruit: Fruit? @State var favoriteFruitErrorMessage: String? @State var isFavoriteFruitErrorActive = false var body: some View { ScrollView { VStack(spacing: 16) { MyTextField(name: "ID", text: $id) .errorStyled(isActive: isIDErrorActive, errorMessage: idErrorMessage) .onChange(of: id) { _ in isIDErrorActive = false } MyTextField(name: "PW", text: $password) .errorStyled(isActive: isPasswordErrorActive, errorMessage: passwordErrorMessage) .onChange(of: password) { _ in isPasswordErrorActive = false } FruitPicker(name: "޷͖ͳϑϧʔπ", selection: $favoriteFruit) .errorStyled(isActive: isFavoriteFruitErrorActive, errorMessage: favoriteFruitErrorMessage) .onChange(of: favoriteFruit) { _ in isFavoriteFruitErrorActive = false } Button("ొ࿥") { register() } .buttonStyle(.myButtonStyle) } .padding() } } func register() { idErrorMessage = nil isIDErrorActive = false passwordErrorMessage = nil isPasswordErrorActive = false favoriteFruitErrorMessage = nil isFavoriteFruitErrorActive = false if id.isEmpty || id.count > 20 { idErrorMessage = "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isIDErrorActive = true } if password.isEmpty || password.count > 20 { passwordErrorMessage = "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isPasswordErrorActive = true } if favoriteFruit == nil { favoriteFruitErrorMessage = "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" isFavoriteFruitErrorActive = true } if isIDErrorActive || isPasswordErrorActive || isFavoriteFruitErrorActive { return } let request = RegisterRequest( id: id, password: password, favoriteFruit: favoriteFruit! ) Task { await API.send(request) } } } ϨΠΞ΢τͷίʔυͱൺ΂Δͱ όϦσʔγϣϯॲཧ௕͗͢

Slide 50

Slide 50 text

͜ͷ఺͸7JFXʹॻ͔ͳ͍ͱ ͍͚ͳ͍ॲཧͰ͸ͳ͍

Slide 51

Slide 51 text

.PEFM΁Ҡಈ͢Δ

Slide 52

Slide 52 text

TUSVDUΛ༻ҙ struct RegisterForm { } struct RegisterView: View { @State var id = "" @State var idErrorMessage: String? @State var isIDErrorActive = false @State var password = "" @State var passwordErrorMessage: String? @State var isPasswordErrorActive = false @State var favoriteFruit: Fruit? @State var favoriteFruitErrorMessage: String? @State var isFavoriteFruitErrorActive = false var body: some View { ScrollView { VStack(spacing: 16) { MyTextField(name: "ID", text: $id) .errorStyled(isActive: isIDErrorActive, errorMessage: idErrorMessage) .onChange(of: id) { _ in isIDErrorActive = false } MyTextField(name: "PW", text: $password) .errorStyled(isActive: isPasswordErrorActive, errorMessage: passwordErrorMessage) .onChange(of: password) { _ in isPasswordErrorActive = false } FruitPicker(name: "޷͖ͳϑϧʔπ", selection: $favoriteFruit) .errorStyled(isActive: isFavoriteFruitErrorActive, errorMessage: favoriteFruitErrorMessage) .onChange(of: favoriteFruit) { _ in isFavoriteFruitErrorActive = false } Button("ొ࿥") { register() } .buttonStyle(.myButtonStyle) } .padding() } } func register() { idErrorMessage = nil isIDErrorActive = false passwordErrorMessage = nil isPasswordErrorActive = false favoriteFruitErrorMessage = nil isFavoriteFruitErrorActive = false if id.isEmpty || id.count > 20 { idErrorMessage = "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isIDErrorActive = true } if password.isEmpty || password.count > 20 { passwordErrorMessage = "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isPasswordErrorActive = true } if favoriteFruit == nil { favoriteFruitErrorMessage = "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" isFavoriteFruitErrorActive = true } if isIDErrorActive || isPasswordErrorActive || isFavoriteFruitErrorActive { return } let request = RegisterRequest( id: id, password: password, favoriteFruit: favoriteFruit! ) Task { await API.send(request) } } }

Slide 53

Slide 53 text

͜ͷ෦෼Λ struct RegisterForm { } struct RegisterView_ver1: View { @State var id = "" @State var idErrorMessage: String? @State var isIDErrorActive = false @State var password = "" @State var passwordErrorMessage: String? @State var isPasswordErrorActive = false @State var favoriteFruit: Fruit? @State var favoriteFruitErrorMessage: String? @State var isFavoriteFruitErrorActive = false var body: some View { ScrollView { VStack(spacing: 16) { MyTextField(name: "ID", text: $id) .errorStyled(isActive: isIDErrorActive, errorMessage: idErrorMessage) .onChange(of: id) { _ in isIDErrorActive = false } MyTextField(name: "PW", text: $password) .errorStyled(isActive: isPasswordErrorActive, errorMessage: passwordErrorMessage) .onChange(of: password) { _ in isPasswordErrorActive = false } FruitPicker(name: "޷͖ͳϑϧʔπ", selection: $favoriteFruit) .errorStyled(isActive: isFavoriteFruitErrorActive, errorMessage: favoriteFruitErrorMessage) .onChange(of: favoriteFruit) { _ in isFavoriteFruitErrorActive = false } Button("ొ࿥") { register() } .buttonStyle(.myButtonStyle) } .padding() } } func register() { idErrorMessage = nil isIDErrorActive = false passwordErrorMessage = nil isPasswordErrorActive = false favoriteFruitErrorMessage = nil isFavoriteFruitErrorActive = false if id.isEmpty || id.count > 20 { idErrorMessage = "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isIDErrorActive = true } if password.isEmpty || password.count > 20 { passwordErrorMessage = "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isPasswordErrorActive = true } if favoriteFruit == nil { favoriteFruitErrorMessage = "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" isFavoriteFruitErrorActive = true } if isIDErrorActive || isPasswordErrorActive || isFavoriteFruitErrorActive { return } let request = RegisterRequest( id: id, password: password, favoriteFruit: favoriteFruit! ) Task { await API.send(request) } } }

Slide 54

Slide 54 text

Ҡಈ struct RegisterForm { @State var id = "" @State var idErrorMessage: String? @State var isIDErrorActive = false @State var password = "" @State var passwordErrorMessage: String? @State var isPasswordErrorActive = false @State var favoriteFruit: Fruit? @State var favoriteFruitErrorMessage: String? @State var isFavoriteFruitErrorActive = false } struct RegisterView_ver1: View { var body: some View { ScrollView { VStack(spacing: 16) { MyTextField(name: "ID", text: $id) .errorStyled(isActive: isIDErrorActive, errorMessage: idErrorMessage) .onChange(of: id) { _ in isIDErrorActive = false } MyTextField(name: "PW", text: $password) .errorStyled(isActive: isPasswordErrorActive, errorMessage: passwordErrorMessage) .onChange(of: password) { _ in isPasswordErrorActive = false } FruitPicker(name: "޷͖ͳϑϧʔπ", selection: $favoriteFruit) .errorStyled(isActive: isFavoriteFruitErrorActive, errorMessage: favoriteFruitErrorMessage) .onChange(of: favoriteFruit) { _ in isFavoriteFruitErrorActive = false } Button("ొ࿥") { register() } .buttonStyle(.myButtonStyle) } .padding() } } func register() { idErrorMessage = nil isIDErrorActive = false passwordErrorMessage = nil isPasswordErrorActive = false favoriteFruitErrorMessage = nil isFavoriteFruitErrorActive = false if id.isEmpty || id.count > 20 { idErrorMessage = "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isIDErrorActive = true } if password.isEmpty || password.count > 20 { passwordErrorMessage = "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isPasswordErrorActive = true } if favoriteFruit == nil { favoriteFruitErrorMessage = "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" isFavoriteFruitErrorActive = true } if isIDErrorActive || isPasswordErrorActive || isFavoriteFruitErrorActive { return } let request = RegisterRequest( id: id, password: password, favoriteFruit: favoriteFruit! ) Task { await API.send(request) } } }

Slide 55

Slide 55 text

͜ͷ෦෼Λ struct RegisterForm { @State var id = "" @State var idErrorMessage: String? @State var isIDErrorActive = false @State var password = "" @State var passwordErrorMessage: String? @State var isPasswordErrorActive = false @State var favoriteFruit: Fruit? @State var favoriteFruitErrorMessage: String? @State var isFavoriteFruitErrorActive = false } struct RegisterView_ver1: View { var body: some View { ScrollView { VStack(spacing: 16) { MyTextField(name: "ID", text: $id) .errorStyled(isActive: isIDErrorActive, errorMessage: idErrorMessage) .onChange(of: id) { _ in isIDErrorActive = false } MyTextField(name: "PW", text: $password) .errorStyled(isActive: isPasswordErrorActive, errorMessage: passwordErrorMessage) .onChange(of: password) { _ in isPasswordErrorActive = false } FruitPicker(name: "޷͖ͳϑϧʔπ", selection: $favoriteFruit) .errorStyled(isActive: isFavoriteFruitErrorActive, errorMessage: favoriteFruitErrorMessage) .onChange(of: favoriteFruit) { _ in isFavoriteFruitErrorActive = false } Button("ొ࿥") { register() } .buttonStyle(.myButtonStyle) } .padding() } } func register() { idErrorMessage = nil isIDErrorActive = false passwordErrorMessage = nil isPasswordErrorActive = false favoriteFruitErrorMessage = nil isFavoriteFruitErrorActive = false if id.isEmpty || id.count > 20 { idErrorMessage = "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isIDErrorActive = true } if password.isEmpty || password.count > 20 { passwordErrorMessage = "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isPasswordErrorActive = true } if favoriteFruit == nil { favoriteFruitErrorMessage = "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" isFavoriteFruitErrorActive = true } if isIDErrorActive || isPasswordErrorActive || isFavoriteFruitErrorActive { return } let request = RegisterRequest( id: id, password: password, favoriteFruit: favoriteFruit! ) Task { await API.send(request) } } }

Slide 56

Slide 56 text

Ҡಈ struct RegisterForm { @State var id = "" @State var idErrorMessage: String? @State var isIDErrorActive = false @State var password = "" @State var passwordErrorMessage: String? @State var isPasswordErrorActive = false @State var favoriteFruit: Fruit? @State var favoriteFruitErrorMessage: String? @State var isFavoriteFruitErrorActive = false func register() -> RegisterRequest? { idErrorMessage = nil isIDErrorActive = false passwordErrorMessage = nil isPasswordErrorActive = false favoriteFruitErrorMessage = nil isFavoriteFruitErrorActive = false if id.isEmpty || id.count > 20 { idErrorMessage = "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isIDErrorActive = true } if password.isEmpty || password.count > 20 { passwordErrorMessage = "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isPasswordErrorActive = true } if favoriteFruit == nil { favoriteFruitErrorMessage = "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" isFavoriteFruitErrorActive = true } if isIDErrorActive || isPasswordErrorActive || isFavoriteFruitErrorActive { return nil } let request = RegisterRequest( id: id, password: password, favoriteFruit: favoriteFruit! ) return request } } struct RegisterView_ver1: View { var body: some View { ScrollView { VStack(spacing: 16) { MyTextField(name: "ID", text: $id) .errorStyled(isActive: isIDErrorActive, errorMessage: idErrorMessage) .onChange(of: id) { _ in isIDErrorActive = false } MyTextField(name: "PW", text: $password) .errorStyled(isActive: isPasswordErrorActive, errorMessage: passwordErrorMessage) .onChange(of: password) { _ in isPasswordErrorActive = false } FruitPicker(name: "޷͖ͳϑϧʔπ", selection: $favoriteFruit) .errorStyled(isActive: isFavoriteFruitErrorActive, errorMessage: favoriteFruitErrorMessage) .onChange(of: favoriteFruit) { _ in isFavoriteFruitErrorActive = false } Button("ొ࿥") { register() } .buttonStyle(.myButtonStyle) } .padding() } } func register() { Task { await API.send(request) } } }

Slide 57

Slide 57 text

!4UBUF͸ෆཁ struct RegisterForm { @State var id = "" @State var idErrorMessage: String? @State var isIDErrorActive = false @State var password = "" @State var passwordErrorMessage: String? @State var isPasswordErrorActive = false @State var favoriteFruit: Fruit? @State var favoriteFruitErrorMessage: String? @State var isFavoriteFruitErrorActive = false } struct RegisterForm { var id = "" var idErrorMessage: String? var isIDErrorActive = false var password = "" var passwordErrorMessage: String? var isPasswordErrorActive = false var favoriteFruit: Fruit? var favoriteFruitErrorMessage: String? var isFavoriteFruitErrorActive = false }

Slide 58

Slide 58 text

EJE4FU͕࢖͑ΔΑ͏ʹͳͬͨ struct RegisterForm { var id = "" { didSet { guard id != oldValue else { return } isIDErrorActive = false } } var password = "" { didSet { guard password != oldValue else { return } isPasswordErrorActive = false } } var favoriteFruit: Fruit? { didSet { guard favoriteFruit != oldValue else { return } isFavoriteFruitErrorActive = false } } } struct RegisterForm { var id = "" var idErrorMessage: String? var isIDErrorActive = false var password = "" var passwordErrorMessage: String? var isPasswordErrorActive = false var favoriteFruit: Fruit? var favoriteFruitErrorMessage: String? var isFavoriteFruitErrorActive = false }

Slide 59

Slide 59 text

EJE4FU͕࢖͑ΔΑ͏ʹͳͬͨ MyTextField(name: "ID", text: $id) .errorStyled(isActive: isIDErrorActive, errorMessage: idErrorMessage) .onChange(of: id) { _ in isIDErrorActive = false } MyTextField(name: "PW", text: $password) .errorStyled(isActive: isPasswordErrorActive, errorMessage: passwordErrorMessage) .onChange(of: password) { _ in isPasswordErrorActive = false } FruitPicker(name: "޷͖ͳϑϧʔπ", selection: $favoriteFruit) .errorStyled(isActive: isFavoriteFruitErrorActive, errorMessage: favoriteFruitErrorMessage) .onChange(of: favoriteFruit) { _ in isFavoriteFruitErrorActive = false } MyTextField(name: "ID", text: $id) .errorStyled(isActive: isIDErrorActive, errorMessage: idErrorM MyTextField(name: "PW", text: $password) .errorStyled(isActive: isPasswordErrorActive, errorMessage: pa FruitPicker(name: "޷͖ͳϑϧʔπ", selection: $favoriteFruit) .errorStyled(isActive: isFavoriteFruitErrorActive, errorMessag

Slide 60

Slide 60 text

ͭ·Γ͜Ε͕ struct RegisterView: View { @State var id = "" @State var idErrorMessage: String? @State var isIDErrorActive = false @State var password = "" @State var passwordErrorMessage: String? @State var isPasswordErrorActive = false @State var favoriteFruit: Fruit? @State var favoriteFruitErrorMessage: String? @State var isFavoriteFruitErrorActive = false var body: some View { ScrollView { VStack(spacing: 16) { MyTextField(name: "ID", text: $id) .errorStyled(isActive: isIDErrorActive, errorMessage: idErrorMessage) .onChange(of: id) { _ in isIDErrorActive = false } MyTextField(name: "PW", text: $password) .errorStyled(isActive: isPasswordErrorActive, errorMessage: passwordErrorMessage) .onChange(of: password) { _ in isPasswordErrorActive = false } FruitPicker(name: "޷͖ͳϑϧʔπ", selection: $favoriteFruit) .errorStyled(isActive: isFavoriteFruitErrorActive, errorMessage: favoriteFruitErrorMessage) .onChange(of: favoriteFruit) { _ in isFavoriteFruitErrorActive = false } Button("ొ࿥") { register() } .buttonStyle(.myButtonStyle) } .padding() } } func register() { idErrorMessage = nil isIDErrorActive = false passwordErrorMessage = nil isPasswordErrorActive = false favoriteFruitErrorMessage = nil isFavoriteFruitErrorActive = false if id.isEmpty || id.count > 20 { idErrorMessage = "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isIDErrorActive = true } if password.isEmpty || password.count > 20 { passwordErrorMessage = "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isPasswordErrorActive = true } if favoriteFruit == nil { favoriteFruitErrorMessage = "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" isFavoriteFruitErrorActive = true } if isIDErrorActive || isPasswordErrorActive || isFavoriteFruitErrorActive { return } let request = RegisterRequest( id: id, password: password, favoriteFruit: favoriteFruit! ) Task { await API.send(request) } } }

Slide 61

Slide 61 text

ϑΝΠϧʹ෼ׂ struct RegisterView: View { @State var form = RegisterForm() var body: some View { ScrollView { VStack(spacing: 16) { MyTextField(name: "ID", text: $form.id) .errorStyled(isActive: form.isIDErrorActive, errorMessage: form.idErrorMessage) MyTextField(name: "PW", text: $form.password) .errorStyled(isActive: form.isPasswordErrorActive, errorMessage: form.passwordErrorMessage) FruitPicker(name: "޷͖ͳϑϧʔπ", selection: $form.favoriteFruit) .errorStyled(isActive: form.isFavoriteFruitErrorActive, errorMessage: form.favoriteFruitErrorMessage) Button("ొ࿥") { register() } .buttonStyle(.myButtonStyle) } .padding() } } func register() { guard let request = form.validate() else { return } Task { await API.send(request) } } } struct RegisterForm { var id = "" { didSet { guard id != oldValue else { return } isIDErrorActive = false } } var idErrorMessage: String? var isIDErrorActive = false var password = "" { didSet { guard password != oldValue else { return } isPasswordErrorActive = false } } var passwordErrorMessage: String? var isPasswordErrorActive = false var favoriteFruit: Fruit? { didSet { guard favoriteFruit != oldValue else { return } isFavoriteFruitErrorActive = false } } var favoriteFruitErrorMessage: String? var isFavoriteFruitErrorActive = false mutating func validate() -> RegisterRequest? { idErrorMessage = nil isIDErrorActive = false passwordErrorMessage = nil isPasswordErrorActive = false favoriteFruitErrorMessage = nil isFavoriteFruitErrorActive = false if id.isEmpty || id.count > 20 { idErrorMessage = "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isIDErrorActive = true } if password.isEmpty || password.count > 20 { passwordErrorMessage = "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isPasswordErrorActive = true } if favoriteFruit == nil { favoriteFruitErrorMessage = "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" isFavoriteFruitErrorActive = true } if isIDErrorActive || isPasswordErrorActive || isFavoriteFruitErrorActive { return nil } let request = RegisterRequest( id: id, password: password, favoriteFruit: favoriteFruit! ) return request } }

Slide 62

Slide 62 text

ঢ়ଶͱϩδοΫΛTUSVDU΁੾Γग़ͨ͠ w 4XJGU6*ͷ౎߹ʹҾͬுΒΕͳ͘ͳͬͨ w EJE4FU͕࢖͑ΔΑ͏ʹͳͬͨ w ςετͰ͖ΔΑ͏ʹͳͬͨ w 7JFXͷϑΝΠϧ͸6*ͷ͜ͱ͚ͩʹूத͢Ε͹ྑ͘ͳͬͨ w ϑΝΠϧʹ෼͔Εͨ͜ͱʹΑͬͯݟ௨͠͸গ͠μ΢ϯʁ w ྫ͕؆୯ͳͷͰϝϦοτײ͡ʹ͍͔͘΋Ͱ͕࣮͢ࡍ͸ೖྗ߲໨਺͸΋ͬͱଟ ͍ͷͰײ͡ํมΘΓͦ͏

Slide 63

Slide 63 text

ؾʹͳΔϙΠϯτ ʮϑΟʔϧυ͝ͱʹม਺ඞཁʯ

Slide 64

Slide 64 text

struct RegisterForm { var id = "" { didSet { guard id != oldValue else { return } isIDErrorActive = false } } var idErrorMessage: String? var isIDErrorActive = false var password = "" { didSet { guard password != oldValue else { return } isPasswordErrorActive = false } } var passwordErrorMessage: String? var isPasswordErrorActive = false var favoriteFruit: Fruit? { didSet { guard favoriteFruit != oldValue else { return } isFavoriteFruitErrorActive = false } } var favoriteFruitErrorMessage: String? var isFavoriteFruitErrorActive = false }

Slide 65

Slide 65 text

struct RegisterForm { var id = "" { didSet { guard id != oldValue else { return } isIDErrorActive = false } } var idErrorMessage: String? var isIDErrorActive = false var password = "" { didSet { guard password != oldValue else { return } isPasswordErrorActive = false } } var passwordErrorMessage: String? var isPasswordErrorActive = false var favoriteFruit: Fruit? { didSet { guard favoriteFruit != oldValue else { return } isFavoriteFruitErrorActive = false } } var favoriteFruitErrorMessage: String? var isFavoriteFruitErrorActive = false } ࣅͨΑ͏ͳॲཧ

Slide 66

Slide 66 text

struct RegisterForm { var id = "" { didSet { guard id != oldValue else { return } isIDErrorActive = false } } var idErrorMessage: String? var isIDErrorActive = false var password = "" { didSet { guard password != oldValue else { return } isPasswordErrorActive = false } } var passwordErrorMessage: String? var isPasswordErrorActive = false var favoriteFruit: Fruit? { didSet { guard favoriteFruit != oldValue else { return } isFavoriteFruitErrorActive = false } } var favoriteFruitErrorMessage: String? var isFavoriteFruitErrorActive = false } ࣅͨΑ͏ͳίʔυॻ͘ͷ͕໘౗

Slide 67

Slide 67 text

struct RegisterForm { mutating func validate() -> RegisterRequest? { idErrorMessage = nil isIDErrorActive = false passwordErrorMessage = nil isPasswordErrorActive = false favoriteFruitErrorMessage = nil isFavoriteFruitErrorActive = false if id.isEmpty || id.count > 20 { idErrorMessage = "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isIDErrorActive = true } if password.isEmpty || password.count > 20 { passwordErrorMessage = "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isPasswordErrorActive = true } if favoriteFruit == nil { favoriteFruitErrorMessage = "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" isFavoriteFruitErrorActive = true } if isIDErrorActive || isPasswordErrorActive || isFavoriteFruitErrorActive { return nil } let request = RegisterRequest( id: id, password: password, favoriteFruit: favoriteFruit! ) return request } } ࣅͨΑ͏ͳॲཧ௕͍

Slide 68

Slide 68 text

δΣωϦΫεΛ࢖ͬͯͭͷ TUSVDUʹ·ͱΊΔ

Slide 69

Slide 69 text

δΣωϦοΫͳTUSVDUͰඞཁͳม਺Λ·ͱΊΔ struct Field { var wrappedValue: Value var isErrorActive = false var errorMessage: String? }

Slide 70

Slide 70 text

δΣωϦοΫͳTUSVDUͰඞཁͳม਺Λ·ͱΊΔ struct Field { var wrappedValue: Value var isErrorActive = false var errorMessage: String? } struct RegisterForm { var id = "" { didSet { guard id != oldValue else { return } isIDErrorActive = false } } var idErrorMessage: String? var isIDErrorActive = false var password = "" { didSet { guard password != oldValue else { return } isPasswordErrorActive = false } } var passwordErrorMessage: String? var isPasswordErrorActive = false var favoriteFruit: Fruit? { didSet { guard favoriteFruit != oldValue else { return } isFavoriteFruitErrorActive = false } } var favoriteFruitErrorMessage: String? var isFavoriteFruitErrorActive = false }

Slide 71

Slide 71 text

δΣωϦοΫͳTUSVDUͰඞཁͳม਺Λ·ͱΊΔ struct Field { var wrappedValue: Value var isErrorActive = false var errorMessage: String? } struct RegisterForm { var id = "" { didSet { guard id != oldValue else { return } isIDErrorActive = false } } var idErrorMessage: String? var isIDErrorActive = false var password = "" { didSet { guard password != oldValue else { return } isPasswordErrorActive = false } } var passwordErrorMessage: String? var isPasswordErrorActive = false var favoriteFruit: Fruit? { didSet { guard favoriteFruit != oldValue else { return } isFavoriteFruitErrorActive = false } } var favoriteFruitErrorMessage: String? var isFavoriteFruitErrorActive = false }

Slide 72

Slide 72 text

δΣωϦοΫͳTUSVDUͰඞཁͳม਺Λ·ͱΊΔ struct Field { var wrappedValue: Value var isErrorActive = false var errorMessage: String? } struct RegisterForm { var id = "" { didSet { guard id != oldValue else { return } isIDErrorActive = false } } var idErrorMessage: String? var isIDErrorActive = false var password = "" { didSet { guard password != oldValue else { return } isPasswordErrorActive = false } } var passwordErrorMessage: String? var isPasswordErrorActive = false var favoriteFruit: Fruit? { didSet { guard favoriteFruit != oldValue else { return } isFavoriteFruitErrorActive = false } } var favoriteFruitErrorMessage: String? var isFavoriteFruitErrorActive = false }

Slide 73

Slide 73 text

δΣωϦοΫͳTUSVDUͰඞཁͳม਺Λ·ͱΊΔ struct Field { var wrappedValue: Value { didSet { guard wrappedValue != oldValue else { return } isErrorActive = false } } var isErrorActive = false var errorMessage: String? } struct RegisterForm { var id = "" { didSet { guard id != oldValue else { return } isIDErrorActive = false } } var idErrorMessage: String? var isIDErrorActive = false var password = "" { didSet { guard password != oldValue else { return } isPasswordErrorActive = false } } var passwordErrorMessage: String? var isPasswordErrorActive = false var favoriteFruit: Fruit? { didSet { guard favoriteFruit != oldValue else { return } isFavoriteFruitErrorActive = false } } var favoriteFruitErrorMessage: String? var isFavoriteFruitErrorActive = false }

Slide 74

Slide 74 text

ͳͷͰ͜Ε͕ struct RegisterForm { var id = "" { didSet { guard id != oldValue else { return } isIDErrorActive = false } } var idErrorMessage: String? var isIDErrorActive = false var password = "" { didSet { guard password != oldValue else { return } isPasswordErrorActive = false } } var passwordErrorMessage: String? var isPasswordErrorActive = false var favoriteFruit: Fruit? { didSet { guard favoriteFruit != oldValue else { return } isFavoriteFruitErrorActive = false } } var favoriteFruitErrorMessage: String? var isFavoriteFruitErrorActive = false }

Slide 75

Slide 75 text

͜͏ͳΔ struct RegisterForm { var id = "" { didSet { guard id != oldValue else { return } isIDErrorActive = false } } var idErrorMessage: String? var isIDErrorActive = false var password = "" { didSet { guard password != oldValue else { return } isPasswordErrorActive = false } } var passwordErrorMessage: String? var isPasswordErrorActive = false var favoriteFruit: Fruit? { didSet { guard favoriteFruit != oldValue else { return } isFavoriteFruitErrorActive = false } } var favoriteFruitErrorMessage: String? var isFavoriteFruitErrorActive = false } struct RegisterForm { var id: Field = .init(wrappedValue: “") var password: Field = .init(wrappedValue: "") var favoriteFruit: Field = .init(wrappedValue: nil) }

Slide 76

Slide 76 text

͜͏ͳΔ struct RegisterForm { var id = "" { didSet { guard id != oldValue else { return } isIDErrorActive = false } } var idErrorMessage: String? var isIDErrorActive = false var password = "" { didSet { guard password != oldValue else { return } isPasswordErrorActive = false } } var passwordErrorMessage: String? var isPasswordErrorActive = false var favoriteFruit: Fruit? { didSet { guard favoriteFruit != oldValue else { return } isFavoriteFruitErrorActive = false } } var favoriteFruitErrorMessage: String? var isFavoriteFruitErrorActive = false } struct RegisterForm { var id: Field = .init(wrappedValue: “") var password: Field = .init(wrappedValue: "") var favoriteFruit: Field = .init(wrappedValue: nil) }

Slide 77

Slide 77 text

ͱ͜ΖͰʜ

Slide 78

Slide 78 text

͜Ε1SPQFSUZ8SBQQFSʹͰ͖Δ΍Μʂ @propertyWrapper struct Field { var wrappedValue: Value { didSet { guard wrappedValue != oldValue else { return } isErrorActive = false } } var isErrorActive = false var errorMessage: String? }

Slide 79

Slide 79 text

ͳͷͰ͜Ε͕ struct RegisterForm { var id: Field = .init(wrappedValue: "") var password: Field = .init(wrappedValue: "") var favoriteFruit: Field = .init(wrappedValue: nil) }

Slide 80

Slide 80 text

͜͏ͳΔ struct RegisterForm { var id: Field = .init(wrappedValue: "") var password: Field = .init(wrappedValue: "") var favoriteFruit: Field = .init(wrappedValue: nil) } struct RegisterForm { @Field var id = "" @Field var password = "" @Field var favoriteFruit: Fruit? }

Slide 81

Slide 81 text

͜Ε΋εοΩϦͤ͞Δ mutating func validate() -> RegisterRequest? { idErrorMessage = nil isIDErrorActive = false passwordErrorMessage = nil isPasswordErrorActive = false favoriteFruitErrorMessage = nil isFavoriteFruitErrorActive = false … }

Slide 82

Slide 82 text

͜Ε΋εοΩϦͤ͞Δ @propertyWrapper struct Field { … mutating func clearError() { isErrorActive = false errorMessage = nil } var projectedValue: Field { get { self } set { self = newValue } } } mutating func validate() -> RegisterRequest? { idErrorMessage = nil isIDErrorActive = false passwordErrorMessage = nil isPasswordErrorActive = false favoriteFruitErrorMessage = nil isFavoriteFruitErrorActive = false … }

Slide 83

Slide 83 text

͜Ε΋εοΩϦͤ͞Δ @propertyWrapper struct Field { … mutating func clearError() { isErrorActive = false errorMessage = nil } var projectedValue: Field { get { self } set { self = newValue } } } mutating func validate() -> RegisterRequest? { idErrorMessage = nil isIDErrorActive = false passwordErrorMessage = nil isPasswordErrorActive = false favoriteFruitErrorMessage = nil isFavoriteFruitErrorActive = false … }

Slide 84

Slide 84 text

͜Ε΋εοΩϦͤ͞Δ mutating func validate() -> RegisterRequest? { $id.clearError() $password.clearError() $favoriteFruit.clearError() … } @propertyWrapper struct Field { … mutating func clearError() { isErrorActive = false errorMessage = nil } var projectedValue: Field { get { self } set { self = newValue } } }

Slide 85

Slide 85 text

mutating func validate() -> RegisterRequest? { $id.clearError() $password.clearError() $favoriteFruit.clearError() … } ͜Ε΋εοΩϦͤ͞Δ @propertyWrapper struct Field { … mutating func clearError() { isErrorActive = false errorMessage = nil } var projectedValue: Field { get { self } set { self = newValue } } }

Slide 86

Slide 86 text

εοΩϦͨ͠ mutating func validate() -> RegisterRequest? { idErrorMessage = nil isIDErrorActive = false passwordErrorMessage = nil isPasswordErrorActive = false favoriteFruitErrorMessage = nil isFavoriteFruitErrorActive = false … } mutating func validate() -> RegisterRequest? { $id.clearError() $password.clearError() $favoriteFruit.clearError() … }

Slide 87

Slide 87 text

struct RegisterForm { var id = "" { didSet { guard id != oldValue else { return } isIDErrorActive = false } } var idErrorMessage: String? var isIDErrorActive = false var password = "" { didSet { guard password != oldValue else { return } isPasswordErrorActive = false } } var passwordErrorMessage: String? var isPasswordErrorActive = false var favoriteFruit: Fruit? { didSet { guard favoriteFruit != oldValue else { return } isFavoriteFruitErrorActive = false } } var favoriteFruitErrorMessage: String? var isFavoriteFruitErrorActive = false mutating func validate() -> RegisterRequest? { idErrorMessage = nil isIDErrorActive = false passwordErrorMessage = nil isPasswordErrorActive = false favoriteFruitErrorMessage = nil isFavoriteFruitErrorActive = false if id.isEmpty || id.count > 20 { idErrorMessage = "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isIDErrorActive = true } if password.isEmpty || password.count > 20 { passwordErrorMessage = "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isPasswordErrorActive = true } if favoriteFruit == nil { favoriteFruitErrorMessage = "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" isFavoriteFruitErrorActive = true } if isIDErrorActive || isPasswordErrorActive || isFavoriteFruitErrorActive { return nil } let request = RegisterRequest( id: id, password: password, favoriteFruit: favoriteFruit! ) return request } } struct RegisterForm { @Field var id = "" @Field var password = "" @Field var favoriteFruit: Fruit? mutating func validate() -> RegisterRequest? { $id.clearError() $password.clearError() $favoriteFruit.clearError() if id.isEmpty || id.count > 20 { idErrorMessage = "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isIDErrorActive = true } if password.isEmpty || password.count > 20 { passwordErrorMessage = "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isPasswordErrorActive = true } if favoriteFruit == nil { favoriteFruitErrorMessage = "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" isFavoriteFruitErrorActive = true } if isIDErrorActive || isPasswordErrorActive || isFavoriteFruitErrorActive { return nil } let request = RegisterRequest( id: id, password: password, favoriteFruit: favoriteFruit! ) return request } }

Slide 88

Slide 88 text

ؾʹͳΔϙΠϯτ ʮόϦσʔγϣϯॲཧ͕৑௕ʯ

Slide 89

Slide 89 text

mutating func validate() -> RegisterRequest? { $id.clearError() $password.clearError() $favoriteFruit.clearError() if id.isEmpty || id.count > 20 { $id.errorMessage = "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" $id.isErrorActive = true } if password.isEmpty || password.count > 20 { $password.errorMessage = "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" $password.isErrorActive = true } if favoriteFruit == nil { $favoriteFruit.errorMessage = "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" $favoriteFruit.isErrorActive = true } … } ΍͍ͬͯΔ͜ͱ͸ಉ͡ ৚݅ͱΤϥʔϝοηʔδ͕ҧ͏͚ͩ

Slide 90

Slide 90 text

mutating func validate() -> RegisterRequest? { $id.clearError() $password.clearError() $favoriteFruit.clearError() if id.isEmpty || id.count > 20 { $id.errorMessage = "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" $id.isErrorActive = true } if password.isEmpty || password.count > 20 { $password.errorMessage = "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" $password.isErrorActive = true } if favoriteFruit == nil { $favoriteFruit.errorMessage = "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" $favoriteFruit.isErrorActive = true } … } Ͱ͖Ε͹όϦσʔγϣϯ͕ύε͢Δ࣌ͷ৚݅Λॻ͖͍ͨ

Slide 91

Slide 91 text

ͦ͏͍͏ϝιουΛ࡞Δ

Slide 92

Slide 92 text

extension Field { mutating func validate(errorMessage: String, with validate: (Value) -> Bool) { if !validate(wrappedValue) { self.errorMessage = errorMessage isErrorActive = true } } }

Slide 93

Slide 93 text

extension Field { mutating func validate(errorMessage: String, with validate: (Value) -> Bool) { if !validate(wrappedValue) { self.errorMessage = errorMessage isErrorActive = true } } } ৚݅ͱΤϥʔϝοηʔδΛҾ਺Ͱ౉͢

Slide 94

Slide 94 text

extension Field { mutating func validate(errorMessage: String, with validate: (Value) -> Bool) { if !validate(wrappedValue) { self.errorMessage = errorMessage isErrorActive = true } } } όϦσʔγϣϯ͕௨Δ࣌ͷ৚݅ΛࢦఆͰ͖ΔΑ͏ʹͳͬͨ

Slide 95

Slide 95 text

ͳͷͰ͜Ε͕ if id.isEmpty || id.count > 20 { $id.errorMessage = "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" $id.isErrorActive = true } if password.isEmpty || password.count > 20 { $password.errorMessage = "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" $password.isErrorActive = true } if favoriteFruit == nil { $favoriteFruit.errorMessage = "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" $favoriteFruit.isErrorActive = true }

Slide 96

Slide 96 text

͜͏ͳΔ $id.validate(errorMessage: "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞") { 1 <= $0.count && $0.count <= 20 } $password.validate(errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞") { 1 <= $0.count && $0.count <= 20 } $favoriteFruit.validate(errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞") { $0 != nil }

Slide 97

Slide 97 text

͜͏ͳΔ $id.validate(errorMessage: "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞") { 1 <= $0.count && $0.count <= 20 } $password.validate(errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞") { 1 <= $0.count && $0.count <= 20 } $favoriteFruit.validate(errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞") { $0 != nil } όϦσʔγϣϯ͕ύε͢Δ৚݅Λࢦఆ͢Ε͹ྑ͍

Slide 98

Slide 98 text

͜͏ͳΔ $id.validate(errorMessage: "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞") { 1 <= $0.count && $0.count <= 20 } $password.validate(errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞") { 1 <= $0.count && $0.count <= 20 } $favoriteFruit.validate(errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞") { $0 != nil } Τϥʔจݴͱ΋Ұக͢Δ

Slide 99

Slide 99 text

struct RegisterForm { @Field var id = "" @Field var password = "" @Field var favoriteFruit: Fruit? mutating func validate() -> RegisterRequest? { $id.clearError() $password.clearError() $favoriteFruit.clearError() if id.isEmpty || id.count > 20 { idErrorMessage = "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isIDErrorActive = true } if password.isEmpty || password.count > 20 { passwordErrorMessage = "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isPasswordErrorActive = true } if favoriteFruit == nil { favoriteFruitErrorMessage = "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" isFavoriteFruitErrorActive = true } if isIDErrorActive || isPasswordErrorActive || isFavoriteFruitErrorActive { return nil } let request = RegisterRequest( id: id, password: password, favoriteFruit: favoriteFruit! ) return request } } struct RegisterForm { @Field var id = "" @Field var password = "" @Field var favoriteFruit: Fruit? mutating func validate() -> RegisterRequest? { $id.clearError() $password.clearError() $favoriteFruit.clearError() $id.validate(errorMessage: "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞") { 1 <= $0.count && $0.count <= 20 } $password.validate(errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞") { 1 <= $0.count && $0.count <= 20 } $favoriteFruit.validateNotNil(errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞") if $id.isErrorActive || $password.isErrorActive || $favoriteFruit.isErrorActive { return nil } let request = RegisterRequest( id: id, password: password, favoriteFruit: favoriteFruit! ) return request } }

Slide 100

Slide 100 text

ؾʹͳΔϙΠϯτ ʮڧ੍Ξϯϥοϓ͍ͯ͠Δʯ

Slide 101

Slide 101 text

if $id.isErrorActive || $password.isErrorActive || $favoriteFruit.isErrorActive { return nil } let request = RegisterRequest( id: id, password: password, favoriteFruit: favoriteFruit! ) return request ͜Ε

Slide 102

Slide 102 text

if $id.isErrorActive || $password.isErrorActive || $favoriteFruit.isErrorActive { return nil } let request = RegisterRequest( id: id, password: password, favoriteFruit: favoriteFruit! ) return request JGͰνΣοΫͯ͠Δ͔Β ͜ͷλΠϛϯάͰOJMʹͳΔ͜ͱ͸ͳ͍͸͕ͣͩ ҆શʹΞϯϥοϓ͍ͨ͠ͱ͜Ζ

Slide 103

Slide 103 text

HVBSEMFUΛ࢖͏

Slide 104

Slide 104 text

͜ΕΛ extension Field { mutating func validate(errorMessage: String, with validate: (Value) -> Bool) { if !validate(wrappedValue) { self.errorMessage = errorMessage isErrorActive = true } } }

Slide 105

Slide 105 text

͜͏͢Δ mutating func validate(errorMessage: String, with validate: (Value) -> Bool) -> Value? { guard validate(wrappedValue) else { self.errorMessage = errorMessage isErrorActive = true return nil } return wrappedValue } ฦΓ஋Λ௥Ճ ύεͨ͠ΒTPNF /(ͳΒOJM Λฦ͢

Slide 106

Slide 106 text

ͳͷͰ͜Ε͕ $id.validate(errorMessage: "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞") { 1 <= $0.count && $0.count <= 20 } $password.validate(errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞") { 1 <= $0.count && $0.count <= 20 } $favoriteFruit.validate(errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞") { $0 != nil } if $id.isErrorActive || $password.isErrorActive || $favoriteFruit.isErrorActive { return nil } let request = RegisterRequest( id: id, password: password, favoriteFruit: favoriteFruit! ) return request

Slide 107

Slide 107 text

͜͏ͳͬͯ let idValue = $id.validate(errorMessage: "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞") { 1 <= $0.count && $0.count <= 20 } let passwordValue = $password.validate(errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞") { 1 <= $0.count && $0.count <= 20 } let favoriteFruitValue = $favoriteFruit.validateNotNil(errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞") if $id.isErrorActive || $password.isErrorActive || $favoriteFruit.isErrorActive { return nil } let request = RegisterRequest( id: id, password: password, favoriteFruit: favoriteFruit! ) return request

Slide 108

Slide 108 text

͜͏ͳΔ let idValue = $id.validate(errorMessage: "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞") { 1 <= $0.count && $0.count <= 20 } let passwordValue = $password.validate(errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞") { 1 <= $0.count && $0.count <= 20 } let favoriteFruitValue = $favoriteFruit.validateNotNil(errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞") guard let idValue, let passwordValue, let favoriteFruitValue else { return nil } let request = RegisterRequest( id: idValue, password: passwordValue, favoriteFruit: favoriteFruitValue ) return request

Slide 109

Slide 109 text

͜͏ͳΔ let idValue = $id.validate(errorMessage: "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞") { 1 <= $0.count && $0.count <= 20 } let passwordValue = $password.validate(errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞") { 1 <= $0.count && $0.count <= 20 } let favoriteFruitValue = $favoriteFruit.validateNotNil(errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞") guard let idValue, let passwordValue, let favoriteFruitValue else { return nil } let request = RegisterRequest( id: idValue, password: passwordValue, favoriteFruit: favoriteFruitValue ) return request ͕ফ͑ͨ

Slide 110

Slide 110 text

ؾʹͳΔϙΠϯτ ʮόϦσʔγϣϯͷهड़Λָ͍ͨ͠ʯ

Slide 111

Slide 111 text

let idValue = $id.validate(errorMessage: "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞") { 1 <= $0.count && $0.count <= 20 } let passwordValue = $password.validate(errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞") { 1 <= $0.count && $0.count <= 20 } จࣈ਺νΣοΫΛ͍ͯ͠Δ ৚݅͸େମಉ͡ͳͷͰ΋ͬͱָ͍ͨ͠

Slide 112

Slide 112 text

͜͏ͨ͠Βָͦ͏ extension (String) -> Bool { static func count(isIn range: ClosedRange) -> Self { { value in range.contains(value.count) } } } let idValue = $id.validate( errorMessage: "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞", with: .count(isIn: 1 ... 20) ) ͜Μͳײ͡ʹॻ͖͍ͨ Λଧͭͱิ׬͕ग़ͯ͘ΔΑ͏ͳੈք

Slide 113

Slide 113 text

͜͏ͨ͠Βָͦ͏ extension (String) -> Bool { static func count(isIn range: ClosedRange) -> Self { { value in range.contains(value.count) } } } let idValue = $id.validate( errorMessage: "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞", with: .count(isIn: 1 ... 20) ) Ҿ਺͸ΫϩʔδϟͳͷͰ&YUFOTJPOΛੜ΍ͤ͹͍͍ͷͰ͸ʁ

Slide 114

Slide 114 text

Ͱ͖·ͤΜ extension (String) -> Bool { static func count(isIn range: ClosedRange) -> Self { { value in range.contains(value.count) } } } let idValue = $id.validate( errorMessage: "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞", with: .count(isIn: 1 ... 20) ) 💥/POOPNJOBMUZQF 4USJOH #PPMDBOOPUCFFYUFOEFE

Slide 115

Slide 115 text

TUSVDUͰϥοϓ͢Δํ๏Λ࢖͏

Slide 116

Slide 116 text

Ϋϩʔδϟ͚ͩͷTUSVDUΛ࡞੒ struct ValidationRule { let validate: (Value) -> Bool } ͭͷΫϩʔδϟΛ࣋ͭ ࣮࣭ΫϩʔδϟͰ͋Δ

Slide 117

Slide 117 text

TUSVDUͳΒTUBUJDGVOD͕࢖͑Δ extension ValidationRule where Value == String { static func count(isIn range: ClosedRange) -> Self { .init { value in range.contains(value.count) } } }

Slide 118

Slide 118 text

TUSVDUͳΒTUBUJDGVOD͕࢖͑Δ extension ValidationRule where Value == String { static func count(isIn range: ClosedRange) -> Self { .init { value in range.contains(value.count) } } }

Slide 119

Slide 119 text

͜ΕΛ mutating func validate(errorMessage: String, with validate: (Value) -> Bool) -> Value? { guard validate(wrappedValue) else { self.errorMessage = errorMessage isErrorActive = true return nil } return wrappedValue }

Slide 120

Slide 120 text

͜͏͢Δ mutating func validate(errorMessage: String, with rule: ValidationRule) -> Value? { guard rule.validate(wrappedValue) else { self.errorMessage = errorMessage isErrorActive = true return nil } return wrappedValue }

Slide 121

Slide 121 text

ݟӫ͑ͷͨΊҾ਺ͷॱ൪Λม͑Δ mutating func validate(with rule: ValidationRule, errorMessage: String) -> Value? { guard rule.validate(wrappedValue) else { self.errorMessage = errorMessage isErrorActive = true return nil } return wrappedValue }

Slide 122

Slide 122 text

ͳͷͰ͜Ε͕ let idValue = $id.validate(errorMessage: "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞") { 1 <= $0.count && $0.count <= 20 } let passwordValue = $password.validate(errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞") { 1 <= $0.count && $0.count <= 20 }

Slide 123

Slide 123 text

͜͏ͳΔ let idValue = $id.validate( with: .count(isIn: 1 ... 20), errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) let passwordValue = $password.validate( with: .count(isIn: 1 ... 20), errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) ҙਤ͕఻ΘΓ΍͘͢ͳͬͨ ॻ͘ͱ͖΋ͱΓ͋͑ͣͱଧͯ͹Կ͔͠Βิ׬͞ΕΔ

Slide 124

Slide 124 text

৚͕݅ෳ਺͋Δ৔߹͸ʁ

Slide 125

Slide 125 text

ྫ͑͹ʮ൒֯ӳ਺ࣈͷΈͰdจࣈҎ಺ʯͷ৔߹ extension ValidationRule where Value == String { static func isOnlyAlphanumericAndCount(isIn range: ClosedRange) -> Self { .init { value in value.range(of: "[^a-zA-Z0-9]", options: .regularExpression) == nil && range.contains(value.count) } } }

Slide 126

Slide 126 text

ྫ͑͹ʮ൒֯ӳ਺ࣈͷΈͰdจࣈҎ಺ʯͷ৔߹ extension ValidationRule where Value == String { static func isOnlyAlphanumericAndCount(isIn range: ClosedRange) -> Self { .init { value in value.range(of: "[^a-zA-Z0-9]", options: .regularExpression) == nil && range.contains(value.count) } } } จࣈ਺νΣοΫ ൒֯ӳ਺ࣈͷΈͷνΣοΫ ͍͍࣮ͪͪ૷͠ͳ͍ͱ͍͚ͳ͍ʜʁ

Slide 127

Slide 127 text

Ͱ߹੒͍ͨ͠ let idValue = $id.validate( with: .count(isIn: 1 ... 20) && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) ͜͏͍͏;͏ʹॻ͖͍ͨ

Slide 128

Slide 128 text

ΫϩʔδϟΛ߹੒͢Δ struct ValidationRule { let validate: (Value) -> Bool static func && (_ lhs: Self, _ rhs: Self) -> Self { ValidationRule { value in lhs.validate(value) && rhs.validate(value) } } } ͭͷ7BMJEBUJPO3VMFΛड͚औΔ

Slide 129

Slide 129 text

struct ValidationRule { let validate: (Value) -> Bool static func && (_ lhs: Self, _ rhs: Self) -> Self { ValidationRule { value in lhs.validate(value) && rhs.validate(value) } } } ಺෦తʹ྆ऀͷ݁ՌΛͰܨ͛Δ ΫϩʔδϟΛ߹੒͢Δ

Slide 130

Slide 130 text

struct ValidationRule { let validate: (Value) -> Bool static func && (_ lhs: Self, _ rhs: Self) -> Self { ValidationRule { value in lhs.validate(value) && rhs.validate(value) } } } ͦͷॲཧΛ৽ͨͳ7BMJEBUJPO3VMFͰϥοϓ͢Δ ΫϩʔδϟΛ߹੒͢Δ

Slide 131

Slide 131 text

Ͱ͖ͨ let idValue = $id.validate( with: .count(isIn: 1 ... 20) && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) ಠࣗͷϧʔϧΛ౎౓ఆٛ͢Δඞཁ͕ͳ͘ͳΔ

Slide 132

Slide 132 text

ݱ࣌఺ͷ3FHJTUFS'PSN

Slide 133

Slide 133 text

struct RegisterForm { @Field var id = "" @Field var password = "" @Field var favoriteFruit: Fruit? mutating func validate() -> RegisterRequest? { $id.clearError() $password.clearError() $favoriteFruit.clearError() let idValue = $id.validate( with: .count(isIn: 1 ... 20) && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) let passwordValue = $password.validate( with: .count(isIn: 1 ... 20), errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) let favoriteFruitValue = $favoriteFruit.validateNotNil( errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" ) guard let idValue, let passwordValue, let favoriteFruitValue else { return nil } let request = RegisterRequest( id: idValue, password: passwordValue, favoriteFruit: favoriteFruitValue ) return request } }

Slide 134

Slide 134 text

struct RegisterForm { @Field var id = "" @Field var password = "" @Field var favoriteFruit: Fruit? mutating func validate() -> RegisterRequest? { $id.clearError() $password.clearError() $favoriteFruit.clearError() let idValue = $id.validate( with: .count(isIn: 1 ... 20) && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) let passwordValue = $password.validate( with: .count(isIn: 1 ... 20), errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) let favoriteFruitValue = $favoriteFruit.validateNotNil( errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" ) guard let idValue, let passwordValue, let favoriteFruitValue else { return nil } let request = RegisterRequest( id: idValue, password: passwordValue, favoriteFruit: favoriteFruitValue ) return request } } Τϥʔͷঢ়ଶ͸!'JFMEʹؚ·Ε͍ͯΔ

Slide 135

Slide 135 text

struct RegisterForm { @Field var id = "" @Field var password = "" @Field var favoriteFruit: Fruit? mutating func validate() -> RegisterRequest? { $id.clearError() $password.clearError() $favoriteFruit.clearError() let idValue = $id.validate( with: .count(isIn: 1 ... 20) && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) let passwordValue = $password.validate( with: .count(isIn: 1 ... 20), errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) let favoriteFruitValue = $favoriteFruit.validateNotNil( errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" ) guard let idValue, let passwordValue, let favoriteFruitValue else { return nil } let request = RegisterRequest( id: idValue, password: passwordValue, favoriteFruit: favoriteFruitValue ) return request } } όϦσʔγϣϯͷ৚݅͸ ࠶ར༻݁߹Մೳ

Slide 136

Slide 136 text

struct RegisterForm { @Field var id = "" @Field var password = "" @Field var favoriteFruit: Fruit? mutating func validate() -> RegisterRequest? { $id.clearError() $password.clearError() $favoriteFruit.clearError() let idValue = $id.validate( with: .count(isIn: 1 ... 20) && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) let passwordValue = $password.validate( with: .count(isIn: 1 ... 20), errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) let favoriteFruitValue = $favoriteFruit.validateNotNil( errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" ) guard let idValue, let passwordValue, let favoriteFruitValue else { return nil } let request = RegisterRequest( id: idValue, password: passwordValue, favoriteFruit: favoriteFruitValue ) return request } } όϦσʔγϣϯ݁Ռ͸ ΦϓγϣφϧͰड͚औΔ

Slide 137

Slide 137 text

struct RegisterForm { @Field var id = "" @Field var password = "" @Field var favoriteFruit: Fruit? mutating func validate() -> RegisterRequest? { $id.clearError() $password.clearError() $favoriteFruit.clearError() let idValue = $id.validate( with: .count(isIn: 1 ... 20) && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) let passwordValue = $password.validate( with: .count(isIn: 1 ... 20), errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) let favoriteFruitValue = $favoriteFruit.validateNotNil( errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" ) guard let idValue, let passwordValue, let favoriteFruitValue else { return nil } let request = RegisterRequest( id: idValue, password: passwordValue, favoriteFruit: favoriteFruitValue ) return request } } શͯύε͚ͨ࣌ͩ͠ 3FHJTUFS3FRVFTUΛฦ͢

Slide 138

Slide 138 text

࠷ॳͱݟൺ΂Δ struct RegisterForm { @Field var id = "" @Field var password = "" @Field var favoriteFruit: Fruit? mutating func validate() -> RegisterRequest? { $id.clearError() $password.clearError() $favoriteFruit.clearError() let idValue = $id.validate( with: .count(isIn: 1 ... 20) && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) let passwordValue = $password.validate( with: .count(isIn: 1 ... 20), errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) let favoriteFruitValue = $favoriteFruit.validateNotNil( errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" ) guard let idValue, let passwordValue, let favoriteFruitValue else { return nil } let request = RegisterRequest( id: idValue, password: passwordValue, favoriteFruit: favoriteFruitValue ) return request } } struct RegisterForm { var id = "" { didSet { guard id != oldValue else { return } isIDErrorActive = false } } var idErrorMessage: String? var isIDErrorActive = false var password = "" { didSet { guard password != oldValue else { return } isPasswordErrorActive = false } } var passwordErrorMessage: String? var isPasswordErrorActive = false var favoriteFruit: Fruit? { didSet { guard favoriteFruit != oldValue else { return } isFavoriteFruitErrorActive = false } } var favoriteFruitErrorMessage: String? var isFavoriteFruitErrorActive = false mutating func validate() -> RegisterRequest? { idErrorMessage = nil isIDErrorActive = false passwordErrorMessage = nil isPasswordErrorActive = false favoriteFruitErrorMessage = nil isFavoriteFruitErrorActive = false if id.isEmpty || id.count > 20 { idErrorMessage = "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isIDErrorActive = true } if password.isEmpty || password.count > 20 { passwordErrorMessage = "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" isPasswordErrorActive = true } if favoriteFruit == nil { favoriteFruitErrorMessage = "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" isFavoriteFruitErrorActive = true } if isIDErrorActive || isPasswordErrorActive || isFavoriteFruitErrorActive { return nil } let request = RegisterRequest( id: id, password: password, favoriteFruit: favoriteFruit! ) return request } }

Slide 139

Slide 139 text

Α͘ͳ͖ͬͯͨ

Slide 140

Slide 140 text

΋͏গ͠೉͍͠ έʔεΛߟ͑ͯΈΔ

Slide 141

Slide 141 text

ϓϩϑΟʔϧೖྗཝΛ௥Ճ

Slide 142

Slide 142 text

ΦϑͳΒχοΫωʔϜͱ೥ྸ͸ ೖྗͰ͖ͳ͍

Slide 143

Slide 143 text

Φϑͷ৔߹όϦσʔγϣϯ͢Δඞཁ͕ͳ͍ͷͰ Τϥʔʹ͸͠ͳ͍

Slide 144

Slide 144 text

͔͠͠Φϯͷ৔߹͸ όϦσʔγϣϯͷର৅ͱͳΔ ৗʹόϦσʔγϣϯ͢ΔΘ͚Ͱ͸ͳ͘ ৚݅ʹԠͯ͢͡Δ͔͠ͳ͍͔Λม͑Δ

Slide 145

Slide 145 text

͔͠͠Φϯͷ৔߹͸ όϦσʔγϣϯͷର৅ͱͳΔ ৗʹόϦσʔγϣϯ͢ΔΘ͚Ͱ͸ͳ͘ ৚݅ʹԠͯ͢͡Δ͔͠ͳ͍͔Λม͑Δ ͋Δબ୒ࢶΛબΜ͚ͩ࣌ͩ ௥ՃͷϑΟʔϧυ͕ొ৔͢Δ Έ͍ͨͳͷͬͯΑ͋͘ΔΑͶ

Slide 146

Slide 146 text

۪௚ʹ࣮૷ͯ͠ΈΔ

Slide 147

Slide 147 text

ϓϩύςΟΛ௥Ճ struct RegisterForm { @Field var id = "" @Field var password = "" @Field var favoriteFruit: Fruit? var includesProfile = false @Field var nickName = "" @Field var age = "" } ϓϩϑΟʔϧΛؚΊΔ͔Ͳ͏͔ Τϥʔঢ়ଶʹ͸ͳΒͳ͍ͷͰ!'JFME͸ෆཁ

Slide 148

Slide 148 text

struct RegisterForm { @Field var id = "" @Field var password = "" @Field var favoriteFruit: Fruit? var includesProfile = false @Field var nickName = "" @Field var age = "" } χοΫωʔϜͱ೥ྸ ϓϩύςΟΛ௥Ճ

Slide 149

Slide 149 text

όϦσʔγϣϯॲཧΛ௥Ճ let nickNameValue: String? = $nickName.validate( with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" )

Slide 150

Slide 150 text

όϦσʔγϣϯॲཧΛ௥Ճ let nickNameValue: String? = $nickName.validate( with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) ͜ΕͰ͸ຖճόϦσʔγϣϯ͞Εͯ͠·͏

Slide 151

Slide 151 text

όϦσʔγϣϯॲཧΛ௥Ճ let nickNameValue: String?? if includesProfile { nickNameValue = $nickName.validate( with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) } else { nickNameValue = .some(nil) } ϓϩϑΟʔϧΛؚΊΔ͚࣌ͩόϦσʔγϣϯ͢Δ

Slide 152

Slide 152 text

όϦσʔγϣϯॲཧΛ௥Ճ let nickNameValue: String?? if includesProfile { nickNameValue = $nickName.validate( with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) } else { nickNameValue = .some(nil) } ೋॏΦϓγϣφϧʹͳ͍ͬͯΔ

Slide 153

Slide 153 text

όϦσʔγϣϯॲཧΛ௥Ճ let nickNameValue: String?? if includesProfile { nickNameValue = $nickName.validate( with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) } else { nickNameValue = .some(nil) } w ͜ͷม਺͕औΓ͏Δ஋͸ͭ w 4USJOH TPNF TPNF MFUWBMVF ʜϓϩϑΟʔϧೖྗΦϯͰόϦσʔγϣϯύεͨ࣌͠ w 4USJOH TPNF OJM ʜϓϩϑΟʔϧೖྗΦϑͷ࣌ w 4USJOH OJMʜόϦσʔγϣϯʹࣦഊͨ࣌͠ w ΍΍͍͜͠

Slide 154

Slide 154 text

ϦΫΤετ૊Έཱͯॲཧ΋ݟͯΈΔ let request = RegisterRequest( id: idValue, password: passwordValue, favoriteFruit: favoriteFruitValue, profile: includesProfile ? .init(nickName: nickNameValue!, age: ageValue!) : nil )

Slide 155

Slide 155 text

ϦΫΤετ૊Έཱͯॲཧ let request = RegisterRequest( id: idValue, password: passwordValue, favoriteFruit: favoriteFruitValue, profile: includesProfile ? .init(nickName: nickNameValue!, age: ageValue!) : nil ) ϓϩϑΟʔϧΛؚΊΔ͔Ͳ͏͔Ͱ஋Λม͑Δ

Slide 156

Slide 156 text

ϦΫΤετ૊Έཱͯॲཧ let request = RegisterRequest( id: idValue, password: passwordValue, favoriteFruit: favoriteFruitValue, profile: includesProfile ? .init(nickName: nickNameValue!, age: ageValue!) : nil ) ؚΊͳ͍ͳΒOJMΛઃఆ

Slide 157

Slide 157 text

ϦΫΤετ૊Έཱͯॲཧ let request = RegisterRequest( id: idValue, password: passwordValue, favoriteFruit: favoriteFruitValue, profile: includesProfile ? .init(nickName: nickNameValue!, age: ageValue!) : nil ) ؚΊΔͳΒ1SP fi MFϞσϧΛઃఆ͢Δ͕ʜ

Slide 158

Slide 158 text

ϦΫΤετ૊Έཱͯॲཧ let request = RegisterRequest( id: idValue, password: passwordValue, favoriteFruit: favoriteFruitValue, profile: includesProfile ? .init(nickName: nickNameValue!, age: ageValue!) : nil ) ·ͨڧ੍Ξϯϥοϓ͕ग़͖ͯͨ

Slide 159

Slide 159 text

όϦσʔγϣϯͨ͠Γ ͠ͳ͔ͬͨΓ͢Δͷ೉͍͠ ࣮ࡍͷϓϩδΣΫτͰ͸͜͏͍͏ͷ͕ଟ͔ͬͨΓ͢ΔͷͰख͸ൈ͚ͳ͍

Slide 160

Slide 160 text

ͱΓ͋͑ͣೋॏΦϓγϣφϧΛ ͳΜͱ͔͠Α͏

Slide 161

Slide 161 text

7BMJEBUJPO3FTVMUܕΛఆٛ enum ValidationResult { case success(Value) case failed } ࠓ·Ͱ͸खΛൈ͍ͯΤϥʔͳΒOJMͱ͍͕ͯͨ͠ ͦͷ͍ͤͰOJMͱͯ͠ѻ͍͍ͨ৔ॴͱͬͪ͝ΌʹͳΔ ͱ͍͏Θ͚Ͱઐ༻ͷܕΛ༻ҙ

Slide 162

Slide 162 text

7BMJEBUJPO3FTVMUʹஔ͖׵͍͑ͯ͘ mutating func validate(with rule: ValidationRule, errorMessage: String) -> Value? { guard rule.validate(wrappedValue) else { self.errorMessage = errorMessage isErrorActive = true return nil } return wrappedValue }

Slide 163

Slide 163 text

7BMJEBUJPO3FTVMUʹஔ͖׵͍͑ͯ͘ mutating func validate(with rule: ValidationRule, errorMessage: String) -> ValidationResult { guard rule.validate(wrappedValue) else { self.errorMessage = errorMessage isErrorActive = true return .failed } return .success(wrappedValue) }

Slide 164

Slide 164 text

͜Ε͕ let nickNameValue: String??

Slide 165

Slide 165 text

͜͏ͳΔ let nickNameValue: ValidationResult w ͜ͷม਺͕औΓ͏Δ஋͸ͭ w 7BMJEBUJPO3FTVMUTVDDFTT TPNF MFUWBMVF ʜϓϩϑΟʔϧೖྗΦϯͰόϦσʔγϣϯύεͨ࣌͠ w 7BMJEBUJPO3FTVMUTVDDFTT OJM ʜϓϩϑΟʔϧೖྗΦϑͷ࣌ w 7BMJEBUJPO3FTVMUGBJMFEʜόϦσʔγϣϯʹࣦഊͨ࣌͠ w Ϛγʹͳͬͨ

Slide 166

Slide 166 text

Αͦ͞͏ʁ

Slide 167

Slide 167 text

ͱࢥ͍͖΍ίϯύΠϧΤϥʔ💥

Slide 168

Slide 168 text

ܕ͕߹Θͳ͘ͳΔ let nickNameValue: ValidationResult if includesProfile { nickNameValue = $nickName.validate( with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) } else { nickNameValue = .success(nil) } ͜ͷϝιουͷฦΓ஋͸7BMJEBUJPO3FTVMU4USJOH ͔͠͠୅ೖઌͷม਺͸7BMJEBUJPO3FTVMU4USJOH  ܕ͕߹Θͳ͍ 💥Cannot assign value of type 'ValidationResult' to type 'ValidationResult'

Slide 169

Slide 169 text

ܕ͕߹Θͳ͘ͳΔ let nickNameValue: ValidationResult if includesProfile { nickNameValue = $nickName.validate( with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) .map(Optional.some) } else { nickNameValue = .success(nil) } 7BMJEBUJPO3FTVMUʹNBQؔ਺Λ࡞ͬͯܕΛ߹ΘͤΔ Τϥʔ͸ղফ͞Ε͕ͨΘ͔Γʹ͍͘

Slide 170

Slide 170 text

ิ଍NBQͷ࣮૷ enum ValidationResult { case success(Value) case failed func map(_ transform: (Value) -> R) -> ValidationResult { switch self { case .success(let value): return .success(transform(value)) case .failed: return .failed } } }

Slide 171

Slide 171 text

HVBSEMFU͕࢖͑ͳ͍ guard let idValue, let passwordValue, let favoriteFruitValue, let ageValue, let nickNameValue else { return nil } 💥Initializer for conditional binding must have Optional type, not 'ValidationResult' શͯ7BMJEBUJPO3FTVMUܕʢඇΦϓγϣφϧʣʹͳͬͨͷͰΤϥʔʹͳΔʢ౰વʣ

Slide 172

Slide 172 text

HVBSEMFU͕࢖͑ͳ͍ guard let idValue, let passwordValue, let favoriteFruitValue, let ageValue, let nickNameValue else { return nil } DBTFʹมߋͯ͠ղܾ ղফ͕ͨ͠ɺͳΜ͔௕͍ guard case .success(let idValue) = idValue, case .success(let passwordValue) = passwordValue, case .success(let favoriteFruitValue) = favoriteFruitValue, case .success(let ageValue) = ageValue, case .success(let nickNameValue) = nickNameValue else { return nil }

Slide 173

Slide 173 text

Ұ୴ݟฦͯ͠ΈΔ

Slide 174

Slide 174 text

let nickNameValue: ValidationResult if includesProfile { nickNameValue = $nickName.validate( with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) .map(Optional.some) } else { nickNameValue = .success(nil) } let ageValue: ValidationResult if includesProfile { ageValue = $age.validate( byTransform: Int.init, with: .range(in: 0 ... 999), errorMessage: "೥ྸ͸0ʙ999ͷൣғͰೖྗ͍ͯͩ͘͠͞" ) .map(Optional.some) } else { ageValue = .success(nil) } guard case .success(let idValue) = idValue, case .success(let passwordValue) = passwordValue, case .success(let favoriteFruitValue) = favoriteFruitValue, case .success(let ageValue) = ageValue, case .success(let nickNameValue) = nickNameValue else { return nil }

Slide 175

Slide 175 text

let nickNameValue: ValidationResult if includesProfile { nickNameValue = $nickName.validate( with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) .map(Optional.some) } else { nickNameValue = .success(nil) } let ageValue: ValidationResult if includesProfile { ageValue = $age.validate( byTransform: Int.init, with: .range(in: 0 ... 999), errorMessage: "೥ྸ͸0ʙ999ͷൣғͰೖྗ͍ͯͩ͘͠͞" ) .map(Optional.some) } else { ageValue = .success(nil) } guard case .success(let idValue) = idValue, case .success(let passwordValue) = passwordValue, case .success(let favoriteFruitValue) = favoriteFruitValue, case .success(let ageValue) = ageValue, case .success(let nickNameValue) = nickNameValue else { return nil } ৚݅෇͖όϦσʔγϣϯͷͨΊͷJG

Slide 176

Slide 176 text

let nickNameValue: ValidationResult if includesProfile { nickNameValue = $nickName.validate( with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) .map(Optional.some) } else { nickNameValue = .success(nil) } let ageValue: ValidationResult if includesProfile { ageValue = $age.validate( byTransform: Int.init, with: .range(in: 0 ... 999), errorMessage: "೥ྸ͸0ʙ999ͷൣғͰೖྗ͍ͯͩ͘͠͞" ) .map(Optional.some) } else { ageValue = .success(nil) } guard case .success(let idValue) = idValue, case .success(let passwordValue) = passwordValue, case .success(let favoriteFruitValue) = favoriteFruitValue, case .success(let ageValue) = ageValue, case .success(let nickNameValue) = nickNameValue else { return nil } ܕΛ߹ΘͤΔͨΊͷNBQ ͺͬͱݟԿ͍ͯ͠Δ͔Θ͔Βͳ͍

Slide 177

Slide 177 text

let nickNameValue: ValidationResult if includesProfile { nickNameValue = $nickName.validate( with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) .map(Optional.some) } else { nickNameValue = .success(nil) } let ageValue: ValidationResult if includesProfile { ageValue = $age.validate( byTransform: Int.init, with: .range(in: 0 ... 999), errorMessage: "೥ྸ͸0ʙ999ͷൣғͰೖྗ͍ͯͩ͘͠͞" ) .map(Optional.some) } else { ageValue = .success(nil) } guard case .success(let idValue) = idValue, case .success(let passwordValue) = passwordValue, case .success(let favoriteFruitValue) = favoriteFruitValue, case .success(let ageValue) = ageValue, case .success(let nickNameValue) = nickNameValue else { return nil } ৚݅෇͖όϦσʔγϣϯେมʜ ΋ͬͱָʹॻ͚ͳ͍΋ͷ͔

Slide 178

Slide 178 text

💡

Slide 179

Slide 179 text

ʮ4XJGU6*Έ͍ͨͳ਌͠Έ΍͍͢ γϯλοΫεͰॻ͚ͨΒ͍͍ͷʹʯ

Slide 180

Slide 180 text

ʮ4XJGU6*ͷ͋ͷγϯλοΫε ͸3FTVMU#VJMEFS΍ͳʯ

Slide 181

Slide 181 text

ʮ͋Εʁ ࠓͷঢ়گͰ࢖͑ΔͷͰ͸ʁʯ

Slide 182

Slide 182 text

3FTVMU#VJMEFSΛ࢖͏

Slide 183

Slide 183 text

3FTVMU#VJMEFSΛ࡞੒ @resultBuilder enum ValidationResultBuilder { static func buildExpression(_ expression: Content) -> ValidationResult { .success(expression) } static func buildExpression(_ expression: ValidationResult) -> ValidationResult { expression } static func buildBlock(_ result: ValidationResult) -> ValidationResult { result } static func buildOptional(_ component: ValidationResult?) -> ValidationResult { guard let component else { return .success(nil) } switch component { case .success(let value): return .success(value) case .failed: return .failed } } static func buildEither(first: ValidationResult?) -> ValidationResult { buildOptional(first) } static func buildEither(second: ValidationResult?) -> ValidationResult { buildOptional(second) } static func buildBlock() -> ValidationResult { .success(()) } static func buildBlock(_ c0: ValidationResult, _ c1: ValidationResult) -> ValidationResult<(C0, C1)> { guard let c0 = c0.successValue, let c1 = c1.successValue else { return .failed } return .success((c0, c1)) } … }

Slide 184

Slide 184 text

3FTVMU#VJMEFSΛ࡞੒ @resultBuilder enum ValidationResultBuilder { static func buildExpression(_ expression: Content) -> ValidationResult { .success(expression) } static func buildExpression(_ expression: ValidationResult) -> ValidationResult { expression } static func buildBlock(_ result: ValidationResult) -> ValidationResult { result } static func buildOptional(_ component: ValidationResult?) -> ValidationResult { guard let component else { return .success(nil) } switch component { case .success(let value): return .success(value) case .failed: return .failed } } static func buildEither(first: ValidationResult?) -> ValidationResult { buildOptional(first) } static func buildEither(second: ValidationResult?) -> ValidationResult { buildOptional(second) } static func buildBlock() -> ValidationResult { .success(()) } static func buildBlock(_ c0: ValidationResult, _ c1: ValidationResult) -> ValidationResult<(C0, C1)> { guard let c0 = c0.successValue, let c1 = c1.successValue else { return .failed } return .success((c0, c1)) } … } ৄࡉ͸ׂѪ

Slide 185

Slide 185 text

ྫ let result: ValidationResult<(Int, String)> = ValidationResult.combine { ValidationResult.success(0) ValidationResult.success("string") } DPNCJOFͱ͍͏ϝιουΛ༻ҙͨ͠ ෳ਺ͷ7BMJEBUJPO3FTVMUΛͭͷ7BMJEBUJPO3FTVMUʹ·ͱΊͯ͘ΕΔ

Slide 186

Slide 186 text

ྫ let result: ValidationResult<(Int, String)> = ValidationResult.combine { ValidationResult.success(0) ValidationResult.success("string") } *OUͷ7BMJEBUJPO3FTVMU 4USJOHͷ7BMJEBUJPO3FTVMU Λ·ͱΊΔ

Slide 187

Slide 187 text

ྫ let result: ValidationResult<(Int, String)> = ValidationResult.combine { ValidationResult.success(0) ValidationResult.success("string") } ݁Ռ͸λϓϧͱͳΔ ValidationResult ValidationResult ValidationResult<(Int, String)> 

Slide 188

Slide 188 text

ྫ let result: ValidationResult<(Int, String?)> = ValidationResult.combine { ValidationResult.success(0) if .random() { ValidationResult.success("string") } }

Slide 189

Slide 189 text

ྫ let result: ValidationResult<(Int, String?)> = ValidationResult.combine { ValidationResult.success(0) if .random() { ValidationResult.success("string") } } JG΋࢖͑Δ

Slide 190

Slide 190 text

ྫ let result: ValidationResult<(Int, String?)> = ValidationResult.combine { ValidationResult.success(0) if .random() { ValidationResult.success("string") } } Φϓγϣφϧͱͯ͠ѻΘΕΔ *GͷதʹೖΔͱTPNF ೖΒͳ͍ͱOJM ͱͳΔ

Slide 191

Slide 191 text

ྫ let result: ValidationResult<(Int, (String, Double)?)> = ValidationResult.combine { ValidationResult.success(0) if .random() { ValidationResult.success("string") ValidationResult.success(0.0) } } JGͷதͷཁૉΛ૿΍ͯ͠ΈΔ

Slide 192

Slide 192 text

ྫ let result: ValidationResult<(Int, (String, Double)?)> = ValidationResult.combine { ValidationResult.success(0) if .random() { ValidationResult.success("string") ValidationResult.success(0.0) } } λϓϧͷΦϓγϣφϧͱͳΔ

Slide 193

Slide 193 text

ྫ let result: ValidationResult<(Int, (String, Double)?)> = ValidationResult.combine { ValidationResult.success(0) if .random() { ValidationResult.success("string") ValidationResult.success(0.0) } }

Slide 194

Slide 194 text

ྫ let result: ValidationResult<(Int, (String, Double)?)> = ValidationResult.combine { ValidationResult.success(0) if .random() { ValidationResult.success("string") ValidationResult.success(0.0) } }

Slide 195

Slide 195 text

ྫ let result: ValidationResult<(Int, (String, Double)?)> = ValidationResult.combine { ValidationResult.success(0) if .random() { ValidationResult.success("string") ValidationResult.success(0.0) } }

Slide 196

Slide 196 text

ྫ let result: ValidationResult<(Int, (String, Double)?)> = ValidationResult.combine { ValidationResult.success(0) if .random() { ValidationResult.success("string") ValidationResult.success(0.0) } }

Slide 197

Slide 197 text

ྫ ValidationResult.combine { ValidationResult.combine { ValidationResult.combine { ValidationResult.combine { ValidationResult.combine { ValidationResult.success("string") } } } } } DPNCJOFͷதͰ΋DPNCJOFΛ࢖͑Δ 4XJGU6*Ͱ7JFXΛωετͰ͖Δͷͱಉ͡

Slide 198

Slide 198 text

࣮ࡍʹ࢖ͬͯΈΔ

Slide 199

Slide 199 text

mutating func validateProfile() -> ValidationResult { let result: ValidationResult<(String, Int)> = ValidationResult.combine { $nickName.validate( with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $age.validate( byTransform: Int.init, with: .range(in: 0 ... 999), errorMessage: "೥ྸ͸0ʙ999ͷൣғͰೖྗ͍ͯͩ͘͠͞" ) } return result.map { (nickName: String, age: Int) in RegisterRequest.Profile(nickName: nickName, age: age) } } ͜ͷ෦෼ͷόϦσʔγϣϯॲཧ

Slide 200

Slide 200 text

mutating func validateProfile() -> ValidationResult { let result: ValidationResult<(String, Int)> = ValidationResult.combine { $nickName.validate( with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $age.validate( byTransform: Int.init, with: .range(in: 0 ... 999), errorMessage: "೥ྸ͸0ʙ999ͷൣғͰೖྗ͍ͯͩ͘͠͞" ) } return result.map { (nickName: String, age: Int) in RegisterRequest.Profile(nickName: nickName, age: age) } }

Slide 201

Slide 201 text

mutating func validateProfile() -> ValidationResult { let result: ValidationResult<(String, Int)> = ValidationResult.combine { $nickName.validate( with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $age.validate( byTransform: Int.init, with: .range(in: 0 ... 999), errorMessage: "೥ྸ͸0ʙ999ͷൣғͰೖྗ͍ͯͩ͘͠͞" ) } return result.map { (nickName: String, age: Int) in RegisterRequest.Profile(nickName: nickName, age: age) } }

Slide 202

Slide 202 text

mutating func validateProfile() -> ValidationResult { let result: ValidationResult<(String, Int)> = ValidationResult.combine { $nickName.validate( with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $age.validate( byTransform: Int.init, with: .range(in: 0 ... 999), errorMessage: "೥ྸ͸0ʙ999ͷൣғͰೖྗ͍ͯͩ͘͠͞" ) } return result.map { (nickName: String, age: Int) in RegisterRequest.Profile(nickName: nickName, age: age) } }

Slide 203

Slide 203 text

mutating func validateProfile() -> ValidationResult { let result: ValidationResult<(String, Int)> = ValidationResult.combine { $nickName.validate( with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $age.validate( byTransform: Int.init, with: .range(in: 0 ... 999), errorMessage: "೥ྸ͸0ʙ999ͷൣғͰೖྗ͍ͯͩ͘͠͞" ) } return result.map { (nickName: String, age: Int) in RegisterRequest.Profile(nickName: nickName, age: age) } } DPNCJOFϝιουͰแΉ

Slide 204

Slide 204 text

mutating func validateProfile() -> ValidationResult { let result: ValidationResult<(String, Int)> = ValidationResult.combine { $nickName.validate( with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $age.validate( byTransform: Int.init, with: .range(in: 0 ... 999), errorMessage: "೥ྸ͸0ʙ999ͷൣғͰೖྗ͍ͯͩ͘͠͞" ) } return result.map { (nickName: String, age: Int) in RegisterRequest.Profile(nickName: nickName, age: age) } } ྆ํͷόϦσʔγϣϯ͕ύεͨ࣌͠ʹ 4USJOH *OU ͷλϓϧΛೖखͰ͖Δ

Slide 205

Slide 205 text

mutating func validateProfile() -> ValidationResult { let result: ValidationResult<(String, Int)> = ValidationResult.combine { $nickName.validate( with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $age.validate( byTransform: Int.init, with: .range(in: 0 ... 999), errorMessage: "೥ྸ͸0ʙ999ͷൣғͰೖྗ͍ͯͩ͘͠͞" ) } return result.map { (nickName: String, age: Int) in RegisterRequest.Profile(nickName: nickName, age: age) } } ྆ํͷόϦσʔγϣϯ͕ύεͨ࣌͠ʹ 4USJOH *OU ͷλϓϧΛೖखͰ͖Δ

Slide 206

Slide 206 text

mutating func validateProfile() -> ValidationResult { let result: ValidationResult<(String, Int)> = ValidationResult.combine { $nickName.validate( with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $age.validate( byTransform: Int.init, with: .range(in: 0 ... 999), errorMessage: "೥ྸ͸0ʙ999ͷൣғͰೖྗ͍ͯͩ͘͠͞" ) } return result.map { (nickName: String, age: Int) in RegisterRequest.Profile(nickName: nickName, age: age) } } ྆ํͷόϦσʔγϣϯ͕ύεͨ࣌͠ʹ 4USJOH *OU ͷλϓϧΛೖखͰ͖Δ

Slide 207

Slide 207 text

mutating func validateProfile() -> ValidationResult { let result: ValidationResult<(String, Int)> = ValidationResult.combine { $nickName.validate( with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $age.validate( byTransform: Int.init, with: .range(in: 0 ... 999), errorMessage: "೥ྸ͸0ʙ999ͷൣғͰೖྗ͍ͯͩ͘͠͞" ) } return result.map { (nickName: String, age: Int) in RegisterRequest.Profile(nickName: nickName, age: age) } } ValidationResult<(String, Int)> ValidationResult ܕΛม׵

Slide 208

Slide 208 text

let result: ValidationResult<(String, String, Fruit, RegisterRequest.Profile?)> = ValidationResult.combine { $id.validate( with: .count(isIn: 1 ... 20) && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) $password.validate( with: .count(isIn: 1 ... 20), errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $favoriteFruit.validateNotNil( errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" ) if includesProfile { validateProfile() } }

Slide 209

Slide 209 text

let result: ValidationResult<(String, String, Fruit, RegisterRequest.Profile?)> = ValidationResult.combine { $id.validate( with: .count(isIn: 1 ... 20) && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) $password.validate( with: .count(isIn: 1 ... 20), errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $favoriteFruit.validateNotNil( errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" ) if includesProfile { validateProfile() } }

Slide 210

Slide 210 text

let result: ValidationResult<(String, String, Fruit, RegisterRequest.Profile?)> = ValidationResult.combine { $id.validate( with: .count(isIn: 1 ... 20) && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) $password.validate( with: .count(isIn: 1 ... 20), errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $favoriteFruit.validateNotNil( errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" ) if includesProfile { validateProfile() } }

Slide 211

Slide 211 text

let result: ValidationResult<(String, String, Fruit, RegisterRequest.Profile?)> = ValidationResult.combine { $id.validate( with: .count(isIn: 1 ... 20) && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) $password.validate( with: .count(isIn: 1 ... 20), errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $favoriteFruit.validateNotNil( errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" ) if includesProfile { validateProfile() } }

Slide 212

Slide 212 text

let result: ValidationResult<(String, String, Fruit, RegisterRequest.Profile?)> = ValidationResult.combine { $id.validate( with: .count(isIn: 1 ... 20) && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) $password.validate( with: .count(isIn: 1 ... 20), errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $favoriteFruit.validateNotNil( errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" ) if includesProfile { validateProfile() } }

Slide 213

Slide 213 text

let result: ValidationResult<(String, String, Fruit, RegisterRequest.Profile?)> = ValidationResult.combine { $id.validate( with: .count(isIn: 1 ... 20) && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) $password.validate( with: .count(isIn: 1 ... 20), errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $favoriteFruit.validateNotNil( errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" ) if includesProfile { validateProfile() } } JODMVEFT1SP fi MF͕USVFͷ࣌͸TPNF GBMTFͷ࣌͸OJMʹͳΔ

Slide 214

Slide 214 text

let result: ValidationResult<(String, String, Fruit, RegisterRequest.Profile?)> = ValidationResult.combine { $id.validate( with: .count(isIn: 1 ... 20) && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) $password.validate( with: .count(isIn: 1 ... 20), errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $favoriteFruit.validateNotNil( errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" ) if includesProfile { validateProfile() } } ʮϓϩϑΟʔϧೖྗཝ͕Φϯͷ࣌ʹϓϩϑΟʔϧ෦෼ΛόϦσʔγϣϯ͢Δʯ ͱ͍͏࢓༷͕ίʔυͰදݱͰ͖͍ͯΔ

Slide 215

Slide 215 text

let result: ValidationResult<(String, String, Fruit, RegisterRequest.Profile?)> = ValidationResult.combine { $id.validate( with: .count(isIn: 1 ... 20) && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) $password.validate( with: .count(isIn: 1 ... 20), errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $favoriteFruit.validateNotNil( errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" ) if includesProfile { validateProfile() } } return result.map { id, password, favoriteFruit, profile in RegisterRequest(id: id, password: password, favoriteFruit: favoriteFruit, profile: profile) } ValidationResult<(String, String, Fruit, RegisterRequest.Profile?)> ValidationResult

Slide 216

Slide 216 text

let result: ValidationResult<(String, String, Fruit, RegisterRequest.Profile?)> = ValidationResult.combine { $id.validate( with: .count(isIn: 1 ... 20) && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) $password.validate( with: .count(isIn: 1 ... 20), errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $favoriteFruit.validateNotNil( errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" ) if includesProfile { validateProfile() } } return result.map(RegisterRequest.init) Ҿ਺ͷॱ൪ͱλϓϧͷॱ൪͕ಉ͡ͳͷͰ͜ΕͰ΋ྑ͍

Slide 217

Slide 217 text

let result: ValidationResult<(String, String, Fruit, RegisterRequest.Profile?)> = ValidationResult.combine { $id.validate( with: .count(isIn: 1 ... 20) && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) $password.validate( with: .count(isIn: 1 ... 20), errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $favoriteFruit.validateNotNil( errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" ) if includesProfile { validateProfile() } } return result.map(RegisterRequest.init) 3FTVMU#VJMEFSʹΑͬͯ৚݅෇͖όϦσʔγϣϯΛ௚ײతʹॻ͚ΔΑ͏ʹͳͬͨ

Slide 218

Slide 218 text

let result: ValidationResult<(String, String, Fruit, RegisterRequest.Profile?)> = ValidationResult.combine { $id.validate( with: .count(isIn: 1 ... 20) && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) $password.validate( with: .count(isIn: 1 ... 20), errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $favoriteFruit.validateNotNil( errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" ) if includesProfile { validateProfile() } } return result.map(RegisterRequest.init) ͍ͭͰʹHVBSEMFU΋ෆཁʹͳͬͨ

Slide 219

Slide 219 text

࠷ऴܕ

Slide 220

Slide 220 text

struct RegisterForm { @Field var id = "" @Field var password = "" @Field var favoriteFruit: Fruit? var includesProfile = false @Field var nickName = "" @Field var age = "" mutating func validate() -> ValidationResult { $id.clearError() $password.clearError() $favoriteFruit.clearError() $nickName.clearError() $age.clearError() return ValidationResult.combine { $id.validate( with: .count(isIn: 1 ... 20) && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) $password.validate( with: .count(isIn: 1 ... 20), errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $favoriteFruit.validateNotNil( errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" ) if includesProfile { validateProfile() } } .map(RegisterRequest.init) } mutating func validateProfile() -> ValidationResult { ValidationResult.combine { $nickName.validate( with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $age.validate( byTransform: Int.init, with: .range(in: 0 ... 999), errorMessage: "೥ྸ͸0ʙ999ͷൣғͰೖྗ͍ͯͩ͘͠͞" ) } .map(RegisterRequest.Profile.init) } }

Slide 221

Slide 221 text

struct RegisterForm { @Field var id = "" @Field var password = "" @Field var favoriteFruit: Fruit? var includesProfile = false @Field var nickName = "" @Field var age = "" mutating func validate() -> ValidationResult { $id.clearError() $password.clearError() $favoriteFruit.clearError() $nickName.clearError() $age.clearError() return ValidationResult.combine { $id.validate( with: .count(isIn: 1 ... 20) && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) $password.validate( with: .count(isIn: 1 ... 20), errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $favoriteFruit.validateNotNil( errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" ) if includesProfile { validateProfile() } } .map(RegisterRequest.init) } mutating func validateProfile() -> ValidationResult { ValidationResult.combine { $nickName.validate( with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $age.validate( byTransform: Int.init, with: .range(in: 0 ... 999), errorMessage: "೥ྸ͸0ʙ999ͷൣғͰೖྗ͍ͯͩ͘͠͞" ) } .map(RegisterRequest.Profile.init) } } 7JFX͔Βঢ়ଶͱϩδοΫΛ෼཭ͤͨ͞

Slide 222

Slide 222 text

struct RegisterForm { @Field var id = "" @Field var password = "" @Field var favoriteFruit: Fruit? var includesProfile = false @Field var nickName = "" @Field var age = "" mutating func validate() -> ValidationResult { $id.clearError() $password.clearError() $favoriteFruit.clearError() $nickName.clearError() $age.clearError() return ValidationResult.combine { $id.validate( with: .count(isIn: 1 ... 20) && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) $password.validate( with: .count(isIn: 1 ... 20), errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $favoriteFruit.validateNotNil( errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" ) if includesProfile { validateProfile() } } .map(RegisterRequest.init) } mutating func validateProfile() -> ValidationResult { ValidationResult.combine { $nickName.validate( with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $age.validate( byTransform: Int.init, with: .range(in: 0 ... 999), errorMessage: "೥ྸ͸0ʙ999ͷൣғͰೖྗ͍ͯͩ͘͠͞" ) } .map(RegisterRequest.Profile.init) } } ϘΠϥʔϓϨʔτΛ1SPQFSUZ8SBQQFSͰղܾ

Slide 223

Slide 223 text

struct RegisterForm { @Field var id = "" @Field var password = "" @Field var favoriteFruit: Fruit? var includesProfile = false @Field var nickName = "" @Field var age = "" mutating func validate() -> ValidationResult { $id.clearError() $password.clearError() $favoriteFruit.clearError() $nickName.clearError() $age.clearError() return ValidationResult.combine { $id.validate( with: .count(isIn: 1 ... 20) && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) $password.validate( with: .count(isIn: 1 ... 20), errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $favoriteFruit.validateNotNil( errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" ) if includesProfile { validateProfile() } } .map(RegisterRequest.init) } mutating func validateProfile() -> ValidationResult { ValidationResult.combine { $nickName.validate( with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $age.validate( byTransform: Int.init, with: .range(in: 0 ... 999), errorMessage: "೥ྸ͸0ʙ999ͷൣғͰೖྗ͍ͯͩ͘͠͞" ) } .map(RegisterRequest.Profile.init) } } όϦσʔγϣϯ৚݅Λ ࠶ར༻Մೳ ίʔυิ׬Մೳ ݁߹Մೳ ʹͨ͠

Slide 224

Slide 224 text

struct RegisterForm { @Field var id = "" @Field var password = "" @Field var favoriteFruit: Fruit? var includesProfile = false @Field var nickName = "" @Field var age = "" mutating func validate() -> ValidationResult { $id.clearError() $password.clearError() $favoriteFruit.clearError() $nickName.clearError() $age.clearError() return ValidationResult.combine { $id.validate( with: .count(isIn: 1 ... 20) && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) $password.validate( with: .count(isIn: 1 ... 20), errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $favoriteFruit.validateNotNil( errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" ) if includesProfile { validateProfile() } } .map(RegisterRequest.init) } mutating func validateProfile() -> ValidationResult { ValidationResult.combine { $nickName.validate( with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $age.validate( byTransform: Int.init, with: .range(in: 0 ... 999), errorMessage: "೥ྸ͸0ʙ999ͷൣғͰೖྗ͍ͯͩ͘͠͞" ) } .map(RegisterRequest.Profile.init) } } 3FTVMU#VJMEFSͰ৚݅෇͖όϦσʔγϣϯΛ ௚ײతʹهड़

Slide 225

Slide 225 text

struct RegisterForm { @Field var id = "" @Field var password = "" @Field var favoriteFruit: Fruit? var includesProfile = false @Field var nickName = "" @Field var age = "" mutating func validate() -> ValidationResult { $id.clearError() $password.clearError() $favoriteFruit.clearError() $nickName.clearError() $age.clearError() return ValidationResult.combine { $id.validate( with: .count(isIn: 1 ... 20) && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) $password.validate( with: .count(isIn: 1 ... 20), errorMessage: "ύεϫʔυ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $favoriteFruit.validateNotNil( errorMessage: "޷͖ͳϑϧʔπΛબ୒͍ͯͩ͘͠͞" ) if includesProfile { validateProfile() } } .map(RegisterRequest.init) } mutating func validateProfile() -> ValidationResult { ValidationResult.combine { $nickName.validate( with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) $age.validate( byTransform: Int.init, with: .range(in: 0 ... 999), errorMessage: "೥ྸ͸0ʙ999ͷൣғͰೖྗ͍ͯͩ͘͠͞" ) } .map(RegisterRequest.Profile.init) } } 3FTVMU#VJMEFSʹΑΓ7BMJEBUJPO3FTVMUΛ߹੒Մೳʹ

Slide 226

Slide 226 text

࣮ࡍ΍ͬͯΈͨ݁Ռ w ͏·͍ͬͨ͘ͷͰ͸ͳ͍͔ͱࢥ͏ w ࠷ॳʹʮ͜͏͍͏࣮૷Ͱ౷Ұ͠·͢ʯͱ͍͏ϨΫνϟʔΛͨ͠ w େ͖ͳτϥϒϧ΋ͳ͘͜ͷελΠϧͰ࣮૷͖ͬͨ͠ w ଞϓϩδΣΫτͰ΋ྲྀ༻͍͔ͨ͠ͱ͍͏ͱ΍͸Γن໛࣍ୈɻগͳ͍ͳΒ۪ ௚ʹ΍Δͷ͕ྑͦ͞͏

Slide 227

Slide 227 text

·ͱΊ w େྔͷը໘Λ࣮૷͕όϥόϥʹͳΒͳ͍Α͏ͳઃܭΛߟ͑ͨ w ͸͡Ί͔Β͏·͘࡞Εͳ͍ͷͰʮ۪௚ʹ࡞Δˠ௚͢ʯΛ܁Γฦͨ͠

Slide 228

Slide 228 text

࿩ͤͳ͔ͬͨ͜ͱ w Τϥʔͷཧ༝ʹΑͬͯΤϥʔϝοηʔδΛม͍͑ͨ࣌Ͳ͏͢Δͷ͔ w 7JFXଆͰ͸&OWJSPONFOUΛ࢖ͬͯΤϥʔͷঢ়ଶΛԼҐͷ7JFXʹ఻͑ ͍ͯΔ͜ͱ

Slide 229

Slide 229 text

͋Γͦ͏ͳ࣭໰ w 2όϦσʔγϣϯॲཧ๯಄ͷΤϥʔঢ়ଶͷϦηοτ͸WBMJEBUFϝιου ͷλΠϛϯάͰ΍Ε͹ྑ͍ͷͰ͸ʁ w "Τϥʔ͕ى͖ͨޙʹόϦσʔγϣϯର৅֎ʹͳΔͱWBMJEBUFϝιου ͕ݺ͹Εͳ͍ͷͰΤϥʔঢ়ଶ͕Ϧηοτ͞Εͳ͍··ʹͳͬͯ͠·͏ͨΊ ͦΕ͸Ͱ͖·ͤΜͰͨ͠ɻ