Swift Equatable Struct не позволяет обновляться?

Я пытаюсь понять Equatable. Когда я использую Equatable в своей структуре CreateCustomer, почему я не могу добавить больше типов телефонов, если я установил один, или когда я добавил больше, почему я могу установить только один? Без Equatable в моей структуре все работает нормально.

Вот мое представление SwiftUI для установки типа телефона

struct T01: View{
    @State var phoneTypes: [String] = ["Other", "Home", "Service", "Work", "Cell"]
    @State var customerCreate: CreateCustomer = CreateCustomer()
    var body: some View {
        VStack{
            if (customerCreate != CreateCustomer()){
                Button(action: {
                    customerCreate = CreateCustomer()
                }, label: {
                    Text("Clear").padding()
                })
            }
            ForEach($customerCreate.phone.indices, id: \.self) { i in
                Menu {
                    ForEach(phoneTypes, id: \.self){ client in
                        Button() {
                            let x = client
                            customerCreate.phone[i].phoneType = x
                            print(customerCreate.phone[i].phoneType)
                        } label:{
                            Text(client)
                            if customerCreate.phone[i].phoneType == client
                            {
                                Image(systemName: "checkmark")
                            }
                        }
                    }
                } label: {
                    VStack{
                        HStack{
                            Spacer()
                            Text(customerCreate.phone[i].phoneType.isEmpty ? "Select the phone type *" : customerCreate.phone[i].phoneType)
                                .foregroundColor(customerCreate.phone[i].phoneType.isEmpty ? .gray : .black)
                            Image(systemName: "chevron.down")
                                .foregroundColor(Color.green)
                            Spacer()
                        }
                    }
                }
            }
            Button(action: {
                customerCreate.addPhone()
            }, label: {
                HStack {
                    Image(systemName: "plus.circle")
                        .font(.system(size: 15))
                    Text("Add Phone")
                        .fontWeight(.thin)
                        .font(.system(size: 15))
                }
            })
        }
    }
}

struct CreateCustomer: Codable, Equatable {
    static func == (lhs: CreateCustomer, rhs: CreateCustomer) -> Bool {
        // It can be fixed by changing == to > but I want the == so I can know if I should display the clear button or not.
        return String(lhs.phone.first?.phoneType ?? "") == String(rhs.phone.first?.phoneType ?? "")
    }
    
    var phone: [CustomerPhone]
    init() {
        phone = [CustomerPhone()]
    }
    public mutating func addPhone(){
        phone.append(CustomerPhone())
    }
}

struct CustomerPhone: Codable {
    var phone: String
    var phoneType: String
    init(){
        phone = ""
        phoneType = ""
    }
}

Спасибо за любую помощь!!!!

Несколько вещей, прежде чем попытаться ответить на вопрос. Использование соглашений о кодировании Swift облегчает чтение вашего кода, поэтому используйте UpperCamelCase для имен типов и протоколов и LowerCamelCase для всего остального (и змеиный регистр нигде). Так что customerCreate вместо Customer_Create, var phone: String вместо var Phone: String и т. д. Кроме того, пожалуйста, просто используйте минимальный код, демонстрирующий проблему, и убедитесь, что он компилируется (приведенный выше код не компилируется). Для справки см. минимальный воспроизводимый пример

Ashley Mills 10.01.2023 16:13

@AshleyMills Спасибо, я пытался сделать его более минимальным. Кажется, что это ошибка только тогда, когда есть структура внутри другой структуры, что делает код немного запутанным для чтения.

micah 10.01.2023 16:55

Очень сложно понять ваш код, например customerCreate != CreateCustomer() и phone.append(CustomerPhone()). Почему вы создаете новые объекты во многих местах?

Joakim Danielson 10.01.2023 17:17

@JoakimDanielson customerCreate != CreateCustomer() Это сравнивает локальный объект с пустым, чтобы определить, должна ли быть видна кнопка очистки, для которой требуется Equatable . В то время как phone.append(CustomerPhone()) должен добавить новый телефон клиента в массив и работает только без Equatable. Я постараюсь добавить некоторые комментарии.

micah 10.01.2023 17:23

Это IMO не лучший способ сделать это, лучше, чем иметь вычисляемое свойство или функцию, которая возвращает логическое значение, является ли объект пустым или нет.

Joakim Danielson 10.01.2023 17:26

@JoakimDanielson Спасибо! Звучит хорошо, я сделаю это и удалю Equatable. Тем не менее интересно, что добавление Equatable нарушает его функциональность.

micah 10.01.2023 17:31
indices и ярости небезопасны в SwiftUI, вы должны использовать сами объекты и соответствовать Identifiable
lorem ipsum 10.01.2023 18:18

Разве ForEach($customerCreate.phone.indices) не сломан полностью? Я не могу протестировать его прямо сейчас, но я ожидаю, что он будет обновлять содержимое своего представления только при изменении количества элементов (что приводит к изменению индексов), но не при изменении любой из записей на месте.

