NSDoding के साथ UserDefaults के साथ कस्टम स्विफ्ट क्लास की बचत करना


79

मैं वर्तमान में NSUserDefaults के लिए एक कस्टम स्विफ्ट वर्ग को बचाने की कोशिश कर रहा हूं। यहाँ मेरे खेल के मैदान से कोड है:

import Foundation

class Blog : NSObject, NSCoding {

    var blogName: String?

    override init() {}

    required init(coder aDecoder: NSCoder) {
        if let blogName = aDecoder.decodeObjectForKey("blogName") as? String {
            self.blogName = blogName
        }
    }

    func encodeWithCoder(aCoder: NSCoder) {
        if let blogName = self.blogName {
            aCoder.encodeObject(blogName, forKey: "blogName")
        }
    }

}

var blog = Blog()
blog.blogName = "My Blog"

let ud = NSUserDefaults.standardUserDefaults()    
ud.setObject(blog, forKey: "blog")

जब मैं कोड चलाता हूं, तो मुझे निम्न त्रुटि मिलती है

निष्पादन बाधित हुआ, कारण: संकेत SIGABRT।

अंतिम पंक्ति में ( ud.setObject...)

मैसेज वाले ऐप में भी यही कोड क्रैश हो जाता है

"प्रारूप के लिए संपत्ति की सूची अमान्य: 200 (संपत्ति सूची में 'CFType' प्रकार की वस्तुएं नहीं हो सकती हैं)"

क्या कोई मदद कर सकता है? मैं Maverick पर Xcode 6.0.1 का उपयोग कर रहा हूं। धन्यवाद।


यहाँ एक अच्छा ट्यूटोरियल है: ios8programminginswift.wordpress.com/2014/08/17/… यहाँ एक अच्छी रेडिट चर्चा है: reddit.com/r/swift/comments/28sp3z/… यहाँ एक और अच्छी पोस्ट है: myswiftjourney.me/2014/ 10/01 / ... पूर्ण वस्तु भंडारण मैं पूरी NSCoding की सिफारिश के लिए - नीचे दो उदाहरण, पूर्ण वर्ग संरचना के साथ मुझ से दूसरा एक: stackoverflow.com/questions/26233067/... stackoverfl
स्टीव रोसेनबर्ग

जवाबों:


62

स्विफ्ट 4 या उच्चतर में, कोडेबल का उपयोग करें।

अपने मामले में, निम्नलिखित कोड का उपयोग करें।

class Blog: Codable {
   var blogName: String?
}

अब इसकी वस्तु बनाएं। उदाहरण के लिए:

var blog = Blog()
blog.blogName = "My Blog"

अब इसे इस तरह एनकोड करें:

if let encoded = try? JSONEncoder().encode(blog) {
    UserDefaults.standard.set(encoded, forKey: "blog")
}

और इसे इस तरह डिकोड करें:

if let blogData = UserDefaults.standard.data(forKey: "blog"),
    let blog = try? JSONDecoder().decode(Blog.self, from: blogData) {
}

अगर मैं इसे स्थायी रूप से संग्रहीत करना चाहता हूं तो इसे कैसे काम करना चाहिए। अग्रिम धन्यवाद
बिरजू

1
मैंने यह कोशिश की है, लेकिन मुझे त्रुटि संदेश मिल रहा है, टाइप ब्लॉग प्रोटोकॉल के अनुरूप नहीं है Decodable
vofili

@vofili क्या आपने कोडेबल से क्लास ब्लॉग विरासत में लिया है?
गुलाम रसूल

अगर मेरे ब्लॉग वर्ग में कोई शब्दकोश [स्ट्रिंग: एनी] है तो मैं इसे कैसे करूँ?
अली रज़ा

46

पहली समस्या यह है कि आपको यह सुनिश्चित करना होगा कि आपके पास एक गैर-आम वर्ग का नाम है:

