IOS editor bug. archivedData renamed
Asked Answered
L

4

17

Please help me! I am stuck in a loop and can't find my way out. I am trying to learn IOS programming for work so I thought I would start with their tutorial app the Meal list application. I am at the part where you are supposed to start saving persistent data and now the editor has me stuck in a never ending loop. I have a line of code...

let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(meals, toFile: Meal.ArchiveURL.path)

That gives me a warning that says...

'archiveRootObject(_:toFile:)' was deprecated in iOS 12.0: Use +archivedDataWithRootObject:requiringSecureCoding:error: instead

OK, so I change the line of code to...

let isSuccessfulSave = NSKeyedArchiver.archivedDataWithRootObject(meals)

Which then gives me the warning...

'archivedDataWithRootObject' has been renamed to 'archivedData(withRootObject:)'

OK, so I change the line of code to...

let isSuccessfulSave = NSKeyedArchiver.archivedData(withRootObject: meals)

Which tells me...

'archivedData(withRootObject:)' was deprecated in iOS 12.0: Use +archivedDataWithRootObject:requiringSecureCoding:error: instead

OK... So... archivedData was deprecated and I have to use archivedDataWithRootObject, but using archivedDataWithRootObject has been renamed to archivedData, but archivedData is deprecated so use archivedDataWithRootObject which is renamed to archivedData which is deprecated... ad infinitum.

I have tried looking on the developer docs but they just tell me the same thing, one is deprecated, with no links or anything and searching google just gives me a bunch of pages showing me the syntax of using any of them. I am still really new to IOS programming and have no idea how to get out of this endless loop of deprecated to renamed to deprecated to...

Please help, I am lost and not sure how to continue. Thank you.

Leifleifer answered 17/11, 2018 at 1:37 Comment(5)
Another question to ask is why use NSKeyedArchiver at all in Swift? Why not use modern Swift APIs based around Codable?Blain
The 2 above comments are literally trash , first deprecation should be avoided anyway in code , second the answer is very related to the zone of the questionTerminable
@matt So you're suggestion for a completely new programmer to a language is to ignore warning and don't ask questions? Thank you, that's helpful. I DON'T know IOS programming.Leifleifer
What I’m saying is don’t call it a bug and don’t vent. Just ask.Colorcast
I understand and I apologize, I just didn't know how else to describe it. The editor is of no help since it just sends me in a loop and the docs are extremely non helpful to a completely new IOS developer. I didn't quite understand the whole :requireingSecureCoding:error: piece of the whole thing, so it just looks like a bug to a complete newbie, where you get stuck in a loop.Leifleifer
D
25

I am following the same example you are trying to do, and I figured out how to update the methods for storing and retrieving values in iOS 12, this should help you:

//MARK: Private Methods
private func saveMeals() {

    let fullPath = getDocumentsDirectory().appendingPathComponent("meals")

    do {
        let data = try NSKeyedArchiver.archivedData(withRootObject: meals, requiringSecureCoding: false)
        try data.write(to: fullPath)
        os_log("Meals successfully saved.", log: OSLog.default, type: .debug)
    } catch {
        os_log("Failed to save meals...", log: OSLog.default, type: .error)
    }
}

func getDocumentsDirectory() -> URL {
    let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
    return paths[0]
}

private func loadMeals() -> [Meal]? {
    let fullPath = getDocumentsDirectory().appendingPathComponent("meals")
    if let nsData = NSData(contentsOf: fullPath) {
        do {

            let data = Data(referencing:nsData)

            if let loadedMeals = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? Array<Meal> {
                return loadedMeals
            }
        } catch {
            print("Couldn't read file.")
            return nil
        }
    }
    return nil
}

Also you will find that you need to update ViewDidLoad as this:

override func viewDidLoad() {
    super.viewDidLoad()

    // Use the edit button item provided by the table view controller.
    navigationItem.leftBarButtonItem = editButtonItem

    let savedMeals = loadMeals()

    if savedMeals?.count ?? 0 > 0 {
        meals = savedMeals ?? [Meal]()
    } else {
        loadSampleMeals()
    }

}

I hope this helps, for me the app is now working, storing and retrieving data.

FYI: This doesn't work with Xcode 11 beta and iOS 13 is should work with anything before those versions.

Defaulter answered 25/1, 2019 at 19:58 Comment(0)
O
5

A general solution for iOS 12 would be:

class SettingsArchiver {
    static func setData(_ value: Any, key: String) {
        let ud = UserDefaults.standard
        let archivedPool = try? NSKeyedArchiver.archivedData(withRootObject: value, requiringSecureCoding: true)
        ud.set(archivedPool, forKey: key)
    }

    static func getData<T>(key: String) -> T? {
        let ud = UserDefaults.standard
        if let val = ud.value(forKey: key) as? Data,
            let obj = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(val) as? T {
            return obj
        }
        return nil
    }
}
Oops answered 1/9, 2019 at 3:59 Comment(0)
T
3

You need

try {
   let data = try NSKeyedArchiver.archivedData(withRootObject:meals,requiringSecureCoding:true)
   try data.write(to:fullPath)
}
catch {
   print(error)
}

Here in Docs it's IOS 11+

Terminable answered 17/11, 2018 at 1:41 Comment(4)
What about the "toFile" part of the original call that is in their tutorial app? Does it all go to the same place now? I had to add static members to my Meal class of... static let DocumentsDirectory = FileManager().urls(for: .documentDirecory, in: .userDomainMask).first! static let ArchiveURL = DocumentsDirectory.appendingPathComponent("meals")Leifleifer
yes but it's deprecated developer.apple.com/documentation/foundation/nskeyedarchiver/… , you need to use the secure one in answer and write the returned data to the fileTerminable
So I still have to write the data to a file after I run the archivedData function? I have no idea how to do that. Their tutorial is obviously outdated and tells you to use deprecated methods that are supposed to write to file automaticallyLeifleifer
Check my answer buddy.Snap
S
1

I would say, the answer directly addressing your question is to use the ArchiveURL defined in your Meal.swift data model (think MVC pattern) and reimplement the saveMeals() function in your MealTableViewController.swift controller using the recommended replacement to the deprecated archiveRootObject method this way:

private func saveMeals(){
    do {
        let data = try NSKeyedArchiver.archivedData(withRootObject: meals, requiringSecureCoding: true)
        try data.write(to: Meal.ArchiveURL)
    }
    catch {
        print("Couldn't save to file")
    }
}

Although this answer is a little late :-) I hope it helps whomever may come across this issue.

Snap answered 21/7, 2020 at 0:36 Comment(1)
Also, you will have to add 'NSSecureCoding' to your Meal class declaration and also the following varible declaration: 'static var supportsSecureCoding: Bool = true'Snap

© 2022 - 2024 — McMap. All rights reserved.