-3

how to set and later get array of json objects in UserDefaults?

My application crashes when i try to set it as follow:

import SwiftyJSON
var finalArray = [JSON]()

UserDefaults.standard.set(finalArray, forKey: "attemptedArray")

my data looks like:

[{
  "marked" : 3,
  "attempted" : true,
  "correct" : 3,
  "subject" : 1,
  "status" : true,
  "question" : 219,
  "answer" : 32931,
  "time" : 15,
  "score" : 5,
  "chapter" : 26
}, {
  "marked" : 4,
  "attempted" : true,
  "correct" : 4,
  "subject" : 1,
  "status" : true,
  "question" : 550,
  "answer" : 34256,
  "time" : 23,
  "score" : 10,
  "chapter" : 26
}, {
  "marked" : 1,
  "attempted" : true,
  "correct" : 1,
  "subject" : 1,
  "status" : true,
  "question" : 566,
  "answer" : 34317,
  "time" : 33,
  "score" : 14,
  "chapter" : 26
}]
Deepak Verma
  • 373
  • 7
  • 19
  • 3
    "My application crashes" And what's the error message? `JSON` doesn't seem to be NSCoding compliant and you can't save anything you want into `UserDefaults` (you need to transform it into allowed types). Check the doc of it. – Larme Feb 18 '19 at 11:31
  • it cant save objects of user created classes... You are saving objects of 'JSON', convert it into dictionary, then you can save – Mehul Thakkar Feb 18 '19 at 11:31
  • You have variable named `finalArr` but you are setting `finalArray `. Please use the proper variable. – pkc456 Feb 18 '19 at 11:33
  • You could maybe convert to `[[String : Any]]`? – George Feb 18 '19 at 11:33
  • 2
    This is abusing user defaults. Please don't abuse user defaults. – Desdenova Feb 18 '19 at 11:45

3 Answers3

4

UserDefaults can't save SwiftyJSON's JSON type. You have to save some type which they supports, in this case, you're looking for Data.

Anyway, for saving Data to UserDefaults aren’t the best and you should save your Data to file somewhere else. To achieve this, you can use FileManager.


So, create custom model for your data instead of using JSON and adopt Codable to your custom model

struct Model: Codable {
    var marked, correct, subject, question, answer, time, score, chapter: Int
    var attempted, status: Bool
}

Then you should use this Model as type of element inside your array (note that then you’ll need to decode your response from Data using JSONDecoder (see below))

var finalArray = [Model]()

then you can use JSONEncoder for encoding your array of models to Data which you can write to some file

do {
    let encoded = try JSONEncoder().encode(finalArray)
    let preferencesDirectoryURL = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first!.appendingPathComponent("Preferences", isDirectory: true)
    let fileURL = preferencesDirectoryURL.appendingPathComponent("fileName.json")
    try encoded.write(to: fileURL)
} catch { print(error) }

and JSONDecoder for decoding Data from saved file

do {
    let preferencesDirectoryURL = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first!.appendingPathComponent("Preferences", isDirectory: true)
    let fileURL = preferencesDirectoryURL.appendingPathComponent("fileName.json")

    if let data = try? Data(contentsOf: fileURL) {
        let decoded = try JSONDecoder().decode([Model].self, from: data)
    }
} catch { print(error) }
Robert Dresler
  • 10,580
  • 2
  • 22
  • 40
  • 2
    The Codable implementation is fine. What it is not is saving the data to UserDefaults. It is mean for storing only the interface state of the App. It is not meant for storing general app data. You can write the data to disk into a separate file `.json` (which is just a simple text file properly formatted) to the same directory UserDefaults uses to store its preferences file `Library/Preferences` https://stackoverflow.com/a/34701970/2303865 – Leo Dabus Feb 18 '19 at 14:46
  • 1
    Related https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html – Leo Dabus Feb 18 '19 at 15:14
0
do{
            try let data = NSKeyedArchiver.archivedData(withRootObject: finalArray, requiringSecureCoding: false)
    UserDefaults.standard.set(data, forKey: "attemptedArray")
        }catch{
            print("Print exception here")
        }

Read:- Property List Programming Guide

pkc456
  • 8,350
  • 38
  • 53
  • 109
0

Saving a lot of data in the NSuser defaults isn’t recommended for some reasons:

  1. If you will delete some of the cells of the array you will need to delete them from the nsuser defaults. It will be easy only if you save them as an array and not as individuals.

  2. If you choose to save them as array every time you will delete or add one item you will need to save all of them. It can take slot of time if the array is big

  3. Anyone can access the NSuser defaults. So it isn’t safe

Anyway I really recommend using core data. It saves you all the troubles I mentioned.

100tomer
  • 137
  • 1
  • 10