0

I'm trying to save a custom class array to UserDefaults but it doesn't work. I get nil back on if let. I looked everywhere online. I'm using Swift 4.2

extension UserDefaults {
    func saveReciters(_ reciters: [Reciter]) {
        do {
            let encodedData = try NSKeyedArchiver.archivedData(withRootObject: reciters, requiringSecureCoding: false)
            self.set(encodedData, forKey: UD_RECITERS)
        } catch {
            debugPrint(error)
            return
        }
    }

    func getReciters() -> [Reciter] {
        if let reciters = self.object(forKey: UD_RECITERS) as? Data {
            return NSKeyedUnarchiver.unarchiveObject(with: reciters) as! [Reciter]
        } else {
            print("EMPTY RECITERS")
            return [Reciter]()
        }
    }
}

UserInfo={NSDebugDescription=Caught exception during archival: -[_SwiftValue encodeWithCoder:]: unrecognized selector sent to instance 0x600001babcc0

Thats my class:

class Reciter: NSCoding {

private(set) public var name: String
private(set) public var image: UIImage?
private(set) public var surahs: [Surah]
private(set) public var documentID: String

private let quranData = QuranData()

init(name: String, image: UIImage?, surahCount: Int?, documentID: String) {
    self.name = name
    self.image = image
    self.documentID = documentID

    if let surahCount = surahCount {
        surahs = Array(quranData.getAllSurahs().prefix(surahCount))
    } else {
        surahs = quranData.getAllSurahs()
    }
}

func encode(with aCoder: NSCoder) {

}

required init?(coder aDecoder: NSCoder) {

}
}

On my Surah class i get nil back. All other properties i get back succesfully enter image description here

SwiftiSwift
  • 7,528
  • 9
  • 56
  • 96
  • 1
    Don't `try?`. `Catch` the error. Most likely it will tell you what's wrong, presumably something about *... is not key-value coding compliant...* – vadian Mar 03 '19 at 18:53
  • i get this: UserInfo={NSDebugDescription=Caught exception during archival: -[_SwiftValue encodeWithCoder:]: unrecognized selector sent to instance 0x600001babcc0 – SwiftiSwift Mar 03 '19 at 18:58
  • 1
    Your class doesn't conform to `NSCoding`, it has to. And the class must inherit from `NSObject`. Consider to use native `Codable` protocol to serialize the class. – vadian Mar 03 '19 at 18:59
  • okay but what should i type in the required protocol methods? – SwiftiSwift Mar 03 '19 at 19:01
  • 1
    I have no idea without knowing the class declaration. Basically you have to encode and decode all properties. – vadian Mar 03 '19 at 19:03
  • I'm not sure how to do it. I've added the class in the question – SwiftiSwift Mar 03 '19 at 19:04
  • 1
    Please look at https://stackoverflow.com/questions/32617425/create-a-class-using-nscoding. `Surah` must conform to `NSCoding`, too. And your class must be a subclass of `NSObject` – vadian Mar 03 '19 at 19:08
  • Thank you so much :)) It works now – SwiftiSwift Mar 03 '19 at 19:15
  • I get nil back on the Surah class. Check the question again please – SwiftiSwift Mar 03 '19 at 19:55
  • 1
    Wrong API: `decodeInteger(forKey: "number")` and no type cast. And stop using this ugly `private(set)`. If you want constants use `let` – vadian Mar 03 '19 at 19:58
  • Oh my god finally. THANK YOU THE SECOND TIME :)) – SwiftiSwift Mar 03 '19 at 20:00

1 Answers1

0

Most often I see developer's use codeable, here I am using user as an example:

YourDataModel.swift

struct User: Codable {
    var userId: String = ""
    var name: String = ""
    var profileImageData: Data? }

UserDefaults.swift

import Foundation

extension UserDefaults {

    /// The current user of the application, see `./Models/User.swift`
    var currentUser: User? {
        get {
            guard let userData = self.object(forKey: #function) as? Data else { return nil }
            return try? JSONDecoder().decode(User.self, from: userData)
        }

        set {
            guard let newuser = newValue else { return }
            if let userData = try? JSONEncoder().encode(newuser) {
                self.set(userData, forKey: #function)
            }
        }
    }

}

Transform the data into json data... #function is the function or value name i.e.

    // For the case the user doesn't yet exist.
    if ( UserDefaults.standard.currentUser == nil )  {
        // Create a new user
        user = User()
        // Generate an id for the user, using a uuid.
        user?.userId = UUID().uuidString
    } else {
        // otherwise, fetch the user from user defaults.
        user = UserDefaults.standard.currentUser
    }
RLoniello
  • 2,309
  • 2
  • 19
  • 26