@objc(Blog)
class Blog : NSObject, NSCoding {

तब आपको उपयोगकर्ता डिफॉल्ट में स्टोर करने से पहले ऑब्जेक्ट (एक NSData में) को एनकोड करना होगा:

ud.setObject(NSKeyedArchiver.archivedDataWithRootObject(blog), forKey: "blog")

इसी तरह, उस वस्तु को पुनर्स्थापित करने के लिए, जिसे आपको इसे अनसर्क करने की आवश्यकता होगी:

if let data = ud.objectForKey("blog") as? NSData {
    let unarc = NSKeyedUnarchiver(forReadingWithData: data)
    unarc.setClass(Blog.self, forClassName: "Blog")
    let blog = unarc.decodeObjectForKey("root")
}

ध्यान दें कि यदि आप इसे खेल के मैदान में उपयोग नहीं कर रहे हैं तो यह थोड़ा सरल है क्योंकि आपको कक्षा को हाथ से पंजीकृत नहीं करना है:

if let data = ud.objectForKey("blog") as? NSData {
    let blog = NSKeyedUnarchiver.unarchiveObjectWithData(data)
}

1
धन्यवाद। यही किया। NSKeyedUnarchiver लापता टुकड़ा था। मैं अपने प्रश्न को काम के नमूने के साथ अपडेट करूंगा। ऐसा लगता है कि आप unarc.setClass को छोड़ सकते हैं और इसके बजाय unarc.decodeObjectForKey को - इस मामले में - ब्लॉग वर्ग में डाउनकास्ट कर सकते हैं।
जॉर्ज

1
setClassयदि आपको प्लेग्राउंड में काम कर रहे हैं तो आपको केवल जरूरत है , विभिन्न कारणों से कक्षाएं स्वतः पंजीकृत नहीं होती हैं।
डेविड बेरी

blogचर ऊपर कस्टम वस्तु में नहीं है if let data = ud.objectForKey("blog") as? NSData { let blog = NSKeyedUnarchiver.unarchiveObjectWithData(data) }, मेरे मामले में मैं इस तरह अपने कस्टम ऑब्जेक्ट के लिए यह कास्ट करने के लिए की जरूरत हैif let data = ud.objectForKey("blog") as? NSData { let blog = NSKeyedUnarchiver.unarchiveObjectWithData(data) if let thisblog = blog as? Blog { return thisblog //this is the custom object from nsuserdefaults } }
CaffeineShots

21

जैसा कि @ dan-beaulieu ने सुझाव दिया कि मैं अपने प्रश्न का उत्तर दूं:

यहाँ अब काम कोड है:

नोट: प्लेग्राउंड्स में काम करने के लिए कोड के लिए वर्ग नाम की डीमंग्लिंग आवश्यक नहीं थी।

import Foundation

class Blog : NSObject, NSCoding {

    var blogName: String?

    override init() {}

    required init(coder aDecoder: NSCoder) {
        if let blogName = aDecoder.decodeObjectForKey("blogName") as? String {
            self.blogName = blogName
        }
    }

    func encodeWithCoder(aCoder: NSCoder) {
        if let blogName = self.blogName {
            aCoder.encodeObject(blogName, forKey: "blogName")
        }
    }

}

let ud = NSUserDefaults.standardUserDefaults()

var blog = Blog()
blog.blogName = "My Blog"

ud.setObject(NSKeyedArchiver.archivedDataWithRootObject(blog), forKey: "blog")

if let data = ud.objectForKey("blog") as? NSData {
    let unarc = NSKeyedUnarchiver(forReadingWithData: data)
    let newBlog = unarc.decodeObjectForKey("root") as Blog
}

क्या होगा यदि मेरे कस्टम वर्ग में UIImage या Data टाइप है? मैं UserDefaults में उस तरह के वर्ग को कैसे संग्रहीत कर सकता हूं?
नीलम पुरसानी

12

यहां स्विफ्ट 4 और 5 के लिए एक पूर्ण समाधान है ।

सबसे पहले, UserDefaultsविस्तार में सहायक विधियों को लागू करें:

extension UserDefaults {

    func set<T: Encodable>(encodable: T, forKey key: String) {
        if let data = try? JSONEncoder().encode(encodable) {
            set(data, forKey: key)
        }
    }

    func value<T: Decodable>(_ type: T.Type, forKey key: String) -> T? {
        if let data = object(forKey: key) as? Data,
            let value = try? JSONDecoder().decode(type, from: data) {
            return value
        }
        return nil
    }
}

कहें, हम Dummy2 डिफ़ॉल्ट फ़ील्ड के साथ कस्टम ऑब्जेक्ट को सहेजना और लोड करना चाहते हैं। Dummyइसके अनुरूप होना चाहिए Codable:

struct Dummy: Codable {
    let value1 = "V1"
    let value2 = "V2"
}

// Save
UserDefaults.standard.set(encodable: Dummy(), forKey: "K1")

// Load
let dummy = UserDefaults.standard.value(Dummy.self, forKey: "K1")


यह स्थायी रूप से डेटा संग्रहीत नहीं करेगा। मैं आवेदन के मारे जाने के बाद डेटा स्टोर करना चाहता हूं।
बिरजू

9

स्विफ्ट 2.1 और Xcode 7.1.1 के साथ परीक्षण किया गया

यदि आपको वैकल्पिक होने के लिए ब्लॉगनाम की आवश्यकता नहीं है (जो मुझे लगता है कि आप नहीं करते हैं), मैं थोड़ा अलग कार्यान्वयन की सिफारिश करूंगा:

class Blog : NSObject, NSCoding {

    var blogName: String

    // designated initializer
    //
    // ensures you'll never create a Blog object without giving it a name
    // unless you would need that for some reason?
    //
    // also : I would not override the init method of NSObject

    init(blogName: String) {
        self.blogName = blogName

        super.init()        // call NSObject's init method
    }

    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(blogName, forKey: "blogName")
    }

    required convenience init?(coder aDecoder: NSCoder) {
        // decoding could fail, for example when no Blog was saved before calling decode
        guard let unarchivedBlogName = aDecoder.decodeObjectForKey("blogName") as? String
            else {
                // option 1 : return an default Blog
                self.init(blogName: "unnamed")
                return

                // option 2 : return nil, and handle the error at higher level
        }

        // convenience init must call the designated init
        self.init(blogName: unarchivedBlogName)
    }
}

परीक्षण कोड इस तरह दिख सकता है:

    let blog = Blog(blogName: "My Blog")

    // save
    let ud = NSUserDefaults.standardUserDefaults()
    ud.setObject(NSKeyedArchiver.archivedDataWithRootObject(blog), forKey: "blog")
    ud.synchronize()

    // restore
    guard let decodedNSData = ud.objectForKey("blog") as? NSData,
    let someBlog = NSKeyedUnarchiver.unarchiveObjectWithData(decodedNSData) as? Blog
        else {
            print("Failed")
            return
    }

    print("loaded blog with name : \(someBlog.blogName)")

अंत में, मैं यह बताना चाहूंगा कि NSKeyedArchiver का उपयोग करना और NSUserDefaults का उपयोग करने के बजाय सीधे किसी फ़ाइल में कस्टम ऑब्जेक्ट की अपनी सरणी को सहेजना आसान होगा। आप यहाँ मेरे उत्तर में उनके मतभेदों के बारे में अधिक जानकारी प्राप्त कर सकते हैं ।


बस FYI करें: BlogName के वैकल्पिक होने का कारण यह है कि ऑब्जेक्ट वास्तविक ब्लॉग को प्रतिबिंबित करता है जो एक उपयोगकर्ता के पास है। उपयोगकर्ता द्वारा नए ब्लॉग के लिए URL जोड़ने के बाद ब्लॉग का नाम स्वचालित रूप से शीर्षक टैग से प्राप्त होता है। इसलिए ऑब्जेक्ट क्रिएशन के समय में कोई ब्लॉग नाम नहीं है और मैं एक नए ब्लॉग को "अनाम" या कुछ इसी तरह से कॉल करने से बचना चाहता था।
जॉर्ज

7

स्विफ्ट 4 में आपके पास एक नया प्रोटोकॉल है जो NSCoding प्रोटोकॉल को बदलता है। यह कहा जाता है Codableऔर यह वर्गों और स्विफ्ट प्रकारों का समर्थन करता है! (एनम, स्ट्रक्चर्स):

struct CustomStruct: Codable {
    let name: String
    let isActive: Bool
}

कक्षा के औजार कोडेबल होते हैं और उनमें तारों का एक वैकल्पिक सरणी होता है:'Attempt to insert non-property list object
व्यासचावल गेर्विचोव

यह जवाब पूरी तरह से गलत है। प्रतिस्थापित नहींCodable करता है । क्लास को कंप्लेंट करने के लिए ऑब्जेक्ट्स को सीधे सेव करने का एकमात्र तरीका है । चूँकि यह एक है , इसलिए इसे संरचना / enum प्रकारों पर लागू नहीं किया जा सकता है। हालाँकि, आप अभी भी अपनी संरचना / एनुम का अनुपालन कर सकते हैं और एनकोड करने के लिए उपयोग कर सकते हैं, जिसे स्टोर किया जा सकता है । NSCodingUserDefaultsNSCodingNSCodingclass protocolCodingJSONSerializerDataUserDefaults
जोश

4

स्विफ्ट 3 संस्करण:

class CustomClass: NSObject, NSCoding {

    let name: String
    let isActive: Bool

    init(name: String, isActive: Bool) {
        self.name = name
        self.isActive = isActive
    }

    // MARK: NSCoding

    required convenience init?(coder decoder: NSCoder) {
        guard let name = decoder.decodeObject(forKey: "name") as? String,
            let isActive = decoder.decodeObject(forKey: "isActive") as? Bool
            else { return nil }

        self.init(name: name, isActive: isActive)
    }

    func encode(with coder: NSCoder) {
        coder.encode(self.name, forKey: "name")
        coder.encode(self.isActive, forKey: "isActive")
    }
}

1

मैं अपनी खुद की संरचना का उपयोग करता हूं। यह बहुत आसान है।

struct UserDefaults {
    private static let kUserInfo = "kUserInformation"

    var UserInformation: DataUserInformation? {
        get {
            guard let user = NSUserDefaults.standardUserDefaults().objectForKey(UserDefaults.kUserInfo) as? DataUserInformation else {
                return nil
            }
            return user
        }
        set {

            NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: UserDefaults.kUserInfo)
        }
    }
}

काम में लाना:

let userinfo = UserDefaults.UserInformation

1

आपको इस तरह से NSCoding के लिए History Model Class बनाना होगा

स्विफ्ट 4 और 5।

class SSGIFHistoryModel: NSObject, NSCoding {

