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?
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.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With