Home > Enterprise >  UserDefaults Decoder Not Decoding
UserDefaults Decoder Not Decoding

Time:12-21

I have the following decoder code. It always executes the catch block. The error I get is as follows:

This decoder will only decode classes that adopt NSSecureCoding

if let dataToRetrieve = UserDefaults.standard.data(forKey: "currentPlayerToon") {
    do {
        let returnedPlayerInformation = try NSKeyedUnarchiver.unarchivedObject(ofClass: PlayerClass.self, from: dataToRetrieve)
        print("Success")
    } catch {
        print(error)
    }
}

Here is my save. This always succeeds.

do {
    let dataToSave = try NSKeyedArchiver.archivedData(withRootObject: cachedToonToSave, requiringSecureCoding: false)
    UserDefaults.standard.set(dataToSave, forKey: "currentPlayerToon")
    print("saving worked")
} catch {
    print("saving failed")
}

In case this is requested, here is my class declaration. It's long:

class PlayerClass: NSObject, NSCoding {
        
    var toonName : String
    var toonLevel : Float
    
    var toonCurrentHealth : Int
    var toonCurrentEnergy : Int
    
    var toonHealthPotionCount : Int
    var toonEnergyPotionCount : Int
    
    var toonRealityShardCount : Int
    var toonGoldCoinCount : Int
    
    var toonPistolAmmoCount : Int
    var toonShotgunAmmoCount : Int
    var toonRifleAmmoCount : Int
    
    
    var toonDamageBoost : Int
    var toonStaminaBoost : Int
    var toonDefensiveBoost : Int
    var toonAwarenessBoost : Int
    
    var toonEquippedWeaponOne : Int
    var toonEquippedWeaponTwo : Int
    var toonEquippedWeaponThree : Int
    
    var toonEquippedArmorOne : Int
    var toonEquippedArmorTwo : Int
    var toonEquippedArmorThree : Int
    var toonEquippedArmorFour : Int
    var toonEquippedArmorFive : Int
    var toonEquippedArmorSix : Int
    
    var toonInventorySlotOne : Int
    var toonInventorySlotTwo : Int
    var toonInventorySlotThree : Int
    var toonInventorySlotFour : Int
    var toonInventorySlotFive : Int
    var toonInventorySlotSix : Int
    var toonInventorySlotSeven : Int
    var toonInventorySlotEight : Int
    var toonInventorySlotNine : Int
    var toonInventorySlotTen : Int
    var toonInventorySlotEleven : Int
    var toonInventorySlotTwelve : Int
    var toonInventorySlotThirteen : Int
    var toonInventorySlotFourteen : Int
    var toonInventorySlotFifteen : Int
    
    var toonAbilityOne : Int
    var toonAbilityTwo : Int
    var toonAbilityThree : Int
    var toonAbilityFour : Int
    
    var abilityOneUseTimestamp : String
    var abilityTwoUseTimestamp : String
    var abilityThreeUseTimestamp : String
    var abilityFourUseTimestamp : String
    
