3

I'm attempting use NSCoding protocol to read and write data to plist. I get an exception when I try to write the [GolfHoles] which is a subclass of NSObject. I've read several posts with different approaches, but none have helped.

class GolfCourse: NSObject, NSCoding {
var name: String = ""
var location: String = ""
var holes: [GolfHole] = [GolfHole]()

init(holes: [GolfHole]) {
    self.holes = holes
}

// MARK: NSCoding Protocol
func encodeWithCoder(aCoder: NSCoder) {
    aCoder.encodeObject(name, forKey: "name")
    aCoder.encodeObject(location, forKey: "location")
    aCoder.encodeObject(holes, forKey: "holes") // exception here

}

required init(coder aDecoder: NSCoder) {
    super.init()
    name = aDecoder.decodeObjectForKey("name") as! String
    location = aDecoder.decodeObjectForKey("location") as! String
    holes = aDecoder.decodeObjectForKey("holes") as! [GolfHole]

}

override init() {
    super.init()
    for var i=0; i<18; i++ {
        let newHole = GolfHole()
        self.holes.append(newHole)

    }
}

}

How do I write and read the array?

user2966301
  • 101
  • 1
  • 1
  • 4
  • 2
    Couldn't decide whether to vote close as a dupe or for not providing the exception description. Either way it's a close. – Hot Licks Sep 12 '15 at 18:49
  • maybe vote for close for not reading the ios tag wiki :-o ... it says "Please follow the article [My App crashed. Now what?](http://www.raywenderlich.com/10209/my-app-crashed-now-what-part-1) by Ray Wenderlich, before posting any questions relating to app crashes. It explains how to properly debug an iOS-App. It's pointless to ask questions relating to crashes when you don't have a *proper* backtrace and exception message." – Michael Sep 12 '15 at 18:54
  • 2
    Does your `GolfHole` class conform to the `NSCoding` protocol? It needs to implement the two protocol methods much like your `GolfCourse` class does. – rmaddy Sep 12 '15 at 19:06
  • To see the actual statement that is causing the error add an exception breakpoint: 1. From the Main Menu Debug:Breakpoints:Create Exception Breakpoint. 2. Right-click the breakpoint and set the exception to Objective-C. 3. Add an action: "po $arg1". Run the app to get the breakpoint and you will be at the line that causes the exception and the error message will be in the debugger console. [Breakpoint example:](http://i.stack.imgur.com/7QB0L.png) – zaph Sep 12 '15 at 19:14
  • Thank you rmaddy. I failed to make GolfHole class conform to NSCoding protocol. – user2966301 Sep 12 '15 at 20:28
  • The question is not duplicate of https://stackoverflow.com/questions/25853947/how-can-i-debug-unrecognized-selector-sent-to-instance-error as later is about how to debug genaral unrecognized selector exception. And this one is about NSCoding implementation, Though it has no stack trace or exception message, it already has good answer. – Vladimir Oct 30 '20 at 18:28

1 Answers1

9

rmaddy is right. You need to have all of your classes that will be saved to also conform to NSCoding. So here is a trivial example of a GolfHole class and how to serialize the GolfCourse object.

class GolfHole: NSObject, NSCoding {
  let number: Int
  let par: Int

  init(number: Int, par: Int) {
    self.number = number
    self.par = par
  }

  convenience required init?(coder aDecoder: NSCoder) {
    guard
      let number = aDecoder.decodeObjectForKey("number") as? Int,
      let par    = aDecoder.decodeObjectForKey("par")    as? Int
    else {
      return nil
    }
    self.init(number: number, par: par)
  }

  func encodeWithCoder(aCoder: NSCoder) {
    aCoder.encodeObject(number, forKey: "number")
    aCoder.encodeObject(par,    forKey: "par")
  }
}


class GolfCourse: NSObject, NSCoding {
  var name = ""
  var location = ""
  var holes = [GolfHole]()

  init(name: String, location: String, holes: [GolfHole]) {
    self.name = name
    self.location = location
    self.holes = holes
  }

  convenience required init?(coder aDecoder: NSCoder) {
    guard
      let name     = aDecoder.decodeObjectForKey("name")     as? String,
      let location = aDecoder.decodeObjectForKey("location") as? String,
      let holes    = aDecoder.decodeObjectForKey("holes")    as? [GolfHole]
    else {
      return nil
    }
    self.init(name: name, location: location, holes: holes)
  }

  func encodeWithCoder(aCoder: NSCoder) {
    aCoder.encodeObject(name,     forKey: "name")
    aCoder.encodeObject(location, forKey: "location")
    aCoder.encodeObject(holes,    forKey: "holes")
  }
}

enter image description here

Price Ringo
  • 3,424
  • 1
  • 19
  • 33