Getting class from string in swift - objective-c

I have created a custom class named PredicateController.
I need to pass the class of PredicateController which will be called from a framework written in Objc. However, whenever I am using the properties of swift, the same is returning me <ProjectName>.PredicateController.
I only need the class name as PredicateController.
Is there any workaround in swift to do the same.
Thanks.

You can get the type of a class by using self on the class and then simply wrap it into a string, e.g. ...
let date = NSDate();
let clazz = NSDate.self
let str = "\(date) is of type \(clazz)"
println(str)

You can use .className on the instance if the class is inheriting from NSObject:
class PredicateController: NSObject {
// ...
}
let pc = PredicateController()
let klass = pc.className
let name = klass.componentsSeparatedByString(".")[1]
let str = "\(pc) is of type \(name)"
println(str) // "<__lldb_expr_1398.PredicateController: 0x7fd92b465080> is of type PredicateController"
My answer is a follow-up to #hexagonstar's one. I don't know if it's the proper way of doing this, but it works.

Related

Kotlin: Using method reference to a getter of another class

I have a class -
class ES {
var issue: SomeClass? = null
}
I need to access its getter in another class -
class CSS {
private val ref: Supplier<SomeClass?> = ES::issue
}
However this is not working. Its throws this error -
Type mismatch.
Required: Supplier<SomeClass?>
Found : KMutableProperty1<ES, SomeClass?>
Can someone tell me what am I doing wrong? I am actually in the process of converting java code to kotlin.
UPDATE
I need a static reference to the getter of the ES class, similar to JAVA, where we can do ->
Function<ES, SomeClass> ref = ES::getIssue;
In Kotlin, instead of using Supplier, you use functional syntax for the type. In this case, the equivalent of your Supplier<SomeClass?> would be () -> SomeClass? (assuming ES is an object since that's how you used it in your example code):
class CSS(/*...*/) {
private val ref: () -> SomeClass? = ES::issue
}
But if you have to use Supplier specifically so it can be easily used with Java code, you can wrap your getter reference in a Supplier implementation:
class CSS(/*...*/) {
private val ref: Supplier<SomeClass?> = Supplier(ES::issue)
}
Update
If you want the getter without a specific instance of the class, similar to Function<ES, SomeClass> in Java, then you need to make ES a parameter of the function.
Either
private val ref: (ES) -> SomeClass? = ES::issue
or
private val ref: ES.() -> SomeClass? = ES::issue
I don't believe there's a way to do this with Supplier, but I don't think you could do it in Java with Supplier either.

Type check an Any variable for Data Class

I have a class that has a constructor of type Any. I'm passing an instance of a Data Class to that constructor. How can I type check the Any variable to make sure it contains a Data Class?
What I tried so far:
private var myObject : Any
fun dataClassTypeCheck(): Boolean {
if (myObject is KClass<*>) {return true}
return false
}
If you want to know if myObject has a type which is a data class then it's:
myObject::class.isData.
If you want to know if myObject is a KClass object of a data class then it's: myObject.isData
if you have Class<?>:
MyObjectClass::class.java.kotlin.isData
and if you have instance of class:
myObject.javaCalass.kotlin.isData

How to declare a property as a function in Swift?

Here is my code:
import Cocoa
class VC1: NSViewController {
let aFunctionVar ()->Void
}
The compiler however tells me: "Class VC1 has no initializers"
According to the swift example in Apple Swift iBook, they did their examplle like so:
var mathFunction: (Int, Int) -> Int = addTwoInts
But in my case, I'm trying to create a property variable. It is not yet known what the variable will be, so i can't set it there. Any help?
Edit - I already know how to make variables optional and lazy when it comes to simple String/Array/Dictionary types etc. But this is a function type property variable. It is meant to hold a function of type ()->Void. Any help on how this can be done?
In objectiveC this can be done by making a block property like this:
#property (nonatomic, copy) void (^aFunctionVar)();
Declare projectLaunchData as an optional var:
import Cocoa
class VC1: NSViewController {
var projectLaunchData: (()->Void)?
}
Then you can assign a value later:
func test() {
print("this works")
}
let myVC = VC1()
// assign the function
myVC.projectLaunchData = test
// Call the function using optional chaining. This will safely do nothing
// if projectLaunchData is nil, and call the function if it has been assigned.
// If the function returns a value, it will then be optional because it was
// called with the optional chaining syntax.
myVC.projectLaunchData?()
If the value will be known after the object is setup, you can use a lazy variable:
class LazyTester {
lazy var someLazyString: String = {
return "So sleepy"
}()
}
var myLazyTester = LazyTester()
myLazyTester.someLazyString
The compiler is giving you that error because you are defining a mandatory stored variable, projectLaunchData, but not giving it a value. If you know the variables value at init time, you can set it at init time.

How to cast an object from its base class into its subclass

I have a class User which is a subclass of class PFUser:
class User: PFUser {
var isManager = false
}
In one of my methods I receive a PFUser object and I want to cast it to a User object
func signUpViewController(signUpController: PFSignUpViewController!, didSignUpUser user: PFUser!) {
currentUser = user
}
Is this possible?
This type of casting is a downcast. Given an instance of a certain base class where you know there exist subclasses, you can try to downcast with the typecast operator as:
class Base {}
class Derived : Base {}
let base : Base = Derived()
let derived = base as Derived
Keep in mind though, that a downcast can fail:
class Base {}
class Derived : Base {}
class Other : Base {}
let base : Base = Other()
let derived = base as Derived // fails with a runtime exception
You can try a downcast using the optional form of the type as operator as?.
class Base {}
class Derived : Base {}
class Other : Base {}
let base : Base = Other()
// The idiomatic implementation to perform a downcast:
if let derived = base as? Derived {
println("base IS A Derived")
}
else {
println("base IS NOT A Derived") // <= this will be printed.
}
This can also be done by using the following:
object_setClass(baseClass, derivedClass.self)
However it is worth nothing that this uses the objc-runtime library which can introduce odd crashes if not used correctly.
If it's an instance of PFUser, and not an instance of User stored in a variable of PFUser type, no it's not possible.
You can cast an instance of a class to one of its superclasses, but you cannot do the other way (unless the cast type is the actual instance type).
I suggest to implement an initializer in User, taking a PFUser instance as parameter - if that's possible.
However, although I never attempted to do so, I think inheriting from PFUser you'll just run into troubles, as my understanding is that this class is not designed to be inherited as it is for PFObject. I'd suggest looking into making PFUser a property of User - that way you can just assign as needed.

Get a user-readable version of the class name in swift (in objc NSStringFromClass was fine)

Is there an equivalent of NSStringFromClass in Swift that gives a user-readable version of the class name? I've tried to use it with a native Swift class I created, but as you can see, the result seems to be the compiler's internal representation of the class name:
println(NSStringFromClass(MyClass.self))
Result:
_TtC5test7MyClass
I've tried adding the #objc attribute to the class, and making it a subclass of NSObject, but it makes no difference. I've discovered that if I replace MyClass with an Objective-C class of the same name, and import this in the bridging header, it gives me 'MyClass', but this shouldn't be necessary.
Another option would be to make a protocol for this, which each class I want to use in this way must conform to:
protocol Nameable {
class var className: String { get }
}
However, it would be easier to have a built-in way to do this in the language.
You can now just do:
String(MyClass)
new format based on xcode 6 beta 5.
(project_name).(class_name)
func getName(classType:AnyClass) -> String {
let classString = NSStringFromClass(classType.self)
let range = classString.rangeOfString(".", options: NSStringCompareOptions.CaseInsensitiveSearch, range: Range<String.Index>(start:classString.startIndex, end: classString.endIndex), locale: nil)
return classString.substringFromIndex(range!.endIndex)
}
Latest 6.3 Xcode Swift 1.2
if you need an extension or you can put this on any common object:
public extension NSObject{
public class var nameOfClass: String{
return NSStringFromClass(self).componentsSeparatedByString(".").last!
}
public var nameOfClass: String{
return NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
}
}
Swift 3
type(of: self) prints ModuleName.ClassName
String(describing: type(of: self)) prints ClassName
At the moment, there's no reliable way to do this. See an Apple developer's comment on https://devforums.apple.com/thread/227425
Swift does not currently have much in the way of introspection.
There is some introspection machinery that is used for the
playgrounds. I don't know if that is intended to be API.
Some Swift
methods and variables can be examined using the Objective-C runtime's
introspection. That's likely to be the best solution today.
Swift does have the notion of a metatype, somewhat analogous to the Class type in Objective C. You can find it using TypeName.self, e.g.:
class Foo {
#required init() {
}
}
var normalFoo : Foo = Foo()
var fooType : Foo.Type = Foo.self;
var fooFromMetatype : Foo = fooType();
Perhaps, by release time, metatypes will include more introspection abilities. I suggest filing a Radar feature request for this.
In Swift 2 beta 4 you can get to the information via the type object:
let className = "\(String.self)" // gives: "Swift.String"
or if you have an instance:
let s = "Hello World"
let className = "\(s.dynamicType)" // gives: "Swift.String"
You get the Module.Class result, like:
Swift.String
ABC.MyGenericClass<Swift.Int>
Funny enough the Type object returned does not seem to conform to the CustomStringConvertible protocol. Hence it has no 'description' property, though the "" pattern still does the right thing.
P.S.: Before b4 the same could be accomplished via reflect(obj.dynamicType).summary.
In Swift v3.0, this worked for me:
String.init(describing: self.self)
----- Updated -----
As #ThomasW mentioned, for Swift 4, we need to use String(describing:type(of:self))
----- Old post ----
I prefer to use String(self.dynamicType)
Use it in my project https://github.com/JakeLin/SwiftWeather/blob/e7165b0919dda53fd7fcba9b43fdfe150d73c6f8/SwiftWeather/ForecastView.swift#L135
If you want to have only the name of the class in swift you can parse the string returned by NSStringFromClass().
This is done in nameOfClass.swift of the INSwift Github repository:
https://github.com/indieSoftware/INSwift
You shouls now be able to use the following to retrieve the class name in swift
let nameSpaceClassName = NSStringFromClass(RWTCountryPopoverViewController.self)
let className = nameSpaceClassName.componentsSeparatedByString(".").last! as String
This is a bit shorter. No need of NSStringFromClass
MyObject.self.description().componentsSeparatedByString(".").last!
Here is Swift 3+, Xcode 8+ example with code:
class MySuperbClass{
let a = 4
func getClassName() -> String? {
return String(describing: type(of:self)).components(separatedBy: ".").first
}
}
let className = String(describing: type(of:MySuperbClass.self)).components(separatedBy: ".").first
//className = "MySuperbClass"
let classNameFromObject = MySuperbClass().getClassName()
//classNameFromObject = "MySuperbClass"
Swift 4
super.init(nibName:String(describing:MyClass.self), bundle: nil)
myObject.description().componentsSeparatedByString(" ").first!
This is not exactly what you want - it will add an unwanted leading '<' and trailing ':'. But when I am debugging I value speed over neatness so this quick + dirty trick worked for me.
Swift 3
NSStringFromClass(self).components(separatedBy: ".").last!
In latest version of swift, below works for me:
NSStringFromClass(type(of: device!.self))