NSSecureCoding how to override the required supportsSecureCoding in a subclass

291 views Asked by At

I'm trying to resolve this runtime error on an iOS project using swift 5

The class must implement +supportsSecureCoding and return YES to verify that its implementation of -initWithCoder: is secure coding compliant." UserInfo={NSDebugDescription=Class 'Divinstructor.ResultDE' has a superclass that supports secure coding, but 'Divinstructor.ResultDE' overrides -initWithCoder: and does not override +supportsSecureCoding. The class must implement +supportsSecureCoding and return YES to verify that its implementation of -initWithCoder: is secure coding compliant.}

However I have not found a way to overcome this error as try to follow the error instruction result in a compilation error that I still fail to solve

I'm updating my application to NSSecureCoding. NSKeyedUnarchiver.unarchiveObject(with:) is deprecated starting iOS12 and have to be replaced by NSKeyedUnarchiver.unarchivedObject(ofClass: ,from: ) which indeed turn in having to move away from NSCoding for NSSecureCoding.

It works for my main class (Result) that I modify to conform to NSSecureCoding. One of the required stuff is to add in the class that conforms to NSSecureCoding

static var supportsSecureCoding: Bool = true

However I'm facing an issue at runtime (see above) for a class (ResultDE) that is inherit from my class (Result).

The error message says that "ResultDE' has a superclass that supports secure coding, but 'Divinstructor.ResultDE' overrides -initWithCoder: and does not override +supportsSecureCoding."

However I have tried many options to override supportsSecureCoding but without any success.

override public static var supportsSecureCoding: Bool= true

The obvious one above also produce the compilation error "Cannot override with a stored property 'supportsSecureCoding'" All my attempts result in a compilation error. "Cannot override with a stored property 'supportsSecureCoding'"

So is anybody as an idea of how to override supportsSecureCoding in a subclass ?

I tried to remove static for the supportsSecureCoding in the man class (making it non conform to protocol) and I still have the "Cannot override with a stored property 'supportsSecureCoding'" compilation error.

here is a short code that reproduce the compilation error


import Foundation

class Result : NSObject, NSSecureCoding {


    static var supportsSecureCoding: Bool = true

    var name: String = "-"

    override init() {
        super.init()
    }

    required init(coder decoder: NSCoder) {
        super.init()
        name = decoder.decodeObject(forKey: "name") as! String
    }
    func encode(with coder: NSCoder) {
        coder.encode(name, forKey: "name")
    }



    class ResultDE: Result {
        override static var supportsSecureCoding: Bool = true

        required init(coder decoder: NSCoder) {
            super.init()
            name = decoder.decodeObject(forKey: "name") as! String
        }
        override func encode(with coder: NSCoder) {
            coder.encode(name, forKey: "name")
        }
    }

}
2

There are 2 answers

2
David H On

EDIT: updated code to use Stephane Gasparini supportsSecureCoding.

class Result : NSObject, NSSecureCoding {
   class var supportsSecureCoding: Bool { true }

    var name: String = "-"

    override init() {
        super.init()
    }

    required init(coder decoder: NSCoder) {
        super.init()
        name = decoder.decodeObject(forKey: "name") as! String
    }
    func encode(with coder: NSCoder) {
        coder.encode(name, forKey: "name")
    }
}


class ResultDE: Result {
    override class var supportsSecureCoding: Bool { true }

    var title: String = "-"

    override init() {
        super.init()
    }

    required init(coder decoder: NSCoder) {
        super.init(coder: decoder)

        name = decoder.decodeObject(forKey: "title") as! String
    }
    override func encode(with coder: NSCoder) {
        super.encode(with: coder)

        coder.encode(name, forKey: "title")
    }
}

I also added this code which generates a warning, since both classes do implement it"

    let r = Result()
    let r1 = ResultDE()

    if let x = r as? NSSecureCoding {
        print("AHA")
    }
    if let x = r1 as? NSSecureCoding {
        print("AHA")
    }

Please note that the subclass calls super for both encode and decode.

Apple is doing something very strange in insuring that the actual class (and not a subclass) is returning supportsSecureCoding (IMHO). What sense does it make for the subclass and the superclass to have different values? If the superclass hasn't provided supportsSecureCoding, then the subclass can provide the var. If the superclass has defined it as false (and why would they), and the subclass returns true, OK that makes sense.

Another oddity - when I ask the runtime if the both classes implement secure coding, the compiler said true, but the runtime failed. Bizarre.

3
Stephane Gasparini On

Thanks to Itai Ferber here is the way to do it.

static var supportsSecureCoding: Bool = true

need to be replaced by

class var supportsSecureCoding: Bool {true}

class then can be overridden in sub-classes.

Here code example with the fixes.

import Foundation

class Result : NSObject, NSSecureCoding {


    public class var supportsSecureCoding: Bool {true}

    var name: String = "-"

    override init() {
        super.init()
    }

    required init(coder decoder: NSCoder) {
        super.init()
        name = decoder.decodeObject(forKey: "name") as! String
    }
    func encode(with coder: NSCoder) {
        coder.encode(name, forKey: "name")
    }



    class ResultDE: Result {
        override public class var supportsSecureCoding: Bool {true} 

        required init(coder decoder: NSCoder) {
            super.init()
            name = decoder.decodeObject(forKey: "name") as! String
        }
        override func encode(with coder: NSCoder) {
            coder.encode(name, forKey: "name")
        }
    }

}