Search code examples
swiftuserdefaults

Setting DateComponents array to UserDefaults not working


I'm making a single-view application which manages all the tasks one works on. I have two arrays: one that stores all the task names and on that shows the finishing times for the tasks. I assign both of those to UserDefaults:

var tasks = [String]()
var finishingDates = [DateComponents]()
let defaults = UserDefaults()
class ViewController: UIViewController {

    func setDefaults() {
        defaults.set(tasks, forKey: "tasks")
        defaults.set(finishingDates, forKey: "finishingDates")

    }
    override func viewDidLoad() {
        super.viewDidLoad()

        tasks = defaults.stringArray(forKey: "tasks") ?? [String]()
        finishingDates = defaults.array(forKey: "finishingDates") as? [DateComponents] ?? [DateComponents]()
    }


}

Then I test to make sure the arrays are working:

tasks = ["task"]
finishingDates = [DateComponents(calendar: calendar, year: 1910, month: 10, day: 1)]

setDefaults()

However, when I run it, the app crashes. In the app delegate, there is the SIGABRT error.

I add an exception breakpoint and it gets called on this line:

defaults.set(finishingDates, forKey: "finishingDates")

It only gets called on this line and not on the line that sets the String array. Other than that, the arrays are identical. How can I solve this?


Solution

  • You can't save an array of DateComponents objects directly to UserDefaults but you can save its Data. DateComponents conforms to Codable protocol so you just need to encode/decode your array using JSONEncoder/JSONDecoder and save the resulting data. Btw I recommend writing it to a JSON text file instead of persisting it with UserDefaults:


    Playground testing:

    var finishingDates: [DateComponents] = []
    finishingDates.append(.init(calendar: .current, year: 2019, month: 7, day: 13))
    finishingDates.first!.month!
    do {
        let finishingDatesData = try JSONEncoder().encode(finishingDates)
        print(String(data: finishingDatesData, encoding: .utf8) ?? "")
    
        UserDefaults.standard.set(finishingDatesData, forKey: "finishingDates")
        print("finishingDates saved to userdefaults")
    
        if let data = UserDefaults.standard.data(forKey: "finishingDates") {
            let loadedDateComponents = try JSONDecoder().decode([DateComponents].self, from: data)
            print(loadedDateComponents)  // "[calendar: gregorian (fixed) year: 2019 month: 7 day: 13 isLeapMonth: false ]\n"
        }
    } catch { 
         print(error) 
    }
    

    This will print:

    "[{"day":13,"year":2019,"calendar":{"locale":{"identifier":"en_US"},"timeZone":{"identifier":"America\/Sao_Paulo"},"identifier":"gregorian","minimumDaysInFirstWeek":1,"firstWeekday":1},"month":7}]\n"

    and

    "[calendar: gregorian (fixed) year: 2019 month: 7 day: 13 isLeapMonth: false ]\n"