मेरे ऐप में, उपयोगकर्ता बारकोड को स्कैन करते हैं और उत्पाद के बारे में जानकारी एक एपीआई से प्राप्त की जाती है।

मैं एक इतिहास अनुभाग बनाना चाहता हूं, जहां उपयोगकर्ता पिछले 10 उत्पादों को देख सकें।

एपीआई डेटा से परिणाम एक परिणाम प्रकार में संग्रहीत किया जाता है, जिसे सूची में दिखाए जाने में सक्षम होने के लिए, पहचान योग्य होना चाहिए।

परिणाम एक कस्टम डेटा प्रकार है जिसका उपयोग मैं एपीआई कॉल से उत्पादों के विवरण को स्टोर करने के लिए कर रहा हूं।

परिणाम

struct Result: Codable, Identifiable {
    var id = UUID()
    var description: String?
    var brand: String?
    var ingredients: String?
    var image: String?
    var upc_code: String?
    var return_message: String?
    var return_code: String?
    
    enum CodingKeys: String, CodingKey {
        case description, brand, ingredients, image, upc_code, return_message, return_code
    }
}

यह डेटा प्रकार परिणाम की सरणी को संग्रहीत करता है जिसे मैं एक सूची के रूप में प्रदर्शित करूंगा

इतिहास

struct History: Codable {
    var results: [Result]
}

यहाँ एपीआई कॉल है:

func loadData(url: String, completion: @escaping (Error?, Result?) -> Void ) {
    if let url = URL(string: url) {
        let task = URLSession.shared.dataTask(with: url) { data, response, error in
            guard let data = data, error == nil else {return}
            
            do {
                let defaults = UserDefaults.standard
                let encoder = JSONEncoder()
                if let encoded = try? encoder.encode(data) {
                    var sizeCheck = defaults.object(forKey:"productHistory") as? [Data] ?? [Data]()
                    if (sizeCheck.count == 10) { //Check if there's more than 10 products already on the history list
                        sizeCheck.removeLast()
                    }
                    sizeCheck.append(encoded) //Add new product to list
                    defaults.set(sizeCheck, forKey: "productHistory") //Add new list to userDefaults
                }
                let decoder = JSONDecoder()
                let result: Result = try decoder.decode(Result.self, from: data)
                completion(nil, result) //Used elsewhere to display the scanned product after it's been added to the history list
            }
            catch let e {
                print(e)
                completion(e, nil)
            }
        }

        task.resume()
    }
}

यह मेरा विचार है जो एक बटन दबाए जाने पर सूची में अंतिम 10 उत्पादों को दिखाता है।

अंतिम 10 उत्पादों को productHistory कुंजी के साथ UserDefaults में संग्रहित किया जाना चाहिए। यह एपीआई कॉल लोडडाटा () में किया जाता है

struct historyView: View {
    @Binding var showingHistory: Bool
    @State private var results = [Result]()
    
    var body: some View {
        let defaults = UserDefaults.standard
        if let products = defaults.object(forKey: "productHistory") as? Data {
            if let decodedResponse = try? JSONDecoder().decode(History.self, from: products) {
                self.results = decodedResponse.results
            }
        }
        return List(self.results, id: \.id) { item in
            Text(item.description!)
        }
    }
}

मेरी समझ में, समस्या यह है कि UserDefaults JSON डेटा संग्रहीत नहीं कर सकता है। तो जब एपीआई डेटा लाया जाता है, तो मैं डेटा को उपयोगकर्ता डिफॉल्ट्स में संग्रहीत करता हूं। फिर जब मुझे इसकी आवश्यकता हो, इसे डीकोड करें, जैसे इसे इतिहास में संग्रहीत करना या इसे प्रदर्शित करना।

वर्तमान में मुझे एक खाली सूची मिल रही है और अगर नीचे दिया गया कथन पास नहीं हो रहा है।

