Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSKeyedArchiver does not archive my Codable class

Here is my Codable class:

class SensorOutput: Codable {

    var timeStamp: Date?

    var gyroX: Double?
    var gyroY: Double?
    var gyroZ: Double?

    var accX: Double?
    var accY: Double?
    var accZ: Double?

    var magX: Double?
    var magY: Double?
    var magZ: Double?

    init() {}
}

Here I try to write and read the object of that class to file:

    let myData = SensorOutput()
    myData.timeStamp = Date()
    myData.gyroX = 0.0
    myData.gyroY = 0.0
    myData.gyroZ = 0.0
    myData.accX = 0.0
    myData.accY = 0.0
    myData.accZ = 0.0
    myData.magX = 0.0
    myData.magY = 0.0
    myData.magZ = 0.0

    NSKeyedArchiver.archiveRootObject(myData, toFile: filePath)

    if let Data = NSKeyedUnarchiver.unarchiveObject(withFile: filePath) as? SensorOutput {
        print (Data)
    }

This gives an error during the process of archiving: Error screenshot

PS: filePath I receiving in such way:

var filePath: String {
    //1 - manager lets you examine contents of a files and folders in your app; creates a directory to where we are saving it
    let manager = FileManager.default
    //2 - this returns an array of urls from our documentDirectory and we take the first path
    let url = manager.urls(for: .documentDirectory, in: .userDomainMask).first
    print("this is the url path in the documentDirectory \(String(describing: url))")
    //3 - creates a new path component and creates a new file called "Data" which is where we will store our Data array.
    return (url!.appendingPathComponent("Data").path)
}

Reading/writing works with Int or Double and with other supported types, but not with my type. What’s wrong?

like image 424
Degtiarev Aleksei Avatar asked Apr 21 '18 02:04

Degtiarev Aleksei


1 Answers

Although @matt's answer contains the essential information to solve your problem, it might not be obvious how to apply that information if you're new to Swift and iOS programming.

You tried using NSKeyedArchiver.archiveRootObject(_:toFile:), which is a class method, so you didn't have to create an instance of NSKeyedArchiver. Since encodeEncodable(_:forKey:) is an instance method, not a class method, you need to create an instance of NSKeyedArchiver to use it. You also need to create an NSMutableData for the archiver to append bytes to, and you have to call finishEncoding after encoding your object.

    let sensorOutput = SensorOutput()
    sensorOutput.timeStamp = Date()

    let mutableData = NSMutableData()
    let archiver = NSKeyedArchiver(forWritingWith: mutableData)
    try! archiver.encodeEncodable(sensorOutput, forKey: NSKeyedArchiveRootObjectKey)
    archiver.finishEncoding()

    // You can now write mutableData to a file or send it to your server
    // or whatever.

Similarly, you tried using NSKeyedUnarchiver.unarchiveObject(withFile:), which is a class method, but you need to use decodeDecodable(_:forKey:) or decodeTopLevelDecodable(_:forKey:), which are instance methods. So you need to read in the archive data and use it to make an instance of NSKeyedUnarchiver.

// Read in the data from a file or your server or whatever.
// I'll just make an immutable copy of the archived data for this example.
let data = mutableData.copy() as! Data

let unarchiver = NSKeyedUnarchiver(forReadingWith: data)
do {
    if let sensorOutputCopy = try unarchiver.decodeTopLevelDecodable(SensorOutput.self, forKey: NSKeyedArchiveRootObjectKey) {
        print("deserialized sensor output: \(sensorOutputCopy)")
    }
} catch {
    print("unarchiving failure: \(error)")
}

(I prefer the decodeTopLevelDecodable method instead of decodeDecodable because it throws a Swift error instead of crashing if the archive is corrupt.)

like image 54
rob mayoff Avatar answered Sep 19 '22 23:09

rob mayoff