🏓
【SwiftUI】Combineを用いたフォームバリデーション
import SwiftUI
import Combine
final class ViewModel: ObservableObject {
@Published var name = ""
@Published var isValidName = false
@Published var password = ""
@Published var isValidPassword = false
@Published var isButtonEnabled = false
static let shared = ViewModel()
private init() {
$name
.removeDuplicates()
.map { str in
return str.count >= 4
}
.assign(to: &$isValidName)
$password
.removeDuplicates()
.map { str in
return str.count >= 8
}
.assign(to: &$isValidPassword)
Publishers.CombineLatest($isValidName, $isValidPassword)
.map { isValidName, isValidPassword in
return isValidName && isValidPassword
}
.assign(to: &$isButtonEnabled)
}
}
struct ContentView: View {
@StateObject var vm = ViewModel.shared
var body: some View {
VStack {
HStack {
TextField("Name", text: $vm.name)
.textFieldStyle(.roundedBorder)
Image(systemName: "checkmark")
.foregroundStyle(vm.isValidName ? .green : .gray)
.fontWeight(vm.isValidName ? .bold : nil)
}
.padding()
HStack {
TextField("Password", text: $vm.password)
.textFieldStyle(.roundedBorder)
Image(systemName: "checkmark")
.foregroundStyle(vm.isValidPassword ? .green : .gray)
.fontWeight(vm.isValidPassword ? .bold : nil)
}
.padding()
Button {
print("name: \(vm.name)")
print("password: \(vm.password)")
} label: {
Text("Submit")
}
.disabled(!vm.isButtonEnabled)
.buttonStyle(.borderedProminent)
}
}
}
#Preview {
ContentView()
}
$nameにcombineLatest($password)で繋げてもいいが、$nameと$passwordの関係性は対等のため、Publishers.CombineLatest($isValidName, $isValidPassword)"としている
個人的な趣向
実際のところ".disabled(!vm.isButtonEnabled)"は".disabled(vm.isValidName && vm.isValidPassword ? false : true)"でOK
Discussion