    var lastPromoDate : String
    var lastFreeLife : String
    
    
    init(
         toonName : String,
         toonLevel : Float,
         
         toonCurrentHealth : Int,
         toonCurrentEnergy : Int,
         
         toonHealthPotionCount : Int,
         toonEnergyPotionCount : Int,
         toonRealityShardCount : Int,
         toonGoldCoinCount : Int,
         toonPistolAmmoCount : Int,
         toonShotgunAmmoCount : Int,
         toonRifleAmmoCount : Int,
         
         toonDamageBoost : Int,
         toonStaminaBoost : Int,
         toonDefensiveBoost : Int,
         toonAwarenessBoost : Int,
         
         toonEquippedWeaponOne : Int,
         toonEquippedWeaponTwo : Int,
         toonEquippedWeaponThree : Int,
         
         toonEquippedArmorOne : Int,
         toonEquippedArmorTwo : Int,
         toonEquippedArmorThree : Int,
         toonEquippedArmorFour : Int,
         toonEquippedArmorFive : Int,
         toonEquippedArmorSix : Int,
         toonInventorySlotOne : Int,
         toonInventorySlotTwo : Int,
         toonInventorySlotThree : Int,
         toonInventorySlotFour : Int,
         toonInventorySlotFive : Int,
         toonInventorySlotSix : Int,
         toonInventorySlotSeven : Int,
         toonInventorySlotEight : Int,
         toonInventorySlotNine : Int,
         toonInventorySlotTen : Int,
         toonInventorySlotEleven : Int,
         toonInventorySlotTwelve : Int,
         toonInventorySlotThirteen : Int,
         toonInventorySlotFourteen : Int,
         toonInventorySlotFifteen : Int,
         
         toonAbilityOne : Int,
         toonAbilityTwo : Int,
         toonAbilityThree : Int,
         toonAbilityFour : Int,
         
         abilityOneUseTimestamp : String,
         abilityTwoUseTimestamp : String,
         abilityThreeUseTimestamp : String,
         abilityFourUseTimestamp : String,
         
         lastPromoDate : String,
         lastFreeLife : String) {
        
             self.toonName = toonName
             self.toonLevel = toonLevel
             
             self.toonCurrentHealth = toonCurrentHealth
             self.toonCurrentEnergy = toonCurrentEnergy
             
             self.toonHealthPotionCount = toonHealthPotionCount
             self.toonEnergyPotionCount = toonEnergyPotionCount
             self.toonRealityShardCount = toonRealityShardCount
             self.toonGoldCoinCount = toonGoldCoinCount
             self.toonPistolAmmoCount = toonPistolAmmoCount
             self.toonShotgunAmmoCount = toonShotgunAmmoCount
             self.toonRifleAmmoCount = toonRifleAmmoCount
             
             self.toonDamageBoost = toonDamageBoost
             self.toonStaminaBoost = toonStaminaBoost
             self.toonDefensiveBoost = toonDefensiveBoost
             self.toonAwarenessBoost = toonAwarenessBoost
             
             self.toonEquippedWeaponOne = toonEquippedWeaponOne
             self.toonEquippedWeaponTwo = toonEquippedWeaponTwo
             self.toonEquippedWeaponThree = toonEquippedWeaponThree
             
             self.toonEquippedArmorOne = toonEquippedArmorOne
             self.toonEquippedArmorTwo = toonEquippedArmorTwo
             self.toonEquippedArmorThree = toonEquippedArmorThree
             self.toonEquippedArmorFour = toonEquippedArmorFour
             self.toonEquippedArmorFive = toonEquippedArmorFive
             self.toonEquippedArmorSix = toonEquippedArmorSix
             self.toonInventorySlotOne = toonInventorySlotOne
             self.toonInventorySlotTwo = toonInventorySlotTwo
             self.toonInventorySlotThree = toonInventorySlotThree
             self.toonInventorySlotFour = toonInventorySlotFour
             self.toonInventorySlotFive = toonInventorySlotFive
             self.toonInventorySlotSix = toonInventorySlotSix
             self.toonInventorySlotSeven = toonInventorySlotSeven
             self.toonInventorySlotEight = toonInventorySlotEight
             self.toonInventorySlotNine = toonInventorySlotNine
             self.toonInventorySlotTen = toonInventorySlotTen
             self.toonInventorySlotEleven = toonInventorySlotEleven
             self.toonInventorySlotTwelve = toonInventorySlotTwelve
             self.toonInventorySlotThirteen = toonInventorySlotThirteen
             self.toonInventorySlotFourteen = toonInventorySlotFourteen
             self.toonInventorySlotFifteen = toonInventorySlotFifteen
             
             self.toonAbilityOne = toonAbilityOne
             self.toonAbilityTwo = toonAbilityTwo
             self.toonAbilityThree = toonAbilityThree
             self.toonAbilityFour = toonAbilityFour
             
             self.abilityOneUseTimestamp = abilityOneUseTimestamp
             self.abilityTwoUseTimestamp = abilityTwoUseTimestamp
             self.abilityThreeUseTimestamp = abilityThreeUseTimestamp
             self.abilityFourUseTimestamp = abilityFourUseTimestamp
             
             self.lastPromoDate = lastPromoDate
             self.lastFreeLife = lastFreeLife
             
    }
    
