0

I want to save the following data:

class Book: NSObject, NSCoding {

var title: String
var chapter: [Chapter]

struct Chapter {
    var chapTitle: String
    var texte: [Texte]
}

struct Texte {
    var description: String
    var texte: [String]
    var position: [Int]
    var hidden: [Int?]
}

I am currently using the NSCoder to save the Book class:

static let DocumentsDirectory = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
static let ArchiveURL = DocumentsDirectory.appendingPathComponent("learn")


struct PropertyKey {
    static let title = "title"
    static let chapter = "Chapter"
}

//MARK: - Initialization
init?(title: String, chapter: [Book.Chapter]) {        
    self.title = title
    self.chapter = chapter
}

//MARK: - NSCoding
func encode(with aCoder: NSCoder) {
    aCoder.encode(title, forKey: PropertyKey.title)
    aCoder.encode(chapter, forKey: PropertyKey.chapter)
}

required convenience init?(coder aDecoder: NSCoder) { 
    let title = aDecoder.decodeObject(forKey: PropertyKey.title) as! String
    let chapter = aDecoder.decodeObject(forKey: PropertyKey.chapter) as! [Book.Chapter]

    self.init(title: title, chapter: chapter)
}

With this I can save the Book's title, and an empty array of chapter.

However when I try to add chapTitle and texte to a Book object, I get the following error:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[SwiftValue encodeWithCoder:]: unrecognized selector sent to instance 0x60000066dec0'

Does this mean that the struct could not be saved ? What should I change ?

1 Answers1

0

You better use codable if you can make book a struct .

struct Book: Codable {

var title: String
var chapter: [Chapter]

struct Chapter:Codable {
    var chapTitle: String
    var texte: [Texte]
}

struct Texte:Codable {
    var description: String
    var texte: [String]
    var position: [Int]
    var hidden: [Int?]
}

Save

static func saveData (books: [Book]) throws
{
     let encoded = try JSONEncoder().encode(books)
     try encoded.write(to: ArchiveURL)
     print("Books saved")
}

Retrive

//retrive data from it's saved place
static func loadData() throws -> [Book]
{
     let data = try Data(contentsOf: ArchiveURL)
     return try JSONDecoder().decode([Book].self, from: data)
}

Also edit the variables on top

let directory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/learn/"

let ArchiveURL = URL(fileURLWithPath: directory)
vadian
  • 274,689
  • 30
  • 353
  • 361
Sameh
  • 436
  • 4
  • 13
  • I tried to use Codable, but when I try to load the data from URL, it returns `nil`. Here is the code I used: SAVE: `static func saveData (books: [Book]) { guard let encoded = try? JSONEncoder().encode(books) else { return } //To user defaults or what ever you choose try? encoded.write(to: ArchiveURL) print("Books saved") }` – θ Grunberg Jun 18 '18 at 10:55
  • LOAD: `static func loadData() -> [Book]?{ let data = ArchiveURL.absoluteURL.dataRepresentation let parsedData = try? JSONDecoder().decode([Book].self, from: data) return parsedData }` – θ Grunberg Jun 18 '18 at 10:58
  • your load and save function are wrong , i included two them in the updated answer , please test it . – Sameh Jun 18 '18 at 13:22
  • Ignoring the error (`try?`) and loading data via `FileManager` is not a good suggestion. Make both functions `throw`, return a non-optional in the `load` function and load the data with `let data = try Data(contentsOf: directoryURL)` since you are dealing with an URL anyway. – vadian Jun 18 '18 at 13:32
  • @vadian: Correct. When I loaded the data with `let data = try Data(contentsOf: directoryURL)`, it worked perfectly. Also the `throw` functions allowed me to identify an error in my data saving. – θ Grunberg Jun 18 '18 at 14:09
  • @Sameh: Please reflect vadian 's comment, so that I can mark your answer as the solution. Thank you for your help ! – θ Grunberg Jun 18 '18 at 14:11
  • @Sameh I edited the answer to remove redundant code. The benefit of passing through the error is that you can omit the `do - catch` block. – vadian Jun 18 '18 at 14:48
  • @vadian Thanks . – Sameh Jun 18 '18 at 14:51