Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

swift optional chaining with cast

Tags:

ios

swift

The code below highlights a problem I'm having combining optional chaining and casts with Apple's swift language

import Foundation
import CoreData

class MyExample {

    var detailItem: NSManagedObject?

    func example() {
        //In the actual implementation it is assigned to a UITableViewCell textLabel.text with the same result.
        let name: String = self.detailItem?.valueForKey("name") as String
    }
}

The above results in:

'AnyObject' is not convertible to 'String'

I am able to do this by making the class variable an implicitly unwrapped optional as follows:

class MyExample2 {

    var detailItem: NSManagedObject!

    func example() {
        let name: String = self.detailItem.valueForKey("name") as String
    }
}

This works, but now this variable doesn't reflect my real world need for it to be optional

This next example also works with detailItem as optional:

class MyExample3 {

    var detailItem: NSManagedObject?

    func example() {
        let name: String = self.detailItem?.valueForKey("name").description as String
    }
}

The problem with the above is I had to use description. It's generally not a good idea to use output from this function to show to the user (according to Apple, and just common sense)

The above also only works because I am looking for a string. If I needed an object, it wouldn't work.

As a point of interest this example throws a different error:

class MyExample4 {

    var detailItem: NSManagedObject?

    func example() {
        let name: String = self.detailItem?.valueForKey("name")
    }
}

The above throws:

Could not find member 'valueForKey'

NSmanagedObject clearly has a valueForKey.

Trying one more thing, I discovered a potential solution:

class MyExamplePotentialSolution {

    var detailItem: NSManagedObject?

    func example() {
        let name: NSString = self.detailItem?.valueForKey("name") as NSString
    }
}

The problem with the above, is it doesn't work when actually assigned to a UITableViewCell detailTextLabel.text attribute.

Any ideas?

Updated Answer

The simplest real world usage turned out to be this:

cell.detailTextLabel.text = self.detailItem?.valueForKey("name") as? NSString

The key is AnyObject can't be cast to a native swift type directly. As the accepted answer showed, it can be cast as a native Swift string from NSString. It just isn't necessary in this case.

like image 738
Derrek Avatar asked Jul 11 '14 18:07

Derrek


1 Answers

There are 2 problems: the first is that in this line:

let name: String = self.detailItem?.valueForKey("name") as String

the right part is an optional (detailItem is optional), so whatever the expression returns, it cannot be assigned to a non-optional variable having type String

The second problem is that valueForKey returns AnyObject!, which can be an instance of any class - but String is not a class, you'd need Any in order to be able to cast that into a String. I presume that NSManagedObject returns an NSString, so you can achieve what you need with this line:

let name: String? = (self.detailItem?.valueForKey("name") as? NSString) as? String
like image 143
Antonio Avatar answered Oct 02 '22 15:10

Antonio