मैं अपने उपयोगकर्ता डिफ़ॉल्ट मानों को सहेजने के लिए एक प्रॉपर्टी रैपर का उपयोग कर रहा हूं। IOS 13 उपकरणों पर, यह समाधान बहुत अच्छा काम करता है। हालाँकि iOS 11 और iOS 12 पर, मान उपयोगकर्ता डिफ़ॉल्ट में सहेजे नहीं जा रहे हैं। मैंने पढ़ा है कि संपत्ति रैपर पीछे की ओर संगत हैं इसलिए मुझे नहीं पता कि यह पुराने आईओएस संस्करणों पर क्यों काम नहीं करेगा।

यह संपत्ति आवरण है:

@propertyWrapper
struct UserDefaultWrapper<T: Codable> {
    private let key: String
    private let defaultValue: T

    init(key: String, defaultValue: T) {
        self.key = key
        self.defaultValue = defaultValue
    }

    var wrappedValue: T {
        get {
            guard let data = UserDefaults.standard.object(forKey: key) as? Data else {
                // Return defaultValue when no data in UserDefaults
                return defaultValue
            }

            // Convert data to the desire data type
            let value = try? JSONDecoder().decode(T.self, from: data)
            return value ?? defaultValue
        }
        set {
            // Convert newValue to data
            let data = try? JSONEncoder().encode(newValue)

            UserDefaults.standard.set(data, forKey: key)
            UserDefaults.standard.synchronize()
        }
    }
}

struct UserDefault {
    @UserDefaultWrapper(key: "userIsSignedIn", defaultValue: false)
    static var isSignedIn: Bool
}

मैं फिर इस तरह का मान सेट कर सकता हूं:

UserDefault.isSignedIn = true

क्या मैं प्रॉपर्टी रैपर का गलत इस्तेमाल कर रहा हूं? क्या पुराने आईओएस संस्करणों पर संपत्ति रैपर के साथ कोई और समस्या चल रही है?

6
dayloosha 24 पद 2019, 23:10

1 उत्तर

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

संपत्ति रैपर के साथ कुछ लेना देना नहीं है! समस्या यह है कि आईओएस 12 और इससे पहले, एक साधारण मूल्य जैसे बूल (या स्ट्रिंग, आदि), हालांकि कोडेबल एक कोडेबल संरचना की संपत्ति के रूप में कोडेबल (उदाहरण के लिए), स्वयं नहीं हो सकता है JSON एन्कोडेड हो। त्रुटि (जिसे आप फेंक रहे हैं) इस बारे में बिल्कुल स्पष्ट है:

शीर्ष-स्तरीय बूल को संख्या JSON खंड के रूप में एन्कोड किया गया।

इसे देखने के लिए, बस इस कोड को चलाएँ:

    do {
        _ = try JSONEncoder().encode(false)
        print("succeeded")
    } catch {
        print(error)
    }

IOS 12 पर, हमें त्रुटि मिलती है। IOS 13 पर, हमें "succeeded" मिलता है।

लेकिन अगर हम अपने बूल (या स्ट्रिंग, आदि) को एक कोडेबल स्ट्रक्चर में लपेटते हैं, तो सब ठीक है:

    struct S : Codable { let prop : Bool }
    do {
        _ = try JSONEncoder().encode(S(prop:false))
        print("succeeded")
    } catch {
        print(error)
    }

यह दोनों iOS 12 और iOS 13 पर ठीक काम करता है।

और वह तथ्य एक समाधान सुझाता है! अपनी संपत्ति के आवरण को फिर से परिभाषित करें ताकि वह एक सामान्य आवरण संरचना में अपना मूल्य लपेटे:

struct UserDefaultWrapper<T: Codable> {

    struct Wrapper<T> : Codable where T : Codable {
        let wrapped : T
    }

    private let key: String
    private let defaultValue: T

    init(key: String, defaultValue: T) {
        self.key = key
        self.defaultValue = defaultValue
    }

    var wrappedValue: T {
        get {
            guard let data = UserDefaults.standard.object(forKey: key) as? Data 
                else { return defaultValue }
            let value = try? JSONDecoder().decode(Wrapper<T>.self, from: data)
            return value?.wrapped ?? defaultValue
        }
        set {
            do {
                let data = try JSONEncoder().encode(Wrapper(wrapped:newValue))
                UserDefaults.standard.set(data, forKey: key)
            } catch {
                print(error)
            }
        }
    }
}

अब यह iOS 12 और iOS 13 पर काम करता है।


वैसे, मुझे वास्तव में लगता है कि आप JSON की बजाय संपत्ति सूची के रूप में सहेजने के लिए बेहतर करेंगे। लेकिन इससे आम तौर पर सवाल पर कोई फर्क नहीं पड़ता। आप एक नंगे बूल को संपत्ति सूची के रूप में भी एन्कोड नहीं कर सकते हैं। आपको अभी भी रैपर दृष्टिकोण की आवश्यकता होगी।

9
matt 26 पद 2019, 07:50