cast NSString! to String in swift
Asked Answered
R

2

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?

Rondarondeau answered 20/9, 2014 at 8:14 Comment(2)
Try: self.name = aDecoder.decodeObjectForKey(kName) as AnyObject! as StringEduard
Thanks for the answer but it still crashes.Rondarondeau
G
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.

Garibay answered 20/9, 2014 at 8:40 Comment(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"Clari
Yeah good advice. I didn't use it just for clarity, but I didn't even mention :)Garibay
thanks for the answer. but the problem seem to be to cast from NSString! to String.Rondarondeau
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.Rondarondeau
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 outGaribay
R
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"
    }
    
Rondarondeau answered 21/9, 2014 at 7:46 Comment(2)
assert halts execution, last line will never be called, but what did you need it for?Bethanie
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 developer.apple.com/swift/blog/?id=4Zincate

© 2022 - 2024 — McMap. All rights reserved.