I am trying to save a very simple object using NSKeyedArchiver in swift, and I see that it is saving correctly, with values, but whenever it tries to decode the saved data it fails. I am new to swift and I have tried googling and os_log (ing) everything, but I can't see where the problem lies. Here is the Object I am trying to save...
class TrackedAmount: NSObject, NSCoding {
var cups: Int
var fraction: Float?
struct PropertyKey {
static let cups = "cups"
static let fraction = "fraction"
}
init?(cups: Int, fraction: Float?) {
os_log("Running init? function", log: .default, type: .debug)
if(cups < 0) {
os_log("Cups less than 0. Returning nil", log: .default, type: .debug)
return nil
}
self.cups = cups
self.fraction = fraction
}
required convenience init? coder aDecoder: NSCoder) {
os_log("Running required convenience init? function", log: .default, type: .debug)
guard let cups = aDecoder.decodeObject(forKey: PropertyKey.cups) as? Int else {
os_log("Unable to decode cups", log: .default, type: .debug)
return nil
}
guard let fraction = aDecoder.decodeObject(forKey: PropertyKey.fraction) as? Float else {
os_log("Unable to decode fraction", log: .default, type: .debug)
return nil
}
self.init(cups: cups, fraction: fraction)
}
func encode(with aCoder: NSCoder) {
aCoder.encode(cups, forKey: PropertyKey.cups)
aCoder.encode(fraction, forKey: PropertyKey.fraction)
}
Then in my ViewController I am trying to get and save them like so,...
private func saveAmount() {
trackedToday = TrackedAmount(cups: selectedCups, fraction: selectedFraction)
print("data.... cups: " + String(trackedToday!.cups) + " fractions: " + String(trackedToday!.fraction!))
let fullPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("dailytracked")
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: trackedToday!, requiringSecureCoding: false)
try data.write(to: fullPath)
} catch {
os_log("Unable to save tracking amount", log: .default, type: .debug)
}
}
private func loadDailyTracked() -> TrackedAmount? {
let fullPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("dailytracked")
if let nsData = NSData(contentsOf: fullPath) {
do {
let data = Data(referencing: nsData)
if let loadedData = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? TrackedAmount {
os_log("it finally worked", log: .default, type: .debug)
return loadedData
}
} catch {
os_log("Couldn't read from file", log: .default, type: .debug)
return nil
}
return nil
}
It is saving correctly but whenever I try to run the loadDailyTracked() method I get the following in the output section in xcode...
Running required convenience init? function
Unable to decode cups
I can't seem to figure out why it can't decode the cups, I know the data is being saved since none of the logs show any failure except for when it is trying to read from the saved data. I even logged the information before saving and it is showing the correct data. Anyone have any ideas? I am really new to IOS and Swift and I just can't figure this out. Thank you.
*Side note, if you need more code or information I am happy to provide it, I was just trying to keep the post from being too big if it didn't need to be.
In terms of NSCoder
scalar types like Int
and Float
are not objects and the dedicated methods don't return optionals
required convenience init? coder aDecoder: NSCoder) {
os_log("Running required convenience init? function", log: .default, type: .debug)
let cups = aDecoder.decodeInteger(forKey: PropertyKey.cups)
let fraction = aDecoder.decodeFloat(forKey: PropertyKey.fraction)
self.init(cups: cups, fraction: fraction)
}
NSCoding
in Swift is pretty heavy. It's highly recommended to use the Codable
protocol to serialize structs and classes.