Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

バリデーション付きフォームを宣言的に実装する / Declaratively Implemen...

バリデーション付きフォームを宣言的に実装する / Declaratively Implementing a Form with Validation

Studio Rookery

May 30, 2024
Tweet

More Decks by Studio Rookery

Other Decks in Programming

Transcript

  1. ͜ΕʹόϦσʔγϣϯॲཧΛ଍͍ͯ͘͠ 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() } } }
  2. ·ͣ͸ঢ়ଶ͔Βߟ͑Δ 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() } } }
  3. ͜͏ͳΔ @State var id = “" @State var idErrorMessage: String?

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

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

    @State var isIDErrorActive = false ϑΟʔϧυʹ͖ͭม਺͕ඞཁʂ
  6. ͭ·Γ͜Ε͕ @State var id = "" @State var password =

    "" @State var favoriteFruit: Fruit?
  7. ͜͏ͳΔ @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
  8. ͜͏ͳΔ @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 ͢Ͱʹେมͦ͏͚ͩͲ ·ͣ͸۪௚ʹ࡞ΔͷͰ͜ΕͰਐΊΔ
  9. 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) } }
  10. 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) } } Τϥʔͷঢ়ଶΛϦηοτ
  11. 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) } } όϦσʔγϣϯΛύε͠ͳ͚Ε͹Τϥʔঢ়ଶʹ͢Δ
  12. 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*Λ౤͛Δ
  13. ͜͏ͳΔ 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 }
  14. .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 } ৄࡉ͸ׂѪ
  15. 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) } } }
  16. 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Λ࢖͍ͬͯΔͱςετॻ͚ͳ͍
  17. 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) } } } ϨΠΞ΢τͷίʔυͱൺ΂Δͱ όϦσʔγϣϯॲཧ௕͗͢
  18. 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) } } }
  19. ͜ͷ෦෼Λ 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) } } }
  20. Ҡಈ 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) } } }
  21. ͜ͷ෦෼Λ 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) } } }
  22. Ҡಈ 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) } } }
  23. !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 }
  24. 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 }
  25. 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
  26. ͭ·Γ͜Ε͕ 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) } } }
  27. ϑΝΠϧʹ෼ׂ 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 } }
  28. ঢ়ଶͱϩδοΫΛTUSVDU΁੾Γग़ͨ͠ w 4XJGU6*ͷ౎߹ʹҾͬுΒΕͳ͘ͳͬͨ w EJE4FU͕࢖͑ΔΑ͏ʹͳͬͨ w ςετͰ͖ΔΑ͏ʹͳͬͨ w 7JFXͷϑΝΠϧ͸6*ͷ͜ͱ͚ͩʹूத͢Ε͹ྑ͘ͳͬͨ w

    ϑΝΠϧʹ෼͔Εͨ͜ͱʹΑͬͯݟ௨͠͸গ͠μ΢ϯʁ w ྫ͕؆୯ͳͷͰϝϦοτײ͡ʹ͍͔͘΋Ͱ͕࣮͢ࡍ͸ೖྗ߲໨਺͸΋ͬͱଟ ͍ͷͰײ͡ํมΘΓͦ͏
  29. 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 }
  30. 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 } ࣅͨΑ͏ͳॲཧ
  31. 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 } ࣅͨΑ͏ͳίʔυॻ͘ͷ͕໘౗
  32. 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 } } ࣅͨΑ͏ͳॲཧ௕͍
  33. δΣωϦοΫͳTUSVDUͰඞཁͳม਺Λ·ͱΊΔ struct Field<Value: Equatable> { 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 }
  34. δΣωϦοΫͳTUSVDUͰඞཁͳม਺Λ·ͱΊΔ struct Field<Value: Equatable> { 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 }
  35. δΣωϦοΫͳTUSVDUͰඞཁͳม਺Λ·ͱΊΔ struct Field<Value: Equatable> { 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 }
  36. δΣωϦοΫͳTUSVDUͰඞཁͳม਺Λ·ͱΊΔ struct Field<Value: Equatable> { 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 }
  37. ͳͷͰ͜Ε͕ 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 }
  38. ͜͏ͳΔ 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<String> = .init(wrappedValue: “") var password: Field<String> = .init(wrappedValue: "") var favoriteFruit: Field<Fruit?> = .init(wrappedValue: nil) }
  39. ͜͏ͳΔ 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<String> = .init(wrappedValue: “") var password: Field<String> = .init(wrappedValue: "") var favoriteFruit: Field<Fruit?> = .init(wrappedValue: nil) }
  40. ͜Ε1SPQFSUZ8SBQQFSʹͰ͖Δ΍Μʂ @propertyWrapper struct Field<Value: Equatable> { var wrappedValue: Value {

    didSet { guard wrappedValue != oldValue else { return } isErrorActive = false } } var isErrorActive = false var errorMessage: String? }
  41. ͳͷͰ͜Ε͕ struct RegisterForm { var id: Field<String> = .init(wrappedValue: "")

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

    var password: Field<String> = .init(wrappedValue: "") var favoriteFruit: Field<Fruit?> = .init(wrappedValue: nil) } struct RegisterForm { @Field var id = "" @Field var password = "" @Field var favoriteFruit: Fruit? }
  43. ͜Ε΋εοΩϦͤ͞Δ mutating func validate() -> RegisterRequest? { idErrorMessage = nil

    isIDErrorActive = false passwordErrorMessage = nil isPasswordErrorActive = false favoriteFruitErrorMessage = nil isFavoriteFruitErrorActive = false … }
  44. ͜Ε΋εοΩϦͤ͞Δ @propertyWrapper struct Field<Value: Equatable> { … mutating func clearError()

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

    { isErrorActive = false errorMessage = nil } var projectedValue: Field<Value> { get { self } set { self = newValue } } } mutating func validate() -> RegisterRequest? { idErrorMessage = nil isIDErrorActive = false passwordErrorMessage = nil isPasswordErrorActive = false favoriteFruitErrorMessage = nil isFavoriteFruitErrorActive = false … }
  46. ͜Ε΋εοΩϦͤ͞Δ mutating func validate() -> RegisterRequest? { $id.clearError() $password.clearError() $favoriteFruit.clearError()

    … } @propertyWrapper struct Field<Value: Equatable> { … mutating func clearError() { isErrorActive = false errorMessage = nil } var projectedValue: Field<Value> { get { self } set { self = newValue } } }
  47. mutating func validate() -> RegisterRequest? { $id.clearError() $password.clearError() $favoriteFruit.clearError() …

    } ͜Ε΋εοΩϦͤ͞Δ @propertyWrapper struct Field<Value: Equatable> { … mutating func clearError() { isErrorActive = false errorMessage = nil } var projectedValue: Field<Value> { get { self } set { self = newValue } } }
  48. εοΩϦͨ͠ 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() … }
  49. 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 } }
  50. 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 } … } ΍͍ͬͯΔ͜ͱ͸ಉ͡ ৚݅ͱΤϥʔϝοηʔδ͕ҧ͏͚ͩ
  51. 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 } … } Ͱ͖Ε͹όϦσʔγϣϯ͕ύε͢Δ࣌ͷ৚݅Λॻ͖͍ͨ
  52. extension Field { mutating func validate(errorMessage: String, with validate: (Value)

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

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

    -> Bool) { if !validate(wrappedValue) { self.errorMessage = errorMessage isErrorActive = true } } } όϦσʔγϣϯ͕௨Δ࣌ͷ৚݅ΛࢦఆͰ͖ΔΑ͏ʹͳͬͨ
  55. ͳͷͰ͜Ε͕ 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 }
  56. ͜͏ͳΔ $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 }
  57. ͜͏ͳΔ $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 } όϦσʔγϣϯ͕ύε͢Δ৚݅Λࢦఆ͢Ε͹ྑ͍
  58. ͜͏ͳΔ $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 } Τϥʔจݴͱ΋Ұக͢Δ
  59. 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 } }
  60. if $id.isErrorActive || $password.isErrorActive || $favoriteFruit.isErrorActive { return nil }

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

    let request = RegisterRequest( id: id, password: password, favoriteFruit: favoriteFruit! ) return request JGͰνΣοΫͯ͠Δ͔Β ͜ͷλΠϛϯάͰOJMʹͳΔ͜ͱ͸ͳ͍͸͕ͣͩ ҆શʹΞϯϥοϓ͍ͨ͠ͱ͜Ζ
  62. ͜ΕΛ extension Field { mutating func validate(errorMessage: String, with validate:

    (Value) -> Bool) { if !validate(wrappedValue) { self.errorMessage = errorMessage isErrorActive = true } } }
  63. ͜͏͢Δ 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 Λฦ͢
  64. ͳͷͰ͜Ε͕ $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
  65. ͜͏ͳͬͯ 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
  66. ͜͏ͳΔ 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
  67. ͜͏ͳΔ 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 ͕ফ͑ͨ
  68. 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 } จࣈ਺νΣοΫΛ͍ͯ͠Δ ৚݅͸େମಉ͡ͳͷͰ΋ͬͱָ͍ͨ͠
  69. ͜͏ͨ͠Βָͦ͏ extension (String) -> Bool { static func count(isIn range:

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

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

    ClosedRange<Int>) -> Self { { value in range.contains(value.count) } } } let idValue = $id.validate( errorMessage: "ID͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞", with: .count(isIn: 1 ... 20) ) 💥/POOPNJOBMUZQF 4USJOH #PPMDBOOPUCFFYUFOEFE
  72. TUSVDUͳΒTUBUJDGVOD͕࢖͑Δ extension ValidationRule where Value == String { static func

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

    count(isIn range: ClosedRange<Int>) -> Self { .init { value in range.contains(value.count) } } }
  74. ͜ΕΛ mutating func validate(errorMessage: String, with validate: (Value) -> Bool)

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

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

    { guard rule.validate(wrappedValue) else { self.errorMessage = errorMessage isErrorActive = true return nil } return wrappedValue }
  77. ͳͷͰ͜Ε͕ 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 }
  78. ͜͏ͳΔ 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จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) ҙਤ͕఻ΘΓ΍͘͢ͳͬͨ ॻ͘ͱ͖΋ͱΓ͋͑ͣͱଧͯ͹Կ͔͠Βิ׬͞ΕΔ
  79. ྫ͑͹ʮ൒֯ӳ਺ࣈͷΈͰdจࣈҎ಺ʯͷ৔߹ extension ValidationRule where Value == String { static func

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

    isOnlyAlphanumericAndCount(isIn range: ClosedRange<Int>) -> Self { .init { value in value.range(of: "[^a-zA-Z0-9]", options: .regularExpression) == nil && range.contains(value.count) } } } จࣈ਺νΣοΫ ൒֯ӳ਺ࣈͷΈͷνΣοΫ ͍͍࣮ͪͪ૷͠ͳ͍ͱ͍͚ͳ͍ʜʁ
  81. Ͱ߹੒͍ͨ͠ let idValue = $id.validate( with: .count(isIn: 1 ... 20)

    && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) ͜͏͍͏;͏ʹॻ͖͍ͨ
  82. ΫϩʔδϟΛ߹੒͢Δ struct ValidationRule<Value> { let validate: (Value) -> Bool static

    func && (_ lhs: Self, _ rhs: Self) -> Self { ValidationRule { value in lhs.validate(value) && rhs.validate(value) } } } ͭͷ7BMJEBUJPO3VMFΛड͚औΔ
  83. struct ValidationRule<Value> { let validate: (Value) -> Bool static func

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

    && (_ lhs: Self, _ rhs: Self) -> Self { ValidationRule { value in lhs.validate(value) && rhs.validate(value) } } } ͦͷॲཧΛ৽ͨͳ7BMJEBUJPO3VMFͰϥοϓ͢Δ ΫϩʔδϟΛ߹੒͢Δ
  85. Ͱ͖ͨ let idValue = $id.validate( with: .count(isIn: 1 ... 20)

    && .isOnlyAlphanumeric, errorMessage: "ID͸1~20จࣈҎ಺ͷӳ਺ࣈͰೖྗ͍ͯͩ͘͠͞" ) ಠࣗͷϧʔϧΛ౎౓ఆٛ͢Δඞཁ͕ͳ͘ͳΔ
  86. 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 } }
  87. 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ʹؚ·Ε͍ͯΔ
  88. 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 } } όϦσʔγϣϯͷ৚݅͸ ࠶ར༻݁߹Մೳ
  89. 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 } } όϦσʔγϣϯ݁Ռ͸ ΦϓγϣφϧͰड͚औΔ
  90. 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Λฦ͢
  91. ࠷ॳͱݟൺ΂Δ 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 } }
  92. ϓϩύςΟΛ௥Ճ struct RegisterForm { @Field var id = "" @Field

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

    password = "" @Field var favoriteFruit: Fruit? var includesProfile = false @Field var nickName = "" @Field var age = "" } χοΫωʔϜͱ೥ྸ ϓϩύςΟΛ௥Ճ
  94. όϦσʔγϣϯॲཧΛ௥Ճ let nickNameValue: String? = $nickName.validate( with: .count(isIn: 1 ...

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

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

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

    with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) } else { nickNameValue = .some(nil) } ೋॏΦϓγϣφϧʹͳ͍ͬͯΔ
  98. όϦσʔγϣϯॲཧΛ௥Ճ 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 ΍΍͍͜͠
  99. ϦΫΤετ૊Έཱͯॲཧ΋ݟͯΈΔ let request = RegisterRequest( id: idValue, password: passwordValue, favoriteFruit:

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

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

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

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

    favoriteFruitValue, profile: includesProfile ? .init(nickName: nickNameValue!, age: ageValue!) : nil ) ·ͨڧ੍Ξϯϥοϓ͕ग़͖ͯͨ
  104. 7BMJEBUJPO3FTVMUʹஔ͖׵͍͑ͯ͘ mutating func validate(with rule: ValidationRule<Value>, errorMessage: String) -> Value?

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

    { guard rule.validate(wrappedValue) else { self.errorMessage = errorMessage isErrorActive = true return .failed } return .success(wrappedValue) }
  106. ͜͏ͳΔ let nickNameValue: ValidationResult<String?> w ͜ͷม਺͕औΓ͏Δ஋͸ͭ w 7BMJEBUJPO3FTVMUTVDDFTT TPNF MFUWBMVF

    ʜϓϩϑΟʔϧೖྗΦϯͰόϦσʔγϣϯύεͨ࣌͠ w 7BMJEBUJPO3FTVMUTVDDFTT OJM ʜϓϩϑΟʔϧೖྗΦϑͷ࣌ w 7BMJEBUJPO3FTVMUGBJMFEʜόϦσʔγϣϯʹࣦഊͨ࣌͠ w Ϛγʹͳͬͨ
  107. ܕ͕߹Θͳ͘ͳΔ let nickNameValue: ValidationResult<String?> 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<String>' to type 'ValidationResult<String?>'
  108. ܕ͕߹Θͳ͘ͳΔ let nickNameValue: ValidationResult<String?> if includesProfile { nickNameValue = $nickName.validate(

    with: .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) .map(Optional.some) } else { nickNameValue = .success(nil) } 7BMJEBUJPO3FTVMUʹNBQؔ਺Λ࡞ͬͯܕΛ߹ΘͤΔ Τϥʔ͸ղফ͞Ε͕ͨΘ͔Γʹ͍͘
  109. ิ଍NBQͷ࣮૷ enum ValidationResult<Value> { case success(Value) case failed func map<R>(_

    transform: (Value) -> R) -> ValidationResult<R> { switch self { case .success(let value): return .success(transform(value)) case .failed: return .failed } } }
  110. 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<String?>' શͯ7BMJEBUJPO3FTVMUܕʢඇΦϓγϣφϧʣʹͳͬͨͷͰΤϥʔʹͳΔʢ౰વʣ
  111. 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 }
  112. let nickNameValue: ValidationResult<String?> if includesProfile { nickNameValue = $nickName.validate( with:

    .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) .map(Optional.some) } else { nickNameValue = .success(nil) } let ageValue: ValidationResult<Int?> 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 }
  113. let nickNameValue: ValidationResult<String?> if includesProfile { nickNameValue = $nickName.validate( with:

    .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) .map(Optional.some) } else { nickNameValue = .success(nil) } let ageValue: ValidationResult<Int?> 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
  114. let nickNameValue: ValidationResult<String?> if includesProfile { nickNameValue = $nickName.validate( with:

    .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) .map(Optional.some) } else { nickNameValue = .success(nil) } let ageValue: ValidationResult<Int?> 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 ͺͬͱݟԿ͍ͯ͠Δ͔Θ͔Βͳ͍
  115. let nickNameValue: ValidationResult<String?> if includesProfile { nickNameValue = $nickName.validate( with:

    .count(isIn: 1 ... 20), errorMessage: "χοΫωʔϜ͸1~20จࣈҎ಺Ͱೖྗ͍ͯͩ͘͠͞" ) .map(Optional.some) } else { nickNameValue = .success(nil) } let ageValue: ValidationResult<Int?> 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 } ৚݅෇͖όϦσʔγϣϯେมʜ ΋ͬͱָʹॻ͚ͳ͍΋ͷ͔
  116. 3FTVMU#VJMEFSΛ࡞੒ @resultBuilder enum ValidationResultBuilder { static func buildExpression<Content>(_ expression: Content)

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

    -> ValidationResult<Content> { .success(expression) } static func buildExpression<Content>(_ expression: ValidationResult<Content>) -> ValidationResult<Content> { expression } static func buildBlock<Content>(_ result: ValidationResult<Content>) -> ValidationResult<Content> { result } static func buildOptional<Component>(_ component: ValidationResult<Component>?) -> ValidationResult<Component?> { guard let component else { return .success(nil) } switch component { case .success(let value): return .success(value) case .failed: return .failed } } static func buildEither<Content>(first: ValidationResult<Content>?) -> ValidationResult<Content?> { buildOptional(first) } static func buildEither<Content>(second: ValidationResult<Content>?) -> ValidationResult<Content?> { buildOptional(second) } static func buildBlock() -> ValidationResult<Void> { .success(()) } static func buildBlock<C0, C1>(_ c0: ValidationResult<C0>, _ c1: ValidationResult<C1>) -> ValidationResult<(C0, C1)> { guard let c0 = c0.successValue, let c1 = c1.successValue else { return .failed } return .success((c0, c1)) } … } ৄࡉ͸ׂѪ
  118. ྫ let result: ValidationResult<(Int, String)> = ValidationResult.combine { ValidationResult<Int>.success(0) ValidationResult<String>.success("string")

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

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

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

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

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

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

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

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

    if .random() { ValidationResult<String>.success("string") ValidationResult<Double>.success(0.0) } }
  127. ྫ ValidationResult.combine { ValidationResult.combine { ValidationResult.combine { ValidationResult.combine { ValidationResult.combine

    { ValidationResult.success("string") } } } } } DPNCJOFͷதͰ΋DPNCJOFΛ࢖͑Δ 4XJGU6*Ͱ7JFXΛωετͰ͖Δͷͱಉ͡
  128. mutating func validateProfile() -> ValidationResult<RegisterRequest.Profile> { 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) } } ͜ͷ෦෼ͷόϦσʔγϣϯॲཧ
  129. mutating func validateProfile() -> ValidationResult<RegisterRequest.Profile> { 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) } }
  130. mutating func validateProfile() -> ValidationResult<RegisterRequest.Profile> { 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) } }
  131. mutating func validateProfile() -> ValidationResult<RegisterRequest.Profile> { 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) } }
  132. mutating func validateProfile() -> ValidationResult<RegisterRequest.Profile> { 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ϝιουͰแΉ
  133. mutating func validateProfile() -> ValidationResult<RegisterRequest.Profile> { 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 ͷλϓϧΛೖखͰ͖Δ
  134. mutating func validateProfile() -> ValidationResult<RegisterRequest.Profile> { 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 ͷλϓϧΛೖखͰ͖Δ
  135. mutating func validateProfile() -> ValidationResult<RegisterRequest.Profile> { 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 ͷλϓϧΛೖखͰ͖Δ
  136. mutating func validateProfile() -> ValidationResult<RegisterRequest.Profile> { 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<RegisterRequest.Profile> ܕΛม׵
  137. 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() } }
  138. 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() } }
  139. 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() } }
  140. 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() } }
  141. 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() } }
  142. 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ʹͳΔ
  143. 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() } } ʮϓϩϑΟʔϧೖྗཝ͕Φϯͷ࣌ʹϓϩϑΟʔϧ෦෼ΛόϦσʔγϣϯ͢Δʯ ͱ͍͏࢓༷͕ίʔυͰදݱͰ͖͍ͯΔ
  144. 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<RegisterRequest>
  145. 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) Ҿ਺ͷॱ൪ͱλϓϧͷॱ൪͕ಉ͡ͳͷͰ͜ΕͰ΋ྑ͍
  146. 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ʹΑͬͯ৚݅෇͖όϦσʔγϣϯΛ௚ײతʹॻ͚ΔΑ͏ʹͳͬͨ
  147. 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΋ෆཁʹͳͬͨ
  148. 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<RegisterRequest> { $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<RegisterRequest.Profile> { 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) } }
  149. 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<RegisterRequest> { $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<RegisterRequest.Profile> { 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͔Βঢ়ଶͱϩδοΫΛ෼཭ͤͨ͞
  150. 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<RegisterRequest> { $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<RegisterRequest.Profile> { 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Ͱղܾ
  151. 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<RegisterRequest> { $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<RegisterRequest.Profile> { 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) } } όϦσʔγϣϯ৚݅Λ ࠶ར༻Մೳ ίʔυิ׬Մೳ ݁߹Մೳ ʹͨ͠
  152. 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<RegisterRequest> { $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<RegisterRequest.Profile> { 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Ͱ৚݅෇͖όϦσʔγϣϯΛ ௚ײతʹهड़
  153. 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<RegisterRequest> { $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<RegisterRequest.Profile> { 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Λ߹੒Մೳʹ