Как правильно настроить модель представления с использованием свойства @Published массива моего пользовательского типа и использовать его в моем пользовательском представлении?

В следующих файлах я пытаюсь использовать данные вызова API в шаблоне типа MVVM для отображения данных в моем представлении. Я получаю сообщение об ошибке при попытке создать представление списка. Я пробовал использовать знак доллара «$» в ссылке на viewModel и в ссылке на массив, но ни один из них не работает. Я просмотрел бесчисленное количество видео и прочитал немало руководств по этому вопросу, но ничто не помогло решить мою ошибку. Любая помощь или совет будут очень признательны.

Сообщения об ошибках, которые я получаю: В ReturnsListView, где я пытаюсь использовать список, я получаю 2 сообщения:

  1. «Невозможно преобразовать значение типа «[ReturnItem]» в ожидаемый тип аргумента «Binding»» и
  2. «Не удалось вывести общий параметр «Данные»»

Также в ReturnsListView, но в строке, где я пытаюсь получить доступ к «returnItem.productImageUrl», я получаю третье сообщение: 3. «Невозможно преобразовать значение типа «Привязка» в ожидаемый тип аргумента «Строка»»

import Foundation

struct ReturnItem: Codable, Identifiable {

    let id = UUID().uuidString
    var puNbr: Int
    var puLineNbr: Int
    var packCd: String
    var puQty: Int
    var partialReturn: Bool
    var itemDescription: String
    var itemSize: String
    var itemPack: Int
    var reasonType: String
    var reasonSubtype: String
    var invoiceNbr: Int
    var productImageUrl: String
    var formattedRetailUPC: String
    var puStatus: String
    var editable: Bool
    
    enum CodingKeys: CodingKey {
        case puNbr, puLineNbr, packCd, puQty, partialReturn,
             itemDescription, itemSize, itemPack, reasonType, reasonSubtype,
             invoiceNbr, productImageUrl, formattedRetailUPC, puStatus, editable
    }
}

Затем я настроил эту ViewModel:

import Foundation

@MainActor
class ReturnsViewModel: ObservableObject {

    @Published var returnItemsArray: [ReturnItem] = []
    @Published var displayAPIError: Bool = false
    @Published var errorText: String = ""
    
    func getDataWithAlamoFire() async {
        Service.sharedInstance.returnsApi { returnItems in
            self.returnItemsArray = returnItems
            
        } failure: { alertType, errorText in
            print("🪵 ReturnsListAPI AlertType is: \(alertType)")
            print("‼️ ReturnsListAPI ErrorText is: \(errorText)")
            self.displayAPIError = true
            self.errorText = errorText
        }
    }
}

И, наконец, вот мой ReturnsListView:

import SwiftUI

struct ReturnsListView: View {
    
    @StateObject var returnsVM = ReturnsViewModel()
    @State var partialReturnToggle = false
    @State var showAlert = false
    
    var imageDimensions = 96.0
    var iPadImageDimentions = 80.0
    var iPadTextSize = 15.0
    
    var body: some View {
        NavigationView {
            VStack(alignment: .center) {
// Getting the first two errors on the line below
// 1. "Cannot convert value of type '[ReturnItem]' to expected argument type 
// 'Binding<Data>'"
// and
// 2. "Generic parameter 'Data' could not be inferred"
                List(returnsVM.returnItemsArray) { returnItem in
                    HStack { // main return list row
                        // Left side of row with Image and Partial Pack Toggle
                        VStack(alignment: .center) {
                            if displayImages {
// Getting the following message on the line below:
// "Cannot convert value of type 'Binding<Subject>' to expected argument type 'String'"
                                Image(uiImage: getReturnsImage(
                                    productImageUrlString: returnItem.productImageUrl,
                                    width: imageDimensions,
                                    height: imageDimensions)
                                )
                                .resizable()
                                .aspectRatio(contentMode: .fit)
                                .frame(width: imageDimensions, height: imageDimensions)
                            }
                            VStack{
                                Text("Partial Pack")
                                Toggle("Partial Pack Toggle", isOn: $partialReturnToggle)
                                    .labelsHidden()
                                    .toggleStyle(SwitchToggleStyle(tint: .orange))
                            }
                        }
                        .padding(.trailing)
                        
                        // Right side of row with Item details and quantity buttons
                        VStack(alignment: .center) {
                            Text(returnItem.itemDescription ?? "") // item description
                                .fontWeight(.black)
                                .frame(alignment: .center)
                            HStack(alignment: .center) {
                                Spacer()
                                Text(returnItem.puStatus ?? "") // pickup status
                                    .font(.system(size: 17))
                                    .fontWeight(.bold)
                                    .foregroundColor(.red)
                                Spacer()
                            }
                            HStack {
                                Text(returnItem.itemSize ?? "") // itemSize
                                    .font(.system(size: 20))
                                Spacer()
                                if let itemPack = returnItem.itemPack {
                                    Text("(\(itemPack) pack)") // itemPack
                                        .font(.system(size: 20))
                                }
                            }
                            HStack(alignment: .leading) {
                                Text(returnItem.formattedRetailUPC ?? "") // formattedRetailUPC
                                    .font(.system(size: 20))
                                Text(returnItem.invoiceNbr) // invoice number
                                    .font(.system(size: 20))
                            }
                        }
                    }
                    .listRowBackground(Color(UIColor.tertiarySystemBackground))
                }
                .listStyle(.plain)
                .ignoresSafeArea()
                .offset(y: -16)
            }
            .background((Color(UIColor.tertiarySystemBackground)))
        }
        .navigationViewStyle(.stack)
        .task {
            await returnsVM.getDataWithAlamoFire()
        } // NaviationView
    } // End of Body
}

