Switching to Swift 5 and IOS 12, I need to conform to NSSecureCoding for Archiver.
I tried using Codable, but I need to store an array of [String: Any] dictionaries and could not get it work.
Maybe JSON encoder could be a good option here ?
The class is pretty simple.
let theKey = "theKey"
class MyFavorites : NSObject, NSCoding {
var myData: [[String: Any]]?
override init() {
super.init()
myData = []
}
required init(coder decoder: NSCoder) {
mesVEs = decoder.decodeObject(forKey: favorisVEKey) as? [[String: Any]]
}
func encode(with coder: NSCoder) {
if let savedData = myData {
coder.encode(savedData, forKey: theKey)
}
}
Archiving and unArchiving are done in a utlity class:
class Util {
class func dataFilePath() -> String {
let paths = NSSearchPathForDirectoriesInDomains(
FileManager.SearchPathDirectory.documentDirectory,
FileManager.SearchPathDomainMask.userDomainMask, true)
let documentsDirectory = paths[0] as NSString
return documentsDirectory.appendingPathComponent("data.archive") as String
}
class func loadData() -> MyFavorites {
let filePath = Util.dataFilePath()
if (FileManager.default.fileExists(atPath: filePath)) {
let data = NSMutableData(contentsOfFile: filePath)!
let unarchiver = NSKeyedUnarchiver(forReadingWith: data as Data)
let allFavoris = unarchiver.decodeObject(forKey: rootKey) as! MyFavorites // whole dictionary
unarchiver.finishDecoding()
return allFavoris
} else {
return MyVEFavoris()
}
}
class func saveData() {
let filePath = dataFilePath()
let favorisToSave = MyFavorites()
favorisToSave.myData = // content read from a stored array of dictionaries
let data = NSMutableData()
let archiver = NSKeyedArchiver(forWritingWith: data)
archiver.encode(favorisToSave, forKey: rootKey)
archiver.finishEncoding()
data.write(toFile: filePath, atomically: true)
}
To make it conform to SecureCoding, I tried to minimize the changes (may be not the best option), so I changed the class as follows:
class MyFavorites : NSObject, NSSecureCoding {
var myData: [[String: Any]]? // No change
static var supportsSecureCoding: Bool {
return true
}
override init() {
super.init() // No change here
self.myData = []
}
required init(coder decoder: NSCoder) {
if let topObject = decoder.decodeObject(of: MyFavorites.self, forKey: theKey) {
myData = topObject.myData
} else {
self.myData = []
}
}
func encode(with coder: NSCoder) {
coder.encode(self, forKey: theKey)
}
Now, I am struggling to adapt the calls to Archiver (loadData and SaveData)
class func loadData() -> MyFavorites {
let filePath = Util.dataFilePath()
if (FileManager.default.fileExists(atPath: filePath)) {
guard let dataObject = NSData(contentsOfFile: filePath) else { return MyFavorites() }
guard let unarchived = try? NSKeyedUnarchiver.unarchivedObject(ofClass: MyFavorites.self, from: dataObject as! Data)
else { return MyFavorites() }
guard let unarchivedArray = unarchived as? [[String: Any]]
else { return MyFavorites() }
let returnFavorites = MyFavorites()
returnFavorites.myData = unarchivedArray
return returnFavorites
} else {
return MyFavorites()
}
And cannot find a way to adapt NSKeyedArchiver to secureCoding