Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling NSStringFromClass on a Swift Class in Objective-C returns module mangled name

I am aware of this question regarding how we can get a readable class name of an objective-c class in Swift.

What I want to achieve is getting the readable class name of a Swift class from inside objective-c without mangling the class name with the module.

So if I have a Swift class:

class Foo: NSObject{}

Then inside Objective-C I would love to use the convenient NSStringFromClass to convert the class name to a string.

I would expect NSStringFromClass([Foo class]) to return @"Foo" but instead it returns @"Bar.Foo" withBarbeing the module name.

I came across this Gist but it seems a little hacky and messy, is there a better way? Something that doesn't include typing the class name manually into a string would be preferred.

like image 982
Daniel Galasko Avatar asked Nov 14 '14 09:11

Daniel Galasko


4 Answers

BEFORE SWIFT 2.1:

Just put @objc(YourClassName) in your swift class:

@objc(YourClassName)

class YourClassName: NSObject {

}

And you can use NSStringFromClass like this:

NSStringFromClass(YourClassName.self)

It should also work from Objective-C then.


SWIFT 2.1

With Swift 2.1 a comment to this answer states that this is sufficient:

class YourClassName: NSObject {

}

And just use:

var str = String(YourClassName)

I have not tested this from Objective-C code myself though.


There's been a edit-suggestions that want to use this instead for Swift 4:

var str = String(describing: YourClassName.self)

I've not tested this from Objective-C though.

like image 159
ullstrm Avatar answered Nov 16 '22 15:11

ullstrm


The reason you get "Bar.Foo" is because swift is a namespace language. I'm assuming the application you were running this in was named Bar. Hence you may get your class name using the following:

let nameSpaceClassName = NSStringFromClass(Foo)
let className = nameSpaceClassName.componentsSeparatedByString(".").last! as String

Edit:

The above is an extremely old answer that should be changed based on more recent introductions in the Swift library (from 3 and 4 at least). Note the following use of the Mirroring interfaces to properly extract a class's metadata:

import Foundation

/// A protocol that provides demangled information regarding core Swift structure naming components in a usable way.
public protocol StructureNameReflectable {
    /**
     An ordered list of domain named based components for the structure.
     - Example: A simple example would be:
     ```
     class Dog: NSObject {
        class Retriever: NSOjbect {}
    }
     let retriever = Retriever()
     ```
     
     `retriever.structuredName` would output `["Dog", "Retriever"]`
 */
    static var structureNameComponents: [String] { get }
    
    ///Outputs the structure's name in a string based form minus namespacing
    static var structureName: String { get }
    
    ///Outputs the structure's name in a string based form with namespacing
    static var namespacedStructureName: String { get }
    
    ///Outputs the bundle the structure is contained in
    static var bundle: Bundle { get }
}

extension StructureNameReflectable {
    
    public static var structureNameComponents: [String] {
        let type = Mirror(reflecting: self).subjectType
        let structureNameComponents = "\(type)".components(separatedBy: ".")
        return structureNameComponents
    }
    
    public static var structureName: String {
        var structureNameComponents = self.structureNameComponents
        
        if structureNameComponents.count > 1 && structureNameComponents.last == "Type" {
            structureNameComponents.removeLast()
        }

        return structureNameComponents.last!
    }
    
    public static var namespacedStructureName: String {
        return structureNameComponents.joined(separator: ".")
    }
}

extension StructureNameReflectable where Self: NSObject {
    public static var bundle: Bundle {
        return Bundle(for: self)
    }
    
    public static var className: String {
        return structureName
    }
}

extension NSObject: StructureNameReflectable { }

Hopefully the above will give guidance for the consistently improper use of String(describing:).

like image 40
TheCodingArt Avatar answered Nov 16 '22 15:11

TheCodingArt


In objective-c your best option is:

NSString *yourCellName = NSStringFromClass([yourCell class]).pathExtension;
like image 6
oskarko Avatar answered Nov 16 '22 14:11

oskarko


Working solution for this in Swift 2.1 is

String(MyClass)
like image 2
ZaEeM ZaFaR Avatar answered Nov 16 '22 13:11

ZaEeM ZaFaR