Alexander 10.01.2023 21:15

@Alexander Александр, да, Equatable static func == (lhs: CreateCustomer, rhs: CreateCustomer), кажется, ломает обновление, поэтому я изменил его, чтобы использовать var isEmpty: Bool как решение, и теперь оно работает нормально :)

micah 10.01.2023 21:22

@micah, ты понимаешь, что здесь делает соответствие Equatable? Я думаю, что есть недостающая часть, которая вызывает у вас некоторое замешательство.

Alexander 11.01.2023 16:31
Конечные и Readonly классы в PHP
Конечные и Readonly классы в PHP
В прошлом, когда вы не хотели, чтобы другие классы расширяли определенный класс, вы могли пометить его как final.
От React к React Native: Руководство для начинающих по разработке мобильных приложений с использованием React
От React к React Native: Руководство для начинающих по разработке мобильных приложений с использованием React
Если вы уже умеете работать с React, создание мобильных приложений для iOS и Android - это новое приключение, в котором вы сможете применить свои...
БЭМ: Конвенция об именовании CSS
БЭМ: Конвенция об именовании CSS
Я часто вижу беспорядочный код CSS, особенно если проект большой. Кроме того, я совершал эту ошибку в профессиональных или личных проектах и...
Революционная веб-разработка ServiceNow
Революционная веб-разработка ServiceNow
В быстро развивающемся мире веб-разработки ServiceNow для достижения успеха крайне важно оставаться на вершине последних тенденций и технологий. По...
Как добавить SEO(Search Engine Optimization) в наше веб-приложение и как это работает?
Как добавить SEO(Search Engine Optimization) в наше веб-приложение и как это работает?
Заголовок веб-страницы играет наиболее важную роль в SEO, он помогает поисковой системе понять, о чем ваш сайт.
Конфигурация Jest в angular
Конфигурация Jest в angular
В этой статье я рассказываю обо всех необходимых шагах, которые нужно выполнить при настройке jest в angular.
0
10
66
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

С небольшой очисткой и переименованием, а также созданием Customer ObservableObject, я думаю, это делает то, что вы хотите…

struct ContentView: View {
    @State var phoneTypes: [String] = ["Other", "Home", "Service", "Work", "Cell"]
    @StateObject var customer = Customer()
        
    
    var body: some View {
        VStack{
            if !customer.isEmpty {
                Button(action: {
                    customer.phones = []
                }, label: {
                    Text("Clear").padding()
                })
            }
            ForEach($customer.phones.indices, id: \.self) { i in
                Menu {
                    ForEach(phoneTypes, id: \.self) { phoneType in
                        Button() {
                            customer.phones[i].phoneType = phoneType
                            
                            print(customer.phones)
                        } label:{
                            Text(phoneType)
                            if customer.phones[i].phoneType == phoneType {
                                Image(systemName: "checkmark")
                            }
                        }
                    }
                } label: {
                    VStack{
                        HStack{
                            Spacer()
                            Text(customer.phones[i].phoneType.isEmpty ? "Select the phone type *" : customer.phones[i].phoneType)
                                .foregroundColor(customer.phones[i].phoneType.isEmpty ? .gray : .black)
                            Image(systemName: "chevron.down")
                                .foregroundColor(Color.green)
                            Spacer()
                        }
                    }
                }
            }
            
            Button(action: {
                customer.addPhone()
                
                print(customer.phones)
            }, label: {
                HStack {
                    Image(systemName: "plus.circle")
                    Text("Add Phone")
                        .fontWeight(.thin)
                }
            })
            .font(.system(size: 15))
        }
    }
}

final class Customer: ObservableObject {
    
    @Published var phones: [CustomerPhone]
    
    var isEmpty: Bool {
        phones == [CustomerPhone()]
    }
    
    init() {
        phones = [CustomerPhone()]
    }
    
    public func addPhone(){
        phones.append(CustomerPhone())
    }    
}

Ответ принят как подходящий

Вот как я бы реализовал Customer и CustomerPhone и как проверить, пуст ли объект Customer

struct Customer: Codable, Equatable {
    var phones: [CustomerPhone]

    init() {
        phones = [CustomerPhone()]
    }

    public mutating func add(phone: CustomerPhone = CustomerPhone()){
        phones.append(phone)
    }

    var isEmpty: Bool {
        phones.isEmpty || phones.allSatisfy(\.isEmpty)
    }
}

struct CustomerPhone: Codable, Equatable {
    var phone: String
    var phoneType: String
    init(){
        phone = ""
        phoneType = ""
    }

    var isEmpty: Bool {
        phone.isEmpty && phoneType.isEmpty
    }
}

Спасибо!!!! Не знал, что можно сделать Equatable без static func == (lhs: CreateCustomer, rhs: CreateCustomer), супер круто!!!!!

micah 10.01.2023 18:52

@micah в некоторых случаях компилятор может автоматически синтезировать оператор равенства для структур и перечислений.

Alexander 11.01.2023 16:31

Другие вопросы по теме