iosswiftnskeyedarchiveruserdefaultsopaque-pointers

Storing OpaquePointer type in UserDefaults


I am working on Xcode 12 and Swift 5 environment to build an iOS application.

I need to store an OpaquePointer type variable("self.loggers" in the code below) before the view disappears(or before the app closes) and retrieve it when the view appears( when the app runs). I tried to use UserDefault as below,

// Storing(In viewWillDisappear)
do {
            encodedData = try NSKeyedArchiver.archivedData(withRootObject: self.loggers, requiringSecureCoding: false)
            UserDefaults.standard.set(encodedData, forKey: "accelLoggerID")
        } catch {
            print("archiving error")
        }
...

// Retrieving(In viewDidLoad)
if let decodedData = UserDefaults.standard.object(forKey: "acceleration") as? Data {
            do {
                self.loggers = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(decodedData) as? [String:OpaquePointer] ?? [:]} catch {
                    print("unarchiving error")
                }
        } else {
            self.loggers = [:]
            print("No value in Userdefault. [viewDidLoad]")
        }

However, NSKeyedArchiver failed to encode such type. After that, I made a class that wraps the variable.

class LoggerWrapper {
    var loggers: [String : OpaquePointer]
    init(loggerDic: [String : OpaquePointer]) {
        loggers = loggerDic
    }
}

And changed my code like

self.loggers = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(decodedData) as? LoggerWrapper ?? LoggerWrapper(loggerDic: [:])} catch {
                    print("unarchiving error")
                }

However, this gives SIGABRT when NSKeyedArchiver.archivedData is called. Is there any way to store Opaquepointer type in UserDefaults? If not, can using core data solve this problem?

Thank you.


Solution

  • You probably need to convert the pointers to Int bitPatterns. There is an initializer on Int for that: let intBitPattern = Int(bitPattern: opaquePointer). There is also an initializer on OpaquePointer that takes an Int bitPattern: let opaquePointer = OpaquePointer(bitPattern: intBitPattern). So you would need to convert the values in your dictionary (e.g. using compactMapValues) before storing and after reading it.

    With a Dictionary<String, Int>, you should be able to store it directly in UserDefaults, without the need for NSKeyedArchiver and NSKeyedUnarchiver.


    Sidenote: Pointers aren't "long-lived". So storing it in UserDefaults is only valid during the lifetime of your app. Once your app restarts, the pointers might no longer be valid.