func getReturnsImage(productImageUrlString: String, width: Double, height: Double) -> UIImage {
    if productImageUrlString.endsWith("nulljpg") {
        return UIImage(named: "placeHolderImage")!
    } else {
        let formattedImageUrlString = productImageUrlString.replacingOccurrences(of: " ", with: "%20")
        if let productImageUrl = URL.init(string: formattedImageUrlString) {
            print("🪵 Formatted ProductImageURL is: \(productImageUrl)")
            let frame = CGRect(x: 0, y: 0, width: width, height: height)
            let prodUIImageView = UIImageView(frame: frame)
            prodUIImageView.af.setImage(withURL: productImageUrl)
            return prodUIImageView.image ?? UIImage(named: "placeHolderImage")!
        } else {
            return UIImage(named: "placeHolderImage")!
        }
    }
}

#Preview {
    ReturnsListView(navHeight: 42, infoString: "Test Customer - 888777", backPressed: {})
}

Где-то опечатка, упростите Views и вы ее найдете. Комментируйте разделы

lorem ipsum 07.03.2024 00:43

Что-то еще, что теперь эти ключевые слова async ничего не делают, если в базовом коде есть обработчики завершения.

lorem ipsum 07.03.2024 02:26

@lorem ipsum, да, это имеет смысл, если у меня есть обработчик завершения, тогда нет необходимости использовать асинхронность и ожидание. Попробую использовать и то, и другое. Кроме того, я делаю копии этих трех файлов, а затем избавляюсь от всего, кроме простого вызова API и простого текстового поля. Тогда я попробую добавить список обратно. Я дам вам знать, как идут дела.

jonathan3087 07.03.2024 02:39

Вероятно, это опечатка ) на height: imageDimensions. Обратите внимание: использование ObservableObject совершенно допустимо и соответствует рекомендациям Apple. См. официальную ссылку, чтобы узнать, как управлять данными в вашем приложении: Мониторинг данных

workingdog support Ukraine 07.03.2024 03:20

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

jonathan3087 07.03.2024 20:42
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
5
139
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

когда вы используете async, вы можете просто удалить ObservableObject и использовать .task, например.

@State var results = ...

...

.task { // task inits in on appear and deinits in disappear (same as StateObject)
    results = await ...
}

Итак, вы предлагаете просмотреть файл модели и удалить «: ObservableObject» из строки класса? Это означает, что мне также нужно удалить обертки свойств (at)Published (Xcode загорелся, как фейерверк). Итак, как только ObseravableObject и «(at)Published» будут удалены, как мне получить автоматические обновления моего массива для отображения в Просмотр контента. Я думал, что ObservableObject, «(at)Published», а затем @StateObject работают вместе, чтобы автоматически обновлять.

jonathan3087 07.03.2024 02:44
@StateObject и @Published предназначены только для случаев, когда вам нужна ссылочная семантика для состояния. Поскольку новый .task предоставляет ссылочную семантику, вы можете просто использовать @State.
malhal 07.03.2024 08:37
Ответ принят как подходящий

Эти ошибки чаще всего вызваны опечаткой.

Если вы закомментируете разделы своего кода, вы обнаружите конкретную проблему.

Короткие очень конкретные представления и уменьшение необходимости проверки типов помогают получить более конкретные ошибки.

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