Save struct in class to NSUserDefaults using Swift

前端 未结 4 1070
佛祖请我去吃肉
佛祖请我去吃肉 2020-12-30 11:31

I have a class and inside the class is a (swift) array, based on a global struct. I want to save an array with this class to NSUserDefaults. This is my code:



        
相关标签:
4条回答
  • 2020-12-30 11:43

    I've developed a small library which may help. You can use it as a replacement of NSCoding for Swift structs.

    You would need to implement a Koting protocol for mystruct:

    struct mystruct: Koting {
    
        var start : NSDate = NSDate()
        var stop : NSDate = NSDate()
    
        // MARK: - Koting
    
        init?(koter: Koter) {
            guard let start: NSDate = koter.dekotObject(forKey: "start"),
                  let stop: NSDate = koter.dekotObject(forKey: "stop") else {
                return nil
            }
            self.init(start: start, stop: stop)
        }
    
        func enkot(with koter: Koter) {
            koter.enkotObject(start, forKey: "start")
            koter.enkotObject(stop, forKey: "stop")
        }
    }
    

    Since then you may easily convert the struct to Data and back:

    let str = mystruct(start: NSDate(/*...*/), stop: NSDate(/*...*/))
    guard let data = str.de_data else { return }  // potentially may be nil
    let restoredStr = mystruct.de_from(data: data)   // if data is irrelevant, returns `nil`
    

    Finally, this is what you do to implement NSCoding:

    class MyClass: NSObject, NSCoding {
    
        var mystructs: [mystruct]
    
        init(mystructs: [mystruct]) {
    
            self.mystructs = mystructs 
            super.init()
        }
    
        func encode(with aCoder: NSCoder) {
            guard let datas = mystructs.flatMap { $0.de_data } else { return }
            aCoder.encode(datas, forKey: "mystructs")
        }
    
        required convenience init?(coder aDecoder: NSCoder) {
            guard let datas = aDecoder.decodeObject(forKey: "mystructs") as? [Data],
                  let mystructs = datas.flatMap { mystruct.de_from(data: $0) } else {
                return nil
            }
    
            self.init(mystructs : mystructs)
        }
    }
    

    It's pretty much the same code you would write if NSCoding supported Swift structs.

    0 讨论(0)
  • 2020-12-30 11:48

    I use this this in my project while coding with Swift 4:

    let jsonData = """ {"variable1":1234,"variable2":"someString"}"""

    struct MyStruct:Codable{ var variable1 :Int var variable2:String }

    let whatever = try JSONDecoder().decode(MyStruct.self,from:jsonData)
    
    let encoded =try JSONEncoder().encode(whatever)
    
    UserDefaults.standart.set(encoded, forKey:"encodedData")
    

    to fetch data from UserDefaults

    if let data = UserDefaults.standard.object(forKey: "encodedData") as? Data {
        let myStruct = try JSONDecoder().decode(MyStruct.self, from: data)
        print(myStruct)
    }
    
    0 讨论(0)
  • 2020-12-30 12:01

    Swift structs are not classes, therefore they don't conform to AnyObject protocol. You have to rethink your approach. Here are some suggestions:

    1. Convert your struct to final class to enforce immutability

      final class MyStruct {
          let start : NSDate = NSDate()
          let stop : NSDate = NSDate()
      }
      
      encoder.encodeObject(mystructs)
      
    2. Map them as an array dictionaries of type [String: NSDate]

      let structDicts = mystructs.map { ["start": $0.start, "stop": $0.stop] }
      encoder.encodeObject(structDicts)
      
    0 讨论(0)
  • 2020-12-30 12:09

    NSUserDefaults is limited in the types it can handle: NSData, NSString, NSNumber, NSDate, NSArray, NSDictionary, and Bool. Thus no Swift objects or structs can be saved. Anything else must be converted to an NSData object.

    NSUserDefaults does not work the same way as NSArchiver. Since you already have added NSCoder to your classes your best choice might be to save and restore with NSArchiver to a file in the Documents directory..

    From the Apple NSUserDefaults Docs:

    A default object must be a property list, that is, an instance of (or for collections a combination of instances of): NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. If you want to store any other type of object, you should typically archive it to create an instance of NSData.

    0 讨论(0)
提交回复
热议问题