How to solve deprecation of unarchiveObject(withFile:)
Asked Answered
U

5

22

With iOS 12.1, unarchiveObject(withFile:) was deprecated.
How can you convert NSKeyedUnarchiver.unarchiveObject(withFile: String) to use a call to NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data: Data), or NSKeyedUnarchiver.unarchivedObject(ofClasses: [AnyClass], from: Data), or NSKeyedUnarchiver.unarchivedObject(ofClass: NSCoding.Protocol, from: Data)?

I'm guessing you have to have something like let fileData = try Data(contentsOf: URL) and then use one of those methods to unarchive the data. But, I cannot figure it out and the documentation accompanying the depreciation is not helpful (at least to me).

The archived data is rather simple -- just an array of strings (an array of class NameToBeSaved as defined by this code):

class NameToBeSaved: NSObject, NSCoding {
var name: String

init(userEnteredName: String) {
    self.name = userEnteredName
    super.init()
}

func encode(with aCoder: NSCoder) {
    aCoder.encode(name, forKey: "name")
}

required init?(coder aDecoder: NSCoder) {
    name = aDecoder.decodeObject(forKey: "name") as! String
    super.init()
}

Here is the code calling unarchiveObject(withFile:) -

init() {
    if let archivedCategoryNames = NSKeyedUnarchiver.unarchiveObject(withFile: categoryNameArchiveURL.path) as? [NameToBeSaved] {
        allCategories += archivedCategoryNames
    } else {
        for category in starterCategories {
            let thisNewCategory = NameToBeSaved(userEnteredName: category)
            createNewCategory(thisNewCategory)
        }
        sortCategories()
    }
}
Unspent answered 1/11, 2018 at 8:3 Comment(3)
This is not an answer: but the Data You Can Trust WWDC 2018 session talk about the new APIs and how their difference from the old and now-deprecated APIs.Photojournalism
The code in your question is encoding or decoding individual keys. Can you also show the code where you currently calling unarchiveObject(withFile:)?Photojournalism
@DavidRönnqvist I've updated the post to include the code calling unarchiveObject(withFile:). I'll also look at the link you provided. Thanks for the assistance.Unspent
U
8

I don't know if this is the best solution, but this solved the conversion for me (old code commented out for comparison):

    init() {

    do {
        let rawdata = try Data(contentsOf: categoryNameArchiveURL)
        if let archivedCategoryNames = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(rawdata) as! [NameToBeSaved]? {
            allCategories += archivedCategoryNames
        }
    } catch {
        print("Couldn't read file")
        for category in starterCategories {
            let thisNewCategory = NameToBeSaved(userEnteredName: category)
            createNewCategory(thisNewCategory)
        }
        sortCategories()
    }

/*        if let archivedCategoryNames = NSKeyedUnarchiver.unarchiveObject(withFile: categoryNameArchiveURL.path) as? [NameToBeSaved] {
            allCategories += archivedCategoryNames
        } else {
            for category in starterCategories {
                let thisNewCategory = NameToBeSaved(userEnteredName: category)
                createNewCategory(thisNewCategory)
            }
            sortCategories()
        }
 */
}
Unspent answered 1/11, 2018 at 18:24 Comment(0)
V
6

Swift 5, drop prefix 'NS' and concentrates usage for future changes...

class KeyedUnarchiver : NSKeyedUnarchiver {
    open override class func unarchiveObject(with data: Data) -> Any? {
        do {
            let object = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSObject.self], from: data)
            return object
        }
        catch let error {
            Swift.print("unarchiveObject(with:) \(error.localizedDescription)")
            return nil
        }
    }

    open override class func unarchiveObject(withFile path: String) -> Any? {
        do {
            let data = try Data(contentsOf: URL.init(fileURLWithPath: path))
            let object = try unarchivedObject(ofClasses: [NSObject.self], from: data)
            return object
        }
        catch let error {
            Swift.print("unarchiveObject(withFile:) \(error.localizedDescription)")
            return nil
        }
    }
}
Vorous answered 18/4, 2020 at 19:55 Comment(0)
B
5

Problem: unarchive an sks file for use as an SKEmitterNode.

OLD METHOD, DEPRECATED:

let filePath = Bundle.main.path(forResource: "myParticleEmitter", ofType: "sks")!
let burnerPathUnarchived = NSKeyedUnarchiver.unarchiveObject(withFile: burnerPath) as! SKEmitterNode

NEW METHOD:

do {
    let fileURL = Bundle.main.url(forResource: "myParticleEmitter", withExtension: "sks")!
    let fileData = try Data(contentsOf: fileURL)
    let unarchivedData = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(burnerData) as! SKEmitterNode
} catch {
    print("didn't work")
}

Then you can do:

mySKEffectNode.addChild(unarchivedData)
mySKSpriteNode.addChild(mySKEffectNode)
Biddable answered 20/7, 2019 at 18:27 Comment(1)
unarchiveTopLevelObjectWithData is now deprecated too. Use unarchivedObjectOfClass.Cultrate
C
2

As suggested by Apple, we should use FileManager for read/write the archived file.

func archiveURL() -> URL? {
    guard let documentURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first 
        else { return nil }

    return documentURL.appendingPathComponent("MyArchive.data")
}

func archive(customObject: CustomObject) {
    guard let dataToBeArchived = try? NSKeyedArchiver.archivedData(withRootObject: customObject, requiringSecureCoding: true), 
        let archiveURL = archiveURL() 
        else  {
        return
    }

    try? dataToBeArchived.write(to: archiveURL)
}

func unarchive() -> CustomObject? {
    guard let archiveURL = archiveURL(),
        let archivedData = try? Data(contentsOf: archiveURL),
        let customObject = (try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(archivedData)) as? CustomObject 
        else {
        return nil
    }

    return customObject
}
Coaly answered 15/1, 2020 at 10:32 Comment(0)
T
1

I had the same issue as Saam Barrager but the suggested replacment was unarchivedObjectOfClass (see comment by Mr Zystem), which worked out like this:

if let epath = Bundle.main.url(forResource: "ParticleImpact", withExtension: "sks") {
  do {
    let data = try Data(contentsOf: epath)
    impactEmitter = try NSKeyedUnarchiver.unarchivedObject(ofClass: SKEmitterNode.self, from: data)
    // ... do something with impactEmitter ...
  } catch {
    // Tough beans
  }
}
Tableware answered 15/3, 2022 at 20:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.