    required init(coder decoder: NSCoder) {
        
        self.toonName = decoder.decodeObject(forKey: "toonName") as? String ?? ""
        self.toonLevel = decoder.decodeFloat(forKey: "toonLevel") 
        
        self.toonCurrentHealth = decoder.decodeInteger(forKey: "toonCurrentHealth")
        self.toonCurrentEnergy = decoder.decodeInteger(forKey: "toonCurrentEnergy")
        
        self.toonHealthPotionCount = decoder.decodeInteger(forKey: "toonHealthPotionCount")
        self.toonEnergyPotionCount = decoder.decodeInteger(forKey: "toonEnergyPotionCount")
        self.toonRealityShardCount = decoder.decodeInteger(forKey: "toonRealityShardCount") 
        self.toonGoldCoinCount = decoder.decodeInteger(forKey: "toonGoldCoinCount") 
        self.toonPistolAmmoCount = decoder.decodeInteger(forKey: "toonPistolAmmoCount") 
        self.toonShotgunAmmoCount = decoder.decodeInteger(forKey: "toonShotgunAmmoCount") 
        self.toonRifleAmmoCount = decoder.decodeInteger(forKey: "toonRifleAmmoCount") 
        
        self.toonDamageBoost = decoder.decodeInteger(forKey: "toonDamageBoost") 
        self.toonStaminaBoost = decoder.decodeInteger(forKey: "toonStaminaBoost") 
        self.toonDefensiveBoost = decoder.decodeInteger(forKey: "toonDefensiveBoost") 
        self.toonAwarenessBoost = decoder.decodeInteger(forKey: "toonAwarenessBoost") 
        
        self.toonEquippedWeaponOne = decoder.decodeInteger(forKey: "toonEquippedWeaponOne")
        self.toonEquippedWeaponTwo = decoder.decodeInteger(forKey: "toonEquippedWeaponTwo")
        self.toonEquippedWeaponThree = decoder.decodeInteger(forKey: "toonEquippedWeaponThree")
        
        self.toonEquippedArmorOne = decoder.decodeInteger(forKey: "toonEquippedArmorOne")
        self.toonEquippedArmorTwo = decoder.decodeInteger(forKey: "toonEquippedArmorTwo")
        self.toonEquippedArmorThree = decoder.decodeInteger(forKey: "toonEquippedArmorThree")
        self.toonEquippedArmorFour = decoder.decodeInteger(forKey: "toonEquippedArmorFour")
        self.toonEquippedArmorFive = decoder.decodeInteger(forKey: "toonEquippedArmorFive")
        self.toonEquippedArmorSix = decoder.decodeInteger(forKey: "toonEquippedArmorSix")
        self.toonInventorySlotOne = decoder.decodeInteger(forKey: "toonInventorySlotOne")
        self.toonInventorySlotTwo = decoder.decodeInteger(forKey: "toonInventorySlotTwo")
        self.toonInventorySlotThree = decoder.decodeInteger(forKey: "toonInventorySlotThree")
        self.toonInventorySlotFour = decoder.decodeInteger(forKey: "toonInventorySlotFour")
        self.toonInventorySlotFive = decoder.decodeInteger(forKey: "toonInventorySlotFive")
        self.toonInventorySlotSix = decoder.decodeInteger(forKey: "toonInventorySlotSix")
        self.toonInventorySlotSeven = decoder.decodeInteger(forKey: "toonInventorySlotSeven")
        self.toonInventorySlotEight = decoder.decodeInteger(forKey: "toonInventorySlotEight")
        self.toonInventorySlotNine = decoder.decodeInteger(forKey: "toonInventorySlotNine")
        self.toonInventorySlotTen = decoder.decodeInteger(forKey: "toonInventorySlotTen")
        self.toonInventorySlotEleven = decoder.decodeInteger(forKey: "toonInventorySlotEleven")
        self.toonInventorySlotTwelve = decoder.decodeInteger(forKey: "toonInventorySlotTwelve")
        self.toonInventorySlotThirteen = decoder.decodeInteger(forKey: "toonInventorySlotThirteen")
        self.toonInventorySlotFourteen = decoder.decodeInteger(forKey: "toonInventorySlotFourteen")
        self.toonInventorySlotFifteen = decoder.decodeInteger(forKey: "toonInventorySlotFifteen")
        
        self.toonAbilityOne = decoder.decodeInteger(forKey: "toonAbilityOne")
        self.toonAbilityTwo = decoder.decodeInteger(forKey: "toonAbilityTwo")
        self.toonAbilityThree = decoder.decodeInteger(forKey: "toonAbilityThree")
        self.toonAbilityFour = decoder.decodeInteger(forKey: "toonAbilityFour")
        
        self.abilityOneUseTimestamp = decoder.decodeObject(forKey: "abilityOneUseTimestamp") as! String
        self.abilityTwoUseTimestamp = decoder.decodeObject(forKey: "abilityTwoUseTimestamp") as! String
        self.abilityThreeUseTimestamp = decoder.decodeObject(forKey: "abilityThreeUseTimestamp") as! String
        self.abilityFourUseTimestamp = decoder.decodeObject(forKey: "abilityFourUseTimestamp") as! String
        
        self.lastPromoDate = decoder.decodeObject(forKey: "lastPromoDate") as? String ?? ""
        self.lastFreeLife = decoder.decodeObject(forKey: "lastFreeLife") as? String ?? ""
    }
    
    func encode(with coder: NSCoder) {
        coder.encode(toonName, forKey: "toonName")
        coder.encode(toonLevel, forKey: "toonLevel")
        coder.encode(toonCurrentHealth, forKey: "toonCurrentHealth")
        coder.encode(toonCurrentEnergy, forKey: "toonCurrentEnergy")
        coder.encode(toonHealthPotionCount, forKey: "toonHealthPotionCount")
        coder.encode(toonEnergyPotionCount, forKey: "toonEnergyPotionCount")
        coder.encode(toonRealityShardCount, forKey: "toonRealityShardCount")
        coder.encode(toonGoldCoinCount, forKey: "toonGoldCoinCount")
        coder.encode(toonPistolAmmoCount, forKey: "toonPistolAmmoCount")
        coder.encode(toonShotgunAmmoCount, forKey: "toonShotgunAmmoCount")
        coder.encode(toonRifleAmmoCount, forKey: "toonRifleAmmoCount")
        coder.encode(toonDamageBoost, forKey: "toonDamageBoost")
        coder.encode(toonStaminaBoost, forKey: "toonStaminaBoost")
        coder.encode(toonDefensiveBoost, forKey: "toonDefensiveBoost")
        coder.encode(toonAwarenessBoost, forKey: "toonAwarenessBoost")
        coder.encode(toonEquippedWeaponOne, forKey: "toonEquippedWeaponOne")
        coder.encode(toonEquippedWeaponTwo, forKey: "toonEquippedWeaponTwo")
        coder.encode(toonEquippedWeaponThree, forKey: "toonEquippedWeaponThree")
        coder.encode(toonEquippedArmorOne, forKey: "toonEquippedArmorOne")
        coder.encode(toonEquippedArmorTwo, forKey: "toonEquippedArmorTwo")
        coder.encode(toonEquippedArmorThree, forKey: "toonEquippedArmorThree")
        coder.encode(toonEquippedArmorFour, forKey: "toonEquippedArmorFour")
        coder.encode(toonEquippedArmorFive, forKey: "toonEquippedArmorFive")
        coder.encode(toonEquippedArmorSix, forKey: "toonEquippedArmorSix")
        coder.encode(toonInventorySlotOne, forKey: "toonInventorySlotOne")
        coder.encode(toonInventorySlotTwo, forKey: "toonInventorySlotTwo")
        coder.encode(toonInventorySlotThree, forKey: "toonInventorySlotThree")
        coder.encode(toonInventorySlotFour, forKey: "toonInventorySlotFour")
        coder.encode(toonInventorySlotFive, forKey: "toonInventorySlotFive")
        coder.encode(toonInventorySlotSix, forKey: "toonInventorySlotSix")
        coder.encode(toonInventorySlotSeven, forKey: "toonInventorySlotSeven")
        coder.encode(toonInventorySlotEight, forKey: "toonInventorySlotEight")
        coder.encode(toonInventorySlotNine, forKey: "toonInventorySlotNine")
        coder.encode(toonInventorySlotTen, forKey: "toonInventorySlotTen")
        coder.encode(toonInventorySlotEleven, forKey: "toonInventorySlotEleven")
        coder.encode(toonInventorySlotTwelve, forKey: "toonInventorySlotTwelve")
        coder.encode(toonInventorySlotThirteen, forKey: "toonInventorySlotThirteen")
        coder.encode(toonInventorySlotFourteen, forKey: "toonInventorySlotFourteen")
        coder.encode(toonInventorySlotFifteen, forKey: "toonInventorySlotFifteen")
        coder.encode(toonAbilityOne, forKey: "toonAbilityOne")
        coder.encode(toonAbilityTwo, forKey: "toonAbilityTwo")
        coder.encode(toonAbilityThree, forKey: "toonAbilityThree")
        coder.encode(toonAbilityFour, forKey: "toonAbilityFour")
        coder.encode(abilityOneUseTimestamp, forKey: "abilityOneUseTimestamp")
        coder.encode(abilityTwoUseTimestamp, forKey: "abilityTwoUseTimestamp")
        coder.encode(abilityThreeUseTimestamp, forKey: "abilityThreeUseTimestamp")
        coder.encode(abilityFourUseTimestamp, forKey: "abilityFourUseTimestamp")
        coder.encode(lastPromoDate, forKey: "lastPromoDate")
        coder.encode(lastFreeLife, forKey: "lastFreeLife")
    }
}

CodePudding user response:

Apple wants everyone to use NSSecureCoding instead of just NSCoding. You need to change your class to conform to NSSecureCoding. Then you pass true to the requiringSecureCoding parameter when you create the archive.

// Change NSCoding to NSSecureCoding
class PlayerClass: NSObject, NSSecureCoding {
    // The following is needed as well
    static var supportsSecureCoding: Bool { return true }

    var toonName : String
    var toonLevel : Float

    // and the rest as you already have it

and:

let dataToSave = try NSKeyedArchiver.archivedData(withRootObject: cachedToonToSave, requiringSecureCoding: true)

With just those changes your code should work.

You will need to resave the updated data in user defaults before you can load it back in.

CodePudding user response:

In addition to the good advice using NSSecureCoding, you could try this example code with unarchiveTopLevelObjectWithData, works for me:

if let dataToRetrieve = UserDefaults.standard.data(forKey: "currentPlayerToon") {
     do {
         // --- here
         if let returnedPlayerInformation = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(dataToRetrieve) as? PlayerClass { 
             print("---> YES DECODED")
         } else {
             print("---> NOT DECODED")
         }
     } catch {
         print("---> catching error")
     }
 }
 
  • Related