8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

需要帮助更新我在 SwiftData 中的 modelContext

volkerschulz 2月前

21 0

我正在尝试创建一个 iOS 应用,当我扫描产品时,会出现有关该产品的信息。当我扫描产品时,我希望使用 SwiftData 存储它并显示在我的“历史记录”页面中。对于某些

我正在尝试创建一个 iOS 应用,当我扫描产品时,有关该产品的信息就会出现。当我扫描产品时,我希望使用 SwiftData 存储它并显示在我的“历史记录”页面中。出于某种原因,无论我尝试什么,都不会在历史记录视图中显示我扫描过的产品。

这是我目前拥有的代码:

这是我用来获取产品信息的函数。它位于我的 AppViewModel.swift 中。

import AVKit
import Foundation
import SwiftUI
import VisionKit

enum ScanType: String {
    case barcode, text
}

enum DataScannerAccessStatusType {
    case notDetermined
    case cameraAccessNotGranted
    case cameraNotAvailable
    case scannerAvailable
    case scannerNotAvailable
}

@MainActor
final class AppViewModel: ObservableObject {
    @Environment(\.modelContext) private var modelContext
    
    @Published var dataScannerAccessStatus: DataScannerAccessStatusType = .notDetermined
    @Published var recognizedItems: [RecognizedItem] = []
    @Published var scanType: ScanType = .barcode
    @Published var textContentType: DataScannerViewController.TextContentType?
    @Published var recognizesMultipleItems = true
    @Published var showNutritionInfo = false
    @Published var productInfo: NutritionInfoView?
    
    public func fetchProductInfo(barcode: String) {
        let baseURL = "https://world.openfoodfacts.org/api/v0/product/"
        guard let url = URL(string: "\(baseURL)\(barcode).json") else {
            print("Invalid URL")
            return
        }
        
        let task = URLSession.shared.dataTask(with: url) { data, response, error in
            if let error = error {
                print("Failed to retrieve data: \(error.localizedDescription)")
                return
            }
            
            guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200, let data = data else {
                print("Failed to retrieve data. Status code: \((response as? HTTPURLResponse)?.statusCode ?? 0)")
                return
            }
            
            do {
                if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
                   let status = json["status"] as? Int, status == 1,
                   let product = json["product"] as? [String: Any] {
                    let newProduct = Product(
                        withAdditives: product["with_additives"] as? String ?? "N/A",
                        name: product["product_name"] as? String ?? "N/A",
                        brand: product["brands"] as? String ?? "N/A",
                        quantity: product["quantity"] as? String ?? "N/A",
                        ingredients: product["ingredients_text"] as? String ?? "N/A",
                        nutritionScore: product["nutriscore_score"] as? Int ?? -1,
                        imageURL: product["image_url"] as? String ?? "N/A"
                    )
                    
                    DispatchQueue.main.async {
                        self.productInfo = NutritionInfoView(product: newProduct)
                        self.showNutritionInfo = true
                        self.modelContext.insert(newProduct)
                        
                        do {
                            try self.modelContext.save()
                        } catch {
                            print("Failed to save context: \(error.localizedDescription)")
                        }
                    }
                } else {
                    print("Product not found.")
                }
            } catch {
                print("Failed to parse JSON: \(error.localizedDescription)")
            }
        }
        
        task.resume()
    }
    // Other code
}

这是我的 CameraView.swift:

import SwiftUI
import VisionKit

struct CameraView: View {
    @EnvironmentObject var vm: AppViewModel
    
    var body: some View {
        switch vm.dataScannerAccessStatus {
        case .scannerAvailable:
            mainView
        case .cameraNotAvailable:
            Text("Your device doesn't have a camera")
        case .scannerNotAvailable:
            Text("Your device doesn't have support for scanning barcode with this app")
        case .cameraAccessNotGranted:
            Text("Please provide access to the camera in settings")
        case .notDetermined:
            Text("Requesting camera access")
        }
    }
    
    private var mainView: some View {
        ZStack {
            DataScannerView(
                recognizedItems: $vm.recognizedItems,
                recognizedDataType: vm.recognizedDataType,
                recognizesMultipleItems: vm.recognizesMultipleItems,
                onScan: { result in
                    // Ensure scanType is barcode
                    if vm.scanType == .barcode {
                        // Iterate over recognized items
                        for item in result {
                            // Check if the item is a barcode
                            if case let .barcode(barcode) = item {
                                // Fetch product info with the barcode payload
                                vm.fetchProductInfo(barcode: barcode.payloadStringValue ?? "")
                                // Exit the loop after processing the first barcode (if needed)
                                break
                            }
                        }
                    }
                }
            )
            .background { Color.gray.opacity(0.3) }
            .ignoresSafeArea()
            .id(vm.dataScannerViewId)
            .sheet(isPresented: $vm.showNutritionInfo) {
                // TODO: Make scrollable if not already, maybe through a ScrollablePane (idk what it's rally caleed look at barcode app I think it's there)
                if let productInfo = vm.productInfo {
                    productInfo
                        .presentationDragIndicator(.visible)
                }
            }
            
            VStack {
                Spacer()
                
                bottomContainerView
                    .background(.clear)
                    .frame(maxWidth: .infinity)
                    .frame(height: UIScreen.main.bounds.height * 0.35) // Height of scanning information
                    .clipped()
            }
            .edgesIgnoringSafeArea(.bottom)
            .onChange(of: vm.scanType) { _ in vm.recognizedItems = [] }
            .onChange(of: vm.textContentType) { _ in vm.recognizedItems = [] }
            .onChange(of: vm.recognizesMultipleItems) { _ in vm.recognizedItems = [] }
        }
    }
    
    private var bottomContainerView: some View {
        VStack {
            Picker("Scan Type", selection: $vm.scanType) {
                Text("Barcode").tag(ScanType.barcode)
                Text("Text").tag(ScanType.text)
            }
            .pickerStyle(.segmented)
            .padding(.leading, 30)
            .padding(.trailing, 30)
            .padding(.horizontal)
        }
    }
}

这是我的 HistoryView.swift

import SwiftUI
import SwiftData

struct HistoryView: View {
    @Environment(\.modelContext) private var modelContext
    @Query() private var products: [Product]
    
    var body: some View {
        NavigationSplitView {
            Group {
                if !products.isEmpty {
                    List {
                        ForEach(products) { product in
                            NavigationLink {
                                NutritionInfoView(product: product)
                            } label: {
                                Text(product.name)
                            }
                        }
                        .onDelete(perform: deleteItems)
                    }
                } else {
                    ContentUnavailableView {
                        Label("You haven't scanned anything yet", systemImage: "barcode.viewfinder")
                    }
                }
            }
            .navigationTitle("History")
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    EditButton()
                }
            }
        } detail: {
            Text("Select a product")
                .navigationTitle("Products")
        }
    }
    
    private func deleteItems(offsets: IndexSet) {
        withAnimation {
            for index in offsets {
                modelContext.delete(products[index])
            }
        }
    }
}

#Preview {
    HistoryView()
}

非常感谢所有帮助!

帖子版权声明 1、本帖标题:需要帮助更新我在 SwiftData 中的 modelContext
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由volkerschulz在本站《swift》版块原创发布, 转载请注明出处!
最新回复 (0)
  • Fynn 2月前 0 只看Ta
    引用 2

    找到答案了!只需要将模型上下文传递给 fetchProductInfo() 函数,因为 AppViewModel 是一个 EnvironmentObject。

返回
作者最近主题: