`supportsSecureCoding` crashes when using Optimize for Speed option

2.1k Views Asked by At

I'm having trouble creating classes that uses NSSecureCoding and its subclasses.

class ClassA: NSObject, NSSecureCoding {
    public static var supportsSecureCoding: Bool { return true }
}

class ClassB: ClassA {
    public static var supportsSecureCoding: Bool { return true } // "Cannot override static var"
}

I'm supposed to call this since the documentation in NSObject.h says,

This property must return YES on all classes that allow secure coding. Subclasses of classes that adopt NSSecureCoding and override initWithCoder: must also override this method and return YES. // The Secure Coding Guide should be consulted when writing methods that decode data.

Objective-C:

@property (class, readonly) BOOL supportsSecureCoding;

Swift:

public static var supportsSecureCoding: Bool { get }

I am using Xcode 10.0, tried on both Swift 4.0 and Swift 4.2. How are people getting around this? Any help is appreciated.

UPDATE: When using public class var supportsSecureCoding, it compiles but it crashes at runtime when Optimize for Speed is used.

4

There are 4 best solutions below

5
On BEST ANSWER

Seems the current optimizer of Swift suppresses to generate the overridden getter method when its definition is the same as its superclass. What a clever optimizer!?

This sort of hack would suppress such too strong optimization.

class ClassB: ClassA {

    //...

    static private var secureCoding = true
    override public class var supportsSecureCoding: Bool { return secureCoding }

}

static private let does not have the same effect. So, when the Swift optimizer being more clever, the code above may not work. Better send a bug report soon.


Seems the Swift optimizer is already clever enough and the workaround above may not work. (See Martin R's comment.)

You may need to remove private.

class ClassB: ClassA {

    //...

    static var secureCoding = true
    override public class var supportsSecureCoding: Bool { return secureCoding }

}
3
On

Fix that worked for me

private static var secureCodingWorkaround = true
@objc override public class var supportsSecureCoding: Bool { return secureCodingWorkaround }
5
On

static in a class declaration is an alias for final class, i.e. a type method which cannot be overridden in a subclass. What you want is a class method

public class var supportsSecureCoding: Bool { return true }

which can be overridden in the subclass with

override public class var supportsSecureCoding: Bool { return true }
0
On

I could not get any of the above answers to work. This is definitely a bug on Apple's side they will need to address so make sure you send it in the Feedback system. I even added the @objc without luck.

The Swift 5.3 compiler now gives a deprecation message for NSKeyedArchiver for initializations that don't specify secure coding.. but apparently the language doesn't allow for overriding this in subclasses.

When I don't override the variable I get: Error: Class 'Subclass' has a superclass that supports secure coding, but 'Subclass' 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. (NSInvalidUnarchiveOperationException)

When I do override the variable I get: "Cannot override a static variable"

CONDITIONAL WORKAROUND:

This workaround worked for me, as I have a super type that is sort of abstract in that it is never used without a subtype. If you have a similar setup, this should work for you:

public class Supertype: NSObject, NSCoding {
     ...

}

Subtype:

public class SubType: Supertype, NSSecureCoding {

     public static var supportsSecureCoding = true
     ...
}

This way the var is at least in all subtypes that I use.

If you instantiate and work with Supertype directly, this will not work for you.