Julien
Julien

Reputation: 91

Decoding array of URL objects using NSKeyedUnarchiver

I'm trying to decode an array of URL objects using NSKeyedUnarchiver. Here is the code:

let urlArray: [URL] = [URL(string: "https://apple.com")!,
                       URL(string: "https://google.com")!]

do {
    let archivedUrls = try NSKeyedArchiver.archivedData(withRootObject: urlArray, requiringSecureCoding: false)
    let _ = try NSKeyedUnarchiver.unarchivedObject(ofClass: NSArray.self, from: archivedUrls)
} catch {
    print(error)
}

I get the following error:

Error Domain=NSCocoaErrorDomain Code=4864 "value for key 'NS.objects' was of unexpected class 'NSURL'. Allowed classes are '{(
    NSArray
)}'." UserInfo={NSDebugDescription=value for key 'NS.objects' was of unexpected class 'NSURL'. Allowed classes are '{(
    NSArray
)}'.}

If I replace let _ = try NSKeyedUnarchiver.unarchivedObject(ofClass: NSArray.self, from: archivedUrls) by let _ = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self, NSURL.self], from: archivedUrls), then it works. But that means it can decode either an NSArray or NSURL object, not an NSArray containing NSURL objects.

If I change the array to be an array of String instead, everything works fine:

let stringArray: [String] = ["string", "string2"]

do {
    let archivedStrings = try NSKeyedArchiver.archivedData(withRootObject: stringArray, requiringSecureCoding: false)
    let _ = try NSKeyedUnarchiver.unarchivedObject(ofClass: NSArray.self, from: archivedStrings)
} catch {
    print(error)
}

Does anyone have an explanation for that behaviour?

Upvotes: 2

Views: 556

Answers (1)

OOPer
OOPer

Reputation: 47896

If you do not require Secure Coding (requiringSecureCoding: false), you can use unarchiveTopLevelObjectWithData(_:).

do {
    let archivedUrls = try NSKeyedArchiver.archivedData(withRootObject: urlArray, requiringSecureCoding: false)
    if let urls = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(archivedUrls) as? [URL] {
        print(urls)
    } else {
        print("not URLs")
    }
} catch {
    print(error)
}

Or you can specify the types included in the archive using unarchivedObject(ofClasses:from:).

do {
    let archivedUrls = try NSKeyedArchiver.archivedData(withRootObject: urlArray, requiringSecureCoding: true)
    if let urls = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self, NSURL.self], from: archivedUrls) as? [URL] {
        print(urls)
    } else {
        print("not URLs")
    }
} catch {
    print(error)
}

NSString seems to be an exception for this rule.

Upvotes: 1

Related Questions