Reputation: 428
Here is my code for a Game object. It has plenty of different properties, one of which is an array of Cards. When I try to use NSCoding to save my game objects, XCode throws this error
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Hearthstone_Tracker.Card encodeWithCoder:]: unrecognized selector sent to instance 0x7fbca9db9750'
From what I've read, these errors happen oftentimes when an UIButton calls a function and passes itself as an argument when the function doesn't expect it or vice versa. I don't see how it's applicable to my case though.
I've read NSCoding has some problem with structs, but as Cards is actually a class, I don't think the problem lies there either.
Here is my Game class:
import Foundation
import UIKit
class Game: NSObject, NSCoding {
// MARK: Properties
let playerHero: String
let playerDeck: String
let playerImage: UIImage?
let opponentHero: String
let opponentDeck: String
let opponentImage: UIImage?
let result: String
let coin: Bool
var date: String?
let mode: String
let rank: String?
let duration: Int?
let durationSec: Int?
let id: Int
let cardsPlayed: [Card]
var manaEfficiency: (Double, Double)?
var turnCount: Int?
var estimatedTurnLength: Int?
var finisher: String?
// MARK: Keys for the data
struct PropertyKey {
static let playerHero = "playerHero"
static let playerDeck = "playerDeck"
static let playerImage = "playerImage"
static let opponentHero = "opponentHero"
static let opponentDeck = "opponentDeck"
static let opponentImage = "opponntImage"
static let result = "result"
static let coin = "coin"
static let date = "date"
static let mode = "mode"
static let rank = "rank"
static let duration = "duration"
static let durationSec = "durationSec"
static let id = "id"
static let cardsPlayed = "cardsPlayed"
}
// MARK: Archiving Paths
static let DocumentsDirectory: AnyObject = NSFileManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
static let ArchiveURL = DocumentsDirectory.URLByAppendingPathComponent("games")
// MARK: Initializer
init(playerHero: String, playerDeck: String, opponentHero: String, opponentDeck: String, result: String, coin: Bool, date: String, mode: String, rank: String, duration: Int?, durationSec: Int?, id: Int, cardsPlayed: [Card]) {
// ...
// Some initializer code
// ...
self.cardsPlayed = cardsPlayed
super.init()
}
// MARK: NSCoding
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(playerHero, forKey: PropertyKey.playerHero)
aCoder.encodeObject(playerDeck, forKey: PropertyKey.playerDeck)
aCoder.encodeObject(opponentHero, forKey: PropertyKey.opponentHero)
aCoder.encodeObject(opponentDeck, forKey: PropertyKey.opponentDeck)
aCoder.encodeObject(result, forKey: PropertyKey.result)
aCoder.encodeBool(coin, forKey: PropertyKey.coin)
aCoder.encodeObject(date, forKey: PropertyKey.date)
aCoder.encodeObject(mode, forKey: PropertyKey.mode)
aCoder.encodeObject(rank, forKey: PropertyKey.rank)
aCoder.encodeObject(duration, forKey: PropertyKey.duration)
aCoder.encodeObject(durationSec, forKey: PropertyKey.durationSec)
aCoder.encodeObject(id, forKey: PropertyKey.id)
aCoder.encodeObject(cardsPlayed, forKey: PropertyKey.cardsPlayed)
}
required convenience init?(coder aDecoder: NSCoder) {
let playerHero = aDecoder.decodeObjectForKey(PropertyKey.playerHero) as! String
let playerDeck = aDecoder.decodeObjectForKey(PropertyKey.playerDeck) as! String
let opponentHero = aDecoder.decodeObjectForKey(PropertyKey.opponentHero) as! String
let opponentDeck = aDecoder.decodeObjectForKey(PropertyKey.opponentDeck) as! String
let result = aDecoder.decodeObjectForKey(PropertyKey.result) as! String
let coin = aDecoder.decodeBoolForKey(PropertyKey.coin)
let date = aDecoder.decodeObjectForKey(PropertyKey.date) as! String
let mode = aDecoder.decodeObjectForKey(PropertyKey.mode) as! String
let rank = aDecoder.decodeObjectForKey(PropertyKey.rank) as! String
let duration = aDecoder.decodeObjectForKey(PropertyKey.duration) as? Int
let durationSec = aDecoder.decodeObjectForKey(PropertyKey.durationSec) as? Int
let id = aDecoder.decodeObjectForKey(PropertyKey.id) as! Int
let cardsPlayed = aDecoder.decodeObjectForKey(PropertyKey.cardsPlayed) as! [Card]
self.init(playerHero: playerHero, playerDeck: playerDeck, opponentHero: opponentHero, opponentDeck: opponentDeck, result: result, coin: coin, date: date, mode: mode, rank: rank, duration: duration, durationSec: durationSec, id: id, cardsPlayed: cardsPlayed)
}
And here is Card class:
import Foundation
import UIKit
class Card: NSObject {
// MARK: Properties
let name: String
let manacost: Int
let turn: Int
let player: String
// MARK: Initialzer
init(name: String, manacost: Int, turn: Int, player: String) {
self.name = name
self.manacost = manacost
self.turn = turn
self.player = player
super.init()
}
}
Upvotes: 0
Views: 471
Reputation: 6824
Every object (and its internal hierarchy of subobjects) to be able to be stored in NSDefaults
must be able to serialise itself ...that is.. must conform to NSCoding
.
Upvotes: 2