nscodingnskeyedunarchiver

NSCoding error messages using NSKeyedUnarchiver.unarchivedObject(ofClass:from:)


I get a code-time yellow warning message in Xcode that says:

'unarchiveObject(with:)' was deprecated in iOS 12.0: Use +unarchivedObjectOfClass:fromData:error: instead

... when I use the following code:

let oldUbiquityIdentityToken = NSKeyedUnarchiver
    .unarchiveObject(with: oldDataUbiquityIdentityToken!)

When I change to unarchivedObject(ofClass:from:) I get 2 code-time red error messages before I even fill in the arguments:

'NSCoding' cannot be used as a type conforming to protocol 'NSCoding' because 'NSCoding' has static requirements

Static method 'unarchivedObject(ofClass:from:)' requires that 'NSCoding' inherit from 'NSObject'

Why am I getting these error messages?

I change the code to:

let defaultFileManager: FileManager = FileManager.default
let standardUserDefaults = UserDefaults.standard

let ubiquityIdentityToken = defaultFileManager.ubiquityIdentityToken

let oldDataUbiquityIdentityToken = standardUserDefaults.data(forKey: UserDefaultsKeys.ubiquityIdentityToken)

do {
    
    let dataUbiquityIdentityToken: Data = try NSKeyedArchiver.archivedData(withRootObject: ubiquityIdentityToken!, requiringSecureCoding: false) // error messages appear here
    standardUserDefaults.set(dataUbiquityIdentityToken, forKey: UserDefaultsKeys.ubiquityIdentityToken)
    
    let oldUbiquityIdentityToken = try NSKeyedUnarchiver.unarchivedObject(ofClass: (NSCoding & NSCopying & NSObjectProtocol).self, from: oldDataUbiquityIdentityToken!)
    
} catch {
    
    print(error)
    
}

Here are the red code-time errors that appear on the line I mark with comments in the code:

'NSCoding & NSCopying & NSObjectProtocol' cannot be used as a type conforming to protocol 'NSCoding' because 'NSCoding' has static requirements

Static method 'unarchivedObject(ofClass:from:)' requires that 'NSCoding & NSCopying & NSObjectProtocol' inherit from 'NSObject'

I get the argument I put in for the 'forClass' parameter from the declaration of FileManager.ubiquityIdentityToken in Apple's documentation, which says:

@NSCopying var ubiquityIdentityToken: (NSCoding & NSCopying & NSObjectProtocol)? { get }

What threw me off at the beginning is the fact that the errors appear before I even put in the arguments in the placeholders.

Update on July 8, 2022:

Now I get the following code-time error messages when I use the following code:

Static method 'unarchivedObject(ofClass:from:)' requires that 'Data' conform to 'NSCoding'

Static method 'unarchivedObject(ofClass:from:)' requires that 'Data' inherit from 'NSObject'

let oldUbiquityIdentityToken = NSKeyedUnarchiver.unarchivedObject(ofClass: Data.self, from: oldDataUbiquityIdentityToken!)

I didn't get that first error message before, but I did get that second message before.

When I execute the following code, I get the following in debug window:

print(type(of:     FileManager.default.ubiquityIdentityToken))

print(type(of:     FileManager.default.ubiquityIdentityToken!))

Optional<NSCoding & NSCopying & NSObject> _NSInlineData


Solution

  • The NSKeyedUnarchiver.unarchivedObject(ofClass:from:) method is expecting the decoded object to conform to NSCoding or NSSecureCoding, which are class-only protocols. Data is a struct that does not and could not conform to both, instead, you could use NSData which directly bridges to Data as such:

    let defaultFileManager = FileManager.default
    let standardUserDefaults = UserDefaults.standard
    
    let ubiquityIdentityToken = defaultFileManager.ubiquityIdentityToken
    // archiving and setting user defaults
    do {
        let tokenData = try NSKeyedArchiver.archivedData(withRootObject: ubiquityIdentityToken!,
                                                         requiringSecureCoding: false)
        standardUserDefaults.set(tokenData, forKey: UserDefaultsKeys.ubiquityIdentityToken)
    } catch {
        print(error)
    }
    
    // unarchiving from user defaults
    let oldDataUbiquityIdentityToken = standardUserDefaults.data(forKey: UserDefaultsKeys.ubiquityIdentityToken)!
    do {
        if let oldTokenData = try NSKeyedUnarchiver.unarchivedObject(ofClass: NSData.self,
                                                                     from: oldDataUbiquityIdentityToken) {
            print(ubiquityIdentityToken!.isEqual(to: oldTokenData)) // prints 'true'
            print(oldTokenData) // {length = ..., bytes = ...}
        }
    } catch {
        print(error)
    }