if let decodedResponse = try? JSONDecoder().decode(History.self, from: products) {

अगर मैं ब्राउज़र में यूआरएल पेस्ट करता हूं तो एपीआई से JSON डेटा यहां दिया गया है:

संपादित करें

यहां मेरा एपीआईकॉल है ():

func callAPI() -> String {
        if (scannedCode.barcode == "") {
            return "noneScanned"
        }
        else {
            let hashedValue = scannedCode.barcode.hashedValue("API ID")
            //print(hashedValue!)
            loadData(url: "URL") { error, result  in
                if let err = error {
                    self.APIresult = err.localizedDescription
                    print(APIresult)
                    //output error
                }
                else if (result?.ingredients == nil) {
                    DispatchQueue.main.async {
                        self.APIresult = "noIngredients"
                    }
                }
                else if (result?.description == nil) {
                    DispatchQueue.main.async {
                        self.APIresult = "noDescription"
                    }
                }
                else {
                    DispatchQueue.main.async {
                        self.APIresult = "success"
                    }                    
                }
                DispatchQueue.main.async {
                    product.result = result!
//updates view that show's the scanned product, as it's @Published
                }
            }
            return APIresult
        }
    }

इस खंड में, मैं यह जानना चाहता हूं कि मेरे पास उत्पाद के बारे में क्या डेटा है और उसके अनुसार इसे संसाधित करें। इसलिए उपरोक्त समाधान के साथ, मैं एक छवि या विवरण आदि के आधार पर एक अलग मूल्य लौटाता हूं ...

वाडियन समाधान के साथ, मैंने इसे इसमें बदल दिया है:

          loadData(url: "URL") { result  in
                switch result {
                case .success(product):
                    print("success")
                case .failure(error):
                    print("failure")
                }
            }
1
PlasticTrees 2 अगस्त 2020, 13:38

1 उत्तर

सबसे बढ़िया उत्तर

जैसा कि टिप्पणियों में बताया गया है कि आप Data और Result को मिला रहे हैं

सबसे पहले History ड्रॉप करें और Result का नाम बदलकर Product कर दें। हम Product की एक सरणी को UserDefaults में सहेजने जा रहे हैं

struct Product: Codable, Identifiable {
    var id = UUID()
    var description: String?
    var image: String?
    var upc_code: String?
    var return_message: String?
    var return_code: String?
    
    private enum CodingKeys: String, CodingKey {
        case description, image, upc_code, return_message, return_code
    }
}

loadData में जेनेरिक Result टाइप को क्लोजर पैरामीटर के रूप में इस्तेमाल करें। डेटा प्राप्त करने के बाद इसे Product उदाहरण में डीकोड करें, फिर सहेजे गए सरणी को लोड करें, पहले (!) आइटम को हटा दें (यदि आवश्यक हो) नया आइटम संलग्न करें, सरणी को वापस सहेजें और नए के साथ कॉल पूरा करें Product. failure मामले में सभी संभावित त्रुटियां पास कर दी जाती हैं।

func loadData(url: String, completion: @escaping (Result<Product,Error>) -> Void ) {
    guard let url = URL(string: url) else { return }
    let task = URLSession.shared.dataTask(with: url) { data, response, error in
        if let error = error { completion(.failure(error));  return }
        
        do {
            let decoder = JSONDecoder()
            let product = try decoder.decode(Product.self, from: data!)
            let defaults = UserDefaults.standard
            var history = [Product]()
            if let readData = defaults.data(forKey:"productHistory") {
                do {
                    history = try decoder.decode([Product].self, from: readData)
                    if history.count == 10 { history.removeFirst() }
                } catch { print(error) }
            }
            history.append(product)
            let saveData = try JSONEncoder().encode(history)
            defaults.set(saveData, forKey: "productHistory")
            completion(.success(product))
        }
        catch {
            print(error)
            completion(.failure(error))
        }
    }
    task.resume()
}

और इसे बुलाओ

loadData(url: "URL") { result  in
    switch result {
    case .success(let product):
       if product.ingredients == nil {
           self.APIresult = "noIngredients"
       } else if product.description == nil {
           self.APIresult = "noDescription"
       } else {
           self.APIresult = "success"                  
       }
       product.result = product           

    case .failure(let error):
       self.APIresult = error.localizedDescription
       print(APIresult)
    }
}

HistoryView में (कृपया अपरकेस अक्षर के साथ नाम संरचनाएँ) UserDefaults से डेटा प्राप्त करें और Product सरणी को डीकोड करें।

struct HistoryView: View {
    @Binding var showingHistory: Bool
    @State private var results = [Product]()
    
    var body: some View {
        let defaults = UserDefaults.standard
        if let historyData = defaults.data(forKey: "productHistory") {
            do {
                self.results = try JSONDecoder().decode([Product].self, from: historyData)
            } catch { print(error) }
        }
        return List(self.results, id: \.id) { item in
            Text(item.description ?? "n/a")
        }
    }
}

नोट: ध्यान रखें कि UUID को एन्कोड और सेव नहीं किया जा रहा है।

और कृपया अधिक वर्णनात्मक चर नामों का उपयोग करें।

2
vadian 2 अगस्त 2020, 16:51