Issue #334

NSSecureCoding has been around since iOS 6 and has had some API changes in iOS 12

A protocol that enables encoding and decoding in a manner that is robust against object substitution attacks.

If the coder responds true to requiresSecureCoding, then the coder calls failWithError(_:) in either of the following cases: The class indicated by cls doesn’t implement NSSecureCoding. The unarchived class doesn’t match cls, nor do any of its superclasses.

If the coder doesn’t require secure coding, it ignores the cls parameter and does not check the decoded object.

The class must subclass from NSObject and conform to NSSecureCoding

class Note: NSObject, NSSecureCoding {
    static var supportsSecureCoding: Bool = true

    func encode(with aCoder: NSCoder) {
        aCoder.encode(id, forKey: "id")
        aCoder.encode(text, forKey: "text")
        aCoder.encode(date, forKey: "date")

    required init?(coder aDecoder: NSCoder) {
            let id = aDecoder.decodeObject(of: [NSString.self], forKey: "id") as? String,
            let text = aDecoder.decodeObject(of: [NSString.self], forKey: "text") as? String,
            let date = aDecoder.decodeObject(of: [NSDate.self], forKey: "date") as? Date
        else {
            return nil
        } = id
        self.text = text = date

    let id: String
    var text = "untitled"
    var date: Date = Date()

    override init() {
        id = UUID().uuidString

First, we need to serialize to Data, then use EasyStash for easy persistency

do {
    let securedItems ={ SecuredClientLoggerItem(item: $0) })
    if #available(iOS 11.0, *) {
        let data = try NSKeyedArchiver.archivedData(
            withRootObject: securedItems,
            requiringSecureCoding: true

        try data.write(to: fileUrl)
    } else {
        _ = NSKeyedArchiver.archiveRootObject(
            toFile: fileUrl.path
} catch {

Then we can use unarchiveTopLevelObjectWithData to unarchive array

do {
    let data = try Data(contentsOf: fileUrl)
    let notes = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? [Note]
    // notes is of type [Note]?
} catch {

Note that for UUID, NSCoding seems to convert to UUID instead of String

let id = aDecoder.decodeObject(
    of: [NSUUID.self],
    forKey: "id"
) as? UUID,