   var bg_image: String
   var total_like: String
   var total_view: String
   let gifID: String
   var title: String

   init(bg_image: String, total_like: String, total_view: String, gifID: String, title: String) {
    self.bg_image = bg_image
    self.total_like = total_like
    self.total_view = total_view
    self.gifID = gifID
    self.title = title

}

required init?(coder aDecoder: NSCoder) {
    self.bg_image = aDecoder.decodeObject(forKey: "bg_image") as? String ?? ""
    self.total_like = aDecoder.decodeObject(forKey: "total_like") as? String ?? ""
    self.total_view = aDecoder.decodeObject(forKey: "total_view") as? String ?? ""
    self.gifID = aDecoder.decodeObject(forKey: "gifID") as? String ?? ""
    self.title = aDecoder.decodeObject(forKey: "title") as? String ?? ""

}

func encode(with aCoder: NSCoder) {
    aCoder.encode(bg_image, forKey: "bg_image")
    aCoder.encode(total_like, forKey: "total_like")
    aCoder.encode(total_view, forKey: "total_view")
    aCoder.encode(gifID, forKey: "gifID")
    aCoder.encode(title, forKey: "title")
}
}

उसके बाद आपको NSData में ऑब्जेक्ट को संग्रहीत करने की आवश्यकता होगी, फिर इसे उपयोगकर्ता की चूक से बचाएं और इसे उपयोगकर्ता की डिफ़ॉल्ट से पुनर्प्राप्त करें और इसे फिर से अनारक्षित करें। आप इसे इस तरह संग्रहित और अनारक्षित कर सकते हैं

func saveGIFHistory() {

    var GIFHistoryArray: [SSGIFHistoryModel] = []

    GIFHistoryArray.append(SSGIFHistoryModel(bg_image: imageData.bg_image, total_like: imageData.total_like, total_view: imageData.total_view, gifID: imageData.quote_id, title: imageData.title))
    if let placesData = UserDefaults.standard.object(forKey: "GIFHistory") as? NSData {
        if let placesArray = NSKeyedUnarchiver.unarchiveObject(with: placesData as Data) as? [SSGIFHistoryModel] {
            for data in placesArray {

                if data.gifID != imageData.quote_id {
                    GIFHistoryArray.append(data)
                }
            }
        }
    }

    let placesData2 = NSKeyedArchiver.archivedData(withRootObject: GIFHistoryArray)
    UserDefaults.standard.set(placesData2, forKey: "GIFHistory")
}

आशा है कि यह आपकी समस्या का समाधान करेगा


1

UserDefaults में कस्टम ऑब्जेक्ट्स को सहेजने का एक सरल उदाहरण नीचे दिया जाएगा:

आपको GREAT, CODABLE ’की मदद से वस्तुओं को बचाने / पुनः प्राप्त करने के लिए बॉयलरप्लेट कोड लिखने की आवश्यकता नहीं है, यही कारण है कि यह आपके लिए परेशान मैनुअल एन्कोडिंग / डिकोडिंग से बचाव के लिए है।

यदि आप पहले से ही NSCoding का उपयोग कर रहे हैं और कोडबल (एनकोडेबल + डिकोडेबल का संयोजन) प्रोटोकॉल पर स्विच कर रहे हैं, तो बस नीचे दिए गए दो तरीकों से छुटकारा पाएं

required init(coder decoder: NSCoder) // NOT REQUIRED ANY MORE, DECODABLE TAKES CARE OF IT

func encode(with coder: NSCoder) // NOT REQUIRED ANY MORE, ENCODABLE TAKES CARE OF IT

चलो कोडेबल की सादगी के साथ शुरू करते हैं:

कोई कस्टम वर्ग या संरचना बनाएँ जिसे आप UserDefaults में संग्रहीत करना चाहते हैं

struct Person : Codable {
    var name:String
}

OR

class Person : Codable {

    var name:String

    init(name:String) {
        self.name = name
    }
}

नीचे के रूप में UserDefaults में ऑब्जेक्ट सहेजें

 if let encoded = try? JSONEncoder().encode(Person(name: "Dhaval")) {
     UserDefaults.standard.set(encoded, forKey: "kSavedPerson")
 }

नीचे के रूप में UserDefaults से ऑब्जेक्ट लोड करें

guard let savedPersonData = UserDefaults.standard.object(forKey: "kSavedPerson") as? Data else { return }
guard let savedPerson = try? JSONDecoder().decode(Person.self, from: savedPersonData) else { return }

print("\n Saved person name : \(savedPerson.name)")

बस।


मैंने अभी देखा है कि आपने दो प्रश्नों के लिए समान उत्तर ( यह एक और एक ) पोस्ट किया है। यदि आपको दो प्रश्न दिखाई देते हैं, जिनका उत्तर एक उत्तर के साथ दिया जा सकता है, तो कृपया एक डुप्लिकेट ध्वज उठाएं। यदि दोनों प्रश्न अलग-अलग हैं, तो कृपया प्रत्येक प्रश्न के उत्तर को दर्जी करें। धन्यवाद!
20'20

0

मुझे पता है कि यह सवाल पुराना है, लेकिन मैंने एक छोटी सी लाइब्रेरी लिखी जो मदद की हो सकती है:

आप इस तरह से ऑब्जेक्ट को स्टोर करते हैं:

class MyObject: NSObject, Codable {

var name:String!
var lastName:String!

enum CodingKeys: String, CodingKey {
    case name
    case lastName = "last_name"
   }
}

self.myStoredPref = Pref<MyObject>(prefs:UserDefaults.standard,key:"MyObjectKey")
let myObject = MyObject()
//... set the object values
self.myStoredPref.set(myObject)

और फिर वस्तु को उसके मूल मूल्य में वापस लाने के लिए:

let myStoredValue: MyObject = self.myStoredPref.get()

0

अपनी कक्षा पर कोडेबल प्रोटोकॉल लागू करने के बाद, आप अपने कस्टम ऑब्जेक्ट्स को UserDefaults में सहेजने के लिए PropertyListEncoder का उपयोग कर सकते हैं। बता दें कि कस्टम क्लास यूजर है

class User: NSObject, Codable {

  var id: Int?
  var name: String?
  var address: String?
}

कोडेबल का अर्थ है कि यह डिकोडेबल और एनकोडेबल प्रोटोकॉल दोनों को लागू करता है। जैसा कि नाम से पता चलता है, एनकोडेबल और डिकोडेबल को लागू करने से, आप अपनी कक्षा को एक बाहरी प्रतिनिधित्व जैसे JSON या प्रॉपर्टी लिस्ट से एन्कोड और डिकोड किया जा सकता है।

अब आप संपत्ति सूची में अनुवादित अपने मॉडल को बचा सकते हैं।

if let encodedData = try? PropertyListEncoder().encode(userModel){
  UserDefaults.standard.set(encodedData, forKey:"YOUR_KEY")
  UserDefaults.standard.synchronize();
}

और आप PropertyListDecoder का उपयोग करके अपने मॉडल को पुनः प्राप्त कर सकते हैं। क्योंकि आपने इसे संपत्ति सूची के रूप में एन्कोड किया था।

if let data = UserDefaults.standard.value(forKey:"YOUR_KEY") as? Data {
     return try? PropertyListDecoder().decode(User.self, from:data)
}

-3

आप सीधे प्रॉपर्टी लिस्ट में ऑब्जेक्ट स्टोर नहीं कर सकते हैं; आप केवल व्यक्तिगत स्ट्रिंग्स या अन्य आदिम प्रकारों (पूर्णांक आदि) को स्टोर कर सकते हैं, इसलिए आपको इसे व्यक्तिगत स्ट्रिंग्स के रूप में संग्रहीत करने की आवश्यकता है, जैसे:

   override init() {
   }

   required public init(coder decoder: NSCoder) {
      func decode(obj:AnyObject) -> AnyObject? {
         return decoder.decodeObjectForKey(String(obj))
      }

      self.login = decode(login) as! String
      self.password = decode(password) as! String
      self.firstname = decode(firstname) as! String
      self.surname = decode(surname) as! String
      self.icon = decode(icon) as! UIImage
   }

   public func encodeWithCoder(coder: NSCoder) {
      func encode(obj:AnyObject) {
         coder.encodeObject(obj, forKey:String(obj))
      }

      encode(login)
      encode(password)
      encode(firstname)
      encode(surname)
      encode(icon)
   }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.