I'm building a game where players (locally) need to describe a number words to their teammates, who in turn guess as many words as possible within a given time limit. This means I have to let them create teams and add players to those teams.
I've set up Core Data and created a SwiftUI view for creating teams and adding players to those teams: this works. I'm now building a view to edit the team properties, but I'm struggling with data validation using the .disabled modifier. I want to make sure that the 'Save' button gets disabled when less than 2 people are in a team and when any of the fields (team name or any of player names) are empty.
What happens is that when editing the team name (a direct property of the Team entity), my .disabled modifier gets triggered as planned, and thus I can check whether it's empty or not. However, when editing any of the player names, the function passed to the .disabled modifier does not trigger, so I can't check if the player name is empty or not.
Below is my code:
struct EditTeamsView: View {
@Environment(\.presentationMode) var presentationMode
@Environment(\.managedObjectContext) var managedObjectContext
@ObservedObject var existingTeam: Team
var body: some View {
ZStack {
Color.init(UIColor.systemGroupedBackground)
.edgesIgnoringSafeArea(.all)
VStack {
Form {
// Team name textfield
Section(footer: Text("A team needs a minimum of 2 players, and can have a maximum of 5 players.")) {
TextField("Team name...", text: $existingTeam.name ?? "")
}
Section {
ForEach(existingTeam.playerArray, id: \.self) { player in
TextField("Player name...", text: Binding(
get: {
player.wrappedName
}, set: { newValue in
player.name = newValue
}))
}
.onDelete(perform: deletePlayers)
}
}
StockFullWidthButton(action: {
saveChanges()
self.presentationMode.wrappedValue.dismiss()
}, label: "Save Changes")
.disabled(!isInputValid())
}
}
.navigationBarTitle("Edit Team", displayMode: .inline)
}
func deletePlayers(at offsets: IndexSet) {
for index in offsets {
let playerToRemove = existingTeam.playerArray[index]
existingTeam.removeFromPlayers(playerToRemove)
}
}
func isInputValid() -> Bool {
print("Input is checked")
guard !existingTeam.name!.isEmpty else { return false }
guard existingTeam.playerArray.count >= 2 else { return false }
for player in existingTeam.playerArray {
if player.name!.isEmpty {
return false
}
}
return true
}
func saveChanges() {
if managedObjectContext.hasChanges {
try? self.managedObjectContext.save()
}
}
}
For the team name TextField binding I used this operator overload (taken from another post on SO). This works nicely for the team name textfield, but doesn't work for the player names.
func ??<T>(lhs: Binding<Optional<T>>, rhs: T) -> Binding<T> {
Binding(
get: { lhs.wrappedValue ?? rhs },
set: { lhs.wrappedValue = $0 }
)
}
That's why I used a custom binding for the player names textfields:
Binding(get: { player.wrappedName }, set: { newValue in player.name = newValue })
Any idea on how to fix this? Thanks in advance!