14

I have a instance variable name in String

var name: String

My class implements the NSCoding protocol. So for name I had

func encodeWithCoder(aCoder: NSCoder) {
    aCoder.encodeObject(self.name, forKey: kName)
}

required init(coder aDecoder: NSCoder) {
    self.name = aDecoder.decodeObjectForKey(kName) as String  // CRASH HERE
}

Result? I was getting a run time crash during initiation with decoder. I changed init to this:

var temp = aDecoder.decodeObjectForKey(kName) as NSString!
self.name = aDecoder.decodeObjectForKey(kName) as String

and realised the value temp is holding the right NSString value. so I thought the line below is going to fix it but it issues a linker error:

self.name = aDecoder.decodeObjectForKey(kName) as NSString!

the questions is how to take the temp and put it into name?

Congruent Tech. UG
  • 1,408
  • 2
  • 12
  • 21

2 Answers2

4

decodeObjectForKey returns an optional AnyObject?, so you have to guard your code against possible nil values. Using a cast to a forced unwrapped doesn't sound safe.

I am unable to reproduce the error (so the problem could be somewhere else in your code), but this is how I would approach when initializing the name property:

    var tempName = aDecoder.decodeObjectForKey("name") as? String
    if let tempName = tempName {
        self.name = tempName
    } else {
        self.name = "some initial value"
    }

Note the usage of the optional downcasting as?, which always produce a result (nil or a valid type value) as opposed to as NSString!, which triggers an exception if the downcast is not possible (for instance if you are expecting a string, but it's an int instead).

That code would also allow you to better debugging - if tempName is nil, then either the key doesn't exist or the corresponding value is of a different type.

Antonio
  • 71,651
  • 11
  • 148
  • 165
  • 5
    You can replace your if/else logic with an expression using Swift's nil-coalescing operator as follows: `self.name = tempName ?? "some initial value"` – jlehr Sep 20 '14 at 16:13
  • Yeah good advice. I didn't use it just for clarity, but I didn't even mention :) – Antonio Sep 20 '14 at 16:41
  • thanks for the answer. but the problem seem to be to cast from NSString! to String. – Congruent Tech. UG Sep 21 '14 at 06:12
  • thanks for the answer. The problem is that the decodeObjectForKey seem to return an NSString that is never cartable to String. So the code you suggested never assigns a value to name property. – Congruent Tech. UG Sep 21 '14 at 06:22
  • Never heard of a `NSString` that cannot be cast to `String`. Try saving what `decodeObjectForKey` returns to a `AnyObject?` variable, then assign to `tempName` via downcast, and use the debugger to step through the lines and see what's going on - hope that can help you figuring out – Antonio Sep 21 '14 at 06:37
0

There were two things I needed to do to get it working:

  1. clean build with removing the derived data folder
  2. For some reason bridging between the NSString and String is not working in this situation. so the casting should be done in two stages

    if let name = aDecoder.decodeObjectForKey(kName) as? NSString {
        self.name = name as String
    } else {
        assert(false, "ERROR: could not decode")
        self.name = "ERROR"
    }
    
Congruent Tech. UG
  • 1,408
  • 2
  • 12
  • 21
  • assert halts execution, last line will never be called, but what did you need it for? – HenryRootTwo Oct 16 '14 at 19:28
  • 2
    assert only halts execution in debug builds - it's the same semantics as the C version. See the very cool blog post about how they built this https://developer.apple.com/swift/blog/?id=4 – Andy Dent Jun 15 '15 at 03:45