Swift language NSClassFromString - ios

How to achieve reflection in Swift Language?
How can I instantiate a class
[[NSClassFromString(#"Foo") alloc] init];

You must put #objc(SwiftClassName) above your swift class.
Like:
#objc(SubClass)
class SubClass: SuperClass {...}

This is the way I init derived UIViewController by class name
var className = "YourAppName.TestViewController"
let aClass = NSClassFromString(className) as! UIViewController.Type
let viewController = aClass()
More information is here
In iOS 9
var className = "YourAppName.TestViewController"
let aClass = NSClassFromString(className) as! UIViewController.Type
let viewController = aClass.init()

Less hacky solution here: https://stackoverflow.com/a/32265287/308315
Note that Swift classes are namespaced now so instead of "MyViewController" it'd be "AppName.MyViewController"
Deprecated since XCode6-beta 6/7
Solution developed using XCode6-beta 3
Thanks to the answer of Edwin Vermeer I was able to build something to instantiate Swift classes into an Obj-C class by doing this:
// swift file
// extend the NSObject class
extension NSObject {
// create a static method to get a swift class for a string name
class func swiftClassFromString(className: String) -> AnyClass! {
// get the project name
if var appName: String? = NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleName") as String? {
// generate the full name of your class (take a look into your "YourProject-swift.h" file)
let classStringName = "_TtC\(appName!.utf16count)\(appName)\(countElements(className))\(className)"
// return the class!
return NSClassFromString(classStringName)
}
return nil;
}
}
// obj-c file
#import "YourProject-Swift.h"
- (void)aMethod {
Class class = NSClassFromString(key);
if (!class)
class = [NSObject swiftClassFromString:(key)];
// do something with the class
}
EDIT
You can also do it in pure obj-c:
- (Class)swiftClassFromString:(NSString *)className {
NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleName"];
NSString *classStringName = [NSString stringWithFormat:#"_TtC%d%#%d%#", appName.length, appName, className.length, className];
return NSClassFromString(classStringName);
}
I hope this will help somebody !

UPDATE: Starting with beta 6 NSStringFromClass will return your bundle name plus class name separated by a dot. So it will be something like MyApp.MyClass
Swift classes will have a constructed internal name that is build up of the following parts:
It will start with _TtC,
followed by a number that is the length of your application name,
followed by your application name,
folowed by a number that is the length of your class name,
followed by your class name.
So your class name will be something like _TtC5MyApp7MyClass
You can get this name as a string by executing:
var classString = NSStringFromClass(self.dynamicType)
Update In Swift 3 this has changed to:
var classString = NSStringFromClass(type(of: self))
Using that string, you can create an instance of your Swift class by executing:
var anyobjectype : AnyObject.Type = NSClassFromString(classString)
var nsobjectype : NSObject.Type = anyobjectype as NSObject.Type
var rec: AnyObject = nsobjectype()

It's almost the same
func NSClassFromString(_ aClassName: String!) -> AnyClass!
Check this doc:
https://developer.apple.com/library/prerelease/ios/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/#//apple_ref/c/func/NSClassFromString

I was able to instantiate an object dynamically
var clazz: NSObject.Type = TestObject.self
var instance : NSObject = clazz()
if let testObject = instance as? TestObject {
println("yes!")
}
I haven't found a way to create AnyClass from a String (without using Obj-C). I think they don't want you to do that because it basically breaks the type system.

For swift2, I created a very simple extension to do this more quickly
https://github.com/damienromito/NSObject-FromClassName
extension NSObject {
class func fromClassName(className : String) -> NSObject {
let className = NSBundle.mainBundle().infoDictionary!["CFBundleName"] as! String + "." + className
let aClass = NSClassFromString(className) as! UIViewController.Type
return aClass.init()
}
}
In my case, i do this to load the ViewController I want:
override func viewDidLoad() {
super.viewDidLoad()
let controllers = ["SettingsViewController", "ProfileViewController", "PlayerViewController"]
self.presentController(controllers.firstObject as! String)
}
func presentController(controllerName : String){
let nav = UINavigationController(rootViewController: NSObject.fromClassName(controllerName) as! UIViewController )
nav.navigationBar.translucent = false
self.navigationController?.presentViewController(nav, animated: true, completion: nil)
}

This will get you the name of the class that you want to instantiate. Then you can use Edwins answer to instantiate a new object of your class.
As of beta 6 _stdlib_getTypeName gets the mangled type name of a variable. Paste this into an empty playground:
import Foundation
class PureSwiftClass {
}
var myvar0 = NSString() // Objective-C class
var myvar1 = PureSwiftClass()
var myvar2 = 42
var myvar3 = "Hans"
println( "TypeName0 = \(_stdlib_getTypeName(myvar0))")
println( "TypeName1 = \(_stdlib_getTypeName(myvar1))")
println( "TypeName2 = \(_stdlib_getTypeName(myvar2))")
println( "TypeName3 = \(_stdlib_getTypeName(myvar3))")
The output is:
TypeName0 = NSString
TypeName1 = _TtC13__lldb_expr_014PureSwiftClass
TypeName2 = _TtSi
TypeName3 = _TtSS
Ewan Swick's blog entry helps to decipher these strings: http://www.eswick.com/2014/06/inside-swift/
e.g. _TtSi stands for Swift's internal Int type.

In Swift 2.0 (tested in the Xcode 7.01) _20150930
let vcName = "HomeTableViewController"
let ns = NSBundle.mainBundle().infoDictionary!["CFBundleExecutable"] as! String
// Convert string to class
let anyobjecType: AnyObject.Type = NSClassFromString(ns + "." + vcName)!
if anyobjecType is UIViewController.Type {
// vc is instance
let vc = (anyobjecType as! UIViewController.Type).init()
print(vc)
}

xcode 7 beta 5:
class MyClass {
required init() { print("Hi!") }
}
if let classObject = NSClassFromString("YOURAPPNAME.MyClass") as? MyClass.Type {
let object = classObject.init()
}

string from class
let classString = NSStringFromClass(TestViewController.self)
or
let classString = NSStringFromClass(TestViewController.classForCoder())
init a UIViewController class from string:
let vcClass = NSClassFromString(classString) as! UIViewController.Type
let viewController = vcClass.init()

I am using this category for Swift 3:
//
// String+AnyClass.swift
// Adminer
//
// Created by Ondrej Rafaj on 14/07/2017.
// Copyright © 2017 manGoweb UK Ltd. All rights reserved.
//
import Foundation
extension String {
func convertToClass<T>() -> T.Type? {
return StringClassConverter<T>.convert(string: self)
}
}
class StringClassConverter<T> {
static func convert(string className: String) -> T.Type? {
guard let nameSpace = Bundle.main.infoDictionary?["CFBundleExecutable"] as? String else {
return nil
}
guard let aClass: T.Type = NSClassFromString("\(nameSpace).\(className)") as? T.Type else {
return nil
}
return aClass
}
}
The use would be:
func getViewController(fromString: String) -> UIViewController? {
guard let viewController: UIViewController.Type = "MyViewController".converToClass() else {
return nil
}
return viewController.init()
}

I think I'm right in saying that you can't, at least not with the current beta (2). Hopefully this is something that will change in future versions.
You can use NSClassFromString to get a variable of type AnyClass but there appears to be no way in Swift to instantiate it. You can use a bridge to Objective C and do it there or -- if it works in your case -- fall back to using a switch statement.

Apparently, it is not possible (anymore) to instantiate an object in Swift when the name of the class is only known at runtime. An Objective-C wrapper is possible for subclasses of NSObject.
At least you can instantiate an object of the same class as another object given at runtime without an Objective-C wrapper (using xCode Version 6.2 - 6C107a):
class Test : NSObject {}
var test1 = Test()
var test2 = test1.dynamicType.alloc()

In Swift 2.0 (tested in the beta2 of Xcode 7) it works like this:
protocol Init {
init()
}
var type = NSClassFromString(className) as? Init.Type
let obj = type!.init()
For sure the type coming from NSClassFromString have to implement this init protocol.
I expect it is clear, className is a String containing the Obj-C runtime name of the class which is by default NOT just "Foo", but this discussion is IMHO not the major topic of your question.
You need this protocol because be default all Swift classes don't implement an init method.

Looks like the correct incantation would be...
func newForName<T:NSObject>(p:String) -> T? {
var result:T? = nil
if let k:AnyClass = NSClassFromString(p) {
result = (k as! T).dynamicType.init()
}
return result
}
...where "p" stands for "packaged" – a distinct issue.
But the critical cast from AnyClass to T currently causes a compiler crash, so in the meantime one must bust initialization of k into a separate closure, which compiles fine.

I use different targets, and in this case the swift class is not found. You should replace CFBundleName with CFBundleExecutable. I also fixed the warnings:
- (Class)swiftClassFromString:(NSString *)className {
NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleExecutable"];
NSString *classStringName = [NSString stringWithFormat:#"_TtC%lu%#%lu%#", (unsigned long)appName.length, appName, (unsigned long)className.length, className];
return NSClassFromString(classStringName);
}

Isn't the solution as simple as this?
// Given the app/framework/module named 'MyApp'
let className = String(reflecting: MyClass.self)
// className = "MyApp.MyClass"

Also in Swift 2.0 (possibly before?) You can access the type directly with the dynamicType property
i.e.
class User {
required init() { // class must have an explicit required init()
}
var name: String = ""
}
let aUser = User()
aUser.name = "Tom"
print(aUser)
let bUser = aUser.dynamicType.init()
print(bUser)
Output
aUser: User = {
name = "Tom"
}
bUser: User = {
name = ""
}
Works for my use case

Try this.
let className: String = String(ControllerName.classForCoder())
print(className)

I have implemented like this,
if let ImplementationClass: NSObject.Type = NSClassFromString(className) as? NSObject.Type{
ImplementationClass.init()
}

Swift 5, easy to use, thanks to #Ondrej Rafaj's
Source code:
extension String {
fileprivate
func convertToClass<T>() -> T.Type? {
return StringClassConverter<T>.convert(string: self)
}
var controller: UIViewController?{
guard let viewController: UIViewController.Type = convertToClass() else {
return nil
}
return viewController.init()
}
}
class StringClassConverter<T> {
fileprivate
static func convert(string className: String) -> T.Type? {
guard let nameSpace = Bundle.main.infoDictionary?["CFBundleExecutable"] as? String, let aClass = NSClassFromString("\(nameSpace).\(className)") as? T.Type else {
return nil
}
return aClass
}
}
Call like this:
guard let ctrl = "ViewCtrl".controller else {
return
}
// ctrl do sth

A page jump example shown here, the hope can help you!
let vc:UIViewController = (NSClassFromString("SwiftAutoCellHeight."+type) as! UIViewController.Type).init()
self.navigationController?.pushViewController(vc, animated: true)
// Click the Table response
tableView.deselectRow(at: indexPath, animated: true)
let sectionModel = models[(indexPath as NSIndexPath).section]
var className = sectionModel.rowsTargetControlerNames[(indexPath as NSIndexPath).row]
className = "GTMRefreshDemo.\(className)"
if let cls = NSClassFromString(className) as? UIViewController.Type {
let dvc = cls.init()
self.navigationController?.pushViewController(dvc, animated: true)
}

Swift3+
extension String {
var `class`: AnyClass? {
guard
let dict = Bundle.main.infoDictionary,
var appName = dict["CFBundleName"] as? String
else { return nil }
appName.replacingOccurrences(of: " ", with: "_")
let className = appName + "." + self
return NSClassFromString(className)
}
}

Here is a good example:
class EPRocks {
#require init() { }
}
class EPAwesome : EPRocks {
func awesome() -> String { return "Yes"; }
}
var epawesome = EPAwesome.self();
print(epawesome.awesome);

Related

How to invoke a class method using performSelector() on AnyClass in Swift?

In ObjC you could simply invoke a class method using the class method from NSObject.
[Machine performSelector:#selector(calculate:) withObject:num];
But how do you do this in Swift 2.2?
#objc(Machine) // put it here, so you can simply copy/paste into Playground
class Machine: NSObject {
static func calculate(param: NSNumber) -> String {
if param.integerValue > 5 {
return "42"
}
return "42" // there is only 1 answer to all the questions :D
}
}
if let aClass = NSClassFromString("Machine") {
let sel = #selector(Machine.calculate(_:))
let num = NSNumber(integer: 1337)
let answer = aClass.performSelector(sel, withObject: num) // compiler error
// let answer = aClass.calculate(num) // <-- this works
print(answer)
}
With this code I'm getting the following compiler error:
error: cannot invoke 'performSelector' with an argument list of type '(Selector, withObject: NSNumber)'
What am I missing here?
AnyClass does not conform to NSObjectProtocol out of the box. I had to cast aClass as NSObjectProtocol to use performSelector (performSelector:withObject: is bridged to Swift as a method on NSObjectProtocol):
Swift 3:
if let aClass = NSClassFromString("Machine") {
let sel = #selector(Machine.calculate(param:))
let num = NSNumber(value: 1337)
if let myClass = aClass as? NSObjectProtocol {
if myClass.responds(to: sel) {
let answer = myClass.perform(sel, with: num).takeRetainedValue() // this returns AnyObject, you may want to downcast to your desired type
print(answer) // "42\n"
}
}
}
Swift 2.x:
(aClass as! NSObjectProtocol).performSelector(sel, withObject: num) // Unmanaged<AnyObject>(_value: 42)
A little bit safer:
if let aClass = NSClassFromString("Machine") {
let sel = #selector(Machine.calculate(_:))
let num = NSNumber(integer: 1337)
if let myClass = aClass as? NSObjectProtocol {
if myClass.respondsToSelector(sel) {
let answer = myClass.performSelector(sel, withObject: num).takeUnretainedValue()
print(answer) // "42\n"
}
}
}
performSelector returns an Unmanaged object, that's why takeUnretainedValue() (or optionally takeRetainedValue() if you want to transfer memory ownership) are required.
If you want to perform calculate on machine, just do:
Machine.calculate(NSNumber(integer: 1337))
If you need to call perform selector, then do:
if let aClass = NSClassFromString("Machine") as? AnyObject
{
let sel = #selector(Machine.calculate(_:))
let num = NSNumber(integer: 1337)
let answer = aClass.performSelector(sel, withObject: num)
print(answer)
}

Write Generic Swift Method to load data from property list

I have a method that loads an array of dictionaries from a propertylist. Then I change those arrays of dictionaries to array of a defined custom type;
I want to write that method in generic form so I call that method with the type I expect, then the method loads it and returns an array of my custom type rather than dictionaries
func loadPropertyList(fileName: String) -> [[String:AnyObject]]?
{
if let path = NSBundle.mainBundle().pathForResource(fileName, ofType: "plist")
{
if let plistXML = NSFileManager.defaultManager().contentsAtPath(path)
{
do {
if let temp = try NSPropertyListSerialization.propertyListWithData(plistXML, options: .Immutable, format: nil) as? [[String:AnyObject]]
{
return temp
}
}catch{}
}
}
return nil
}
//
func loadList<T>(fileName: String) -> [T]?{//**Here the answer I am expecting**}
I am assuming your function to read from a Plist works and that you don't want to subclass NSObject.
Since Swift reflecting does not support setting values this is not possible without some implementation for each Type you want this to work for.
It can however be done in a pretty elegant way.
struct PlistUtils { // encapsulate everything
static func loadPropertyList(fileName: String) -> [[String:AnyObject]]? {
if let path = NSBundle.mainBundle().pathForResource(fileName, ofType: "plist") {
if let plistXML = NSFileManager.defaultManager().contentsAtPath(path) {
do {
if let temp = try NSPropertyListSerialization.propertyListWithData(plistXML, options: .Immutable, format: nil) as? [[String:AnyObject]] {
return temp
}
} catch {
return nil
}
}
}
return nil
}
}
This protocol will be used in a generic fashion to get the Type name and read the corresponding Plist.
protocol PListConstructible {
static func read() -> [Self]
}
This protocol will be used to implement Key Value setters.
protocol KeyValueSettable {
static func set(fromKeyValueStore values:[String:AnyObject]) -> Self
}
This is the combination of both to generate an array of objects. This does require that the Plist is named after the Type.
extension PListConstructible where Self : KeyValueSettable {
static func read() -> [Self] {
let name = String(reflecting: self)
var instances : [Self] = []
if let data = PlistUtils.loadPropertyList(name) {
for entry in data {
instances.append(Self.set(fromKeyValueStore: entry))
}
}
return instances
}
}
This is some Type.
struct Some : PListConstructible {
var alpha : Int = 0
var beta : String = ""
}
All you have to do is implement the Key Value setter and it will now be able to be read from a Plist.
extension Some : KeyValueSettable {
static func set(fromKeyValueStore values: [String : AnyObject]) -> Some {
var some = Some()
some.alpha = (values["alpha"] as? Int) ?? some.alpha
some.beta = (values["beta"] as? String) ?? some.beta
return some
}
}
This is how you use it.
Some.read()

Swift - how do I pass the type through a method call?

A Realm object:
class Dog: Object {
dynamic var name = ""
dynamic var age = 0
}
Calling results:
let results = realm.objects(Dog)
Or doing it this way:
let type = Dog.self
let results = realm.objects(type)
I want to be able to do it by passing into a method like this:
class SomeClass {
func callRealm(type: AnyObject) {
let results = realm.objects(type)
}
}
let someClass = SomeClass()
let type = Dog.self
someClass.callRealm(type)
How would I do this? I've had no luck with generics, although I think this might be the way to go.
Your function callRealm should take input as AnyClass instead of AnyObject.
func callRealm(type: AnyClass) {
let results = realm.objects(type)
}

How to call static method provided by protocol in Swift

How to access to static protocol method within a instance
I have a list of Contact, the contact can be a FamilyContact that inherit from Contact and the GroupStatus protocol
I want to call the static method from GroupStatus but in vain...
Here is my code
protocol GroupStatus {
static func isPrivate() -> Bool // static method that indicates the status
}
protocol IsBusy {
func wizzIt()
}
class AdresseBook {
private var contacts = [Contact]()
func addOne(c: Contact) {
contacts.append(c)
}
func listNonPrivated() -> [Contact]? {
var nonPrivateContact = [Contact]()
for contact in contacts {
// here is I should call the static method provided by the protocol
if self is GroupStatus {
let isPrivate = contact.dynamicType.isPrivate()
if !isPrivate {
nonPrivateContact.append(contact)
}
}
nonPrivateContact.append(contact)
}
return nonPrivateContact
}
}
class Contact : Printable {
var name: String
init(name: String) {
self.name = name
}
func wizz() -> Bool {
if let obj = self as? IsBusy {
obj.wizzIt()
return true
}
return false
}
var description: String {
return self.name
}
}
class FamilyContact: Contact, GroupStatus {
static func isPrivate() -> Bool {
return true
}
}
I can't compile Contact.Type does not have a member named 'isPrivate'
How can I call it ? It works if I delete the static keyword, but I think is more logical to define it static.
If I replace
let isPrivate = contact.dynamicType.isPrivate()
by
let isPrivate = FamilyContact.isPrivate()
It works, but I can have more than 1 subclasses
If I remove the static keywork I can do it by this way :
if let c = contact as? GroupStatus {
if !c.isPrivate() {
nonPrivateContact.append(contact)
}
}
But I want to keep the static keyword
This looks like a bug or a non-supported feature. I would expect that
the following works:
if let gsType = contact.dynamicType as? GroupStatus.Type {
if gsType.isPrivate() {
// ...
}
}
However, it does not compile:
error: accessing members of protocol type value 'GroupStatus.Type' is unimplemented
It does compile with FamilyContact.Type instead of GroupStatus.Type. A similar problem is reported here:
Swift 1.1 and 1.2: accessing members of protocol type value XXX.Type' is unimplemented
Making isPrivate() an instance method instead of a class method is
the only workaround that I currently can think of, maybe someone comes
with a better solution ...
Update for Swift 2 / Xcode 7: As #Tankista noted below, this has
been fixed. The above code compiles and works as expected in Xcode 7 beta 3.
type(of: contact).isPrivate()
This should work in recent Swift.

Get class name of UIViewController in swift

How do you get the class name of a UIViewController class in Swift?
In Objective-C, we can do something like this:
self.appDelegate = (shAppDelegate *)[[UIApplication sharedApplication] delegate];
UIViewController *last_screen = self.appDelegate.popScreens.lastObject ;
if(last_screen.class != self.navigationController.visibleViewController.class){
//.......
}
but in Swift I tried:
let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let last_screen = appDelegate.popScreens?.lastObject as UIViewController
Can't do this.
if last_screen.class != self.navigationController.visibleViewController.class {
//....
}
no class method of UIViewController i.e last screen
To know your class name you can call something like this:
var className = NSStringFromClass(yourClass.classForCoder)
The cleanest way without needing to know the name of your class is like this.
let name = String(describing: type(of: self))
A simple way in swift 3 is to write the below code:
for instances:
let className = String(describing: self)
for classes:
let className = String(describing: YourViewController.self)
Expanding on juangdelvalle's answer.
I added this as an extension so that it's reusable and easier to call from any view controller. Also in some cases NSStringFromClass in Swift returns a string in the format like this:
< project name >.viewControllerClassName.
This extension property is modified to get rid of the project name prefix and return only the class name.
extension UIViewController {
var className: String {
NSStringFromClass(self.classForCoder).components(separatedBy: ".").last!
}
}
Swift 4
Suppose we have class with name HomeViewController. Then you can get name of class with the following code:
let class_name = "\(HomeViewController.classForCoder())"
The classForCoder() method returns AnyClass object (name of your class) which we convert to string for user.
Here is a swift3 version of isuru's answer.
extension UIViewController {
var className: String {
return NSStringFromClass(self.classForCoder).components(separatedBy: ".").last!;
}
}
Swift 5 solution:
extension NSObject {
var className: String {
return String(describing: type(of: self))
}
class var className: String {
return String(describing: self)
}
}
USAGE:
class TextFieldCell: UITableVIewCell {
}
class LoginViewController: UIViewController {
let cellClassName = TextFieldCell.className
}
The property is called dynamicType in Swift.
Use String.init(describing: self.classForCoder)
example:
let viewControllerName = String.init(describing: self.classForCoder)
print("ViewController Name: \(viewControllerName)")
How about:
extension NSObject {
static var stringFromType: String? {
return NSStringFromClass(self).components(separatedBy: ".").last
}
var stringFromInstance: String? {
return NSStringFromClass(type(of: self)).components(separatedBy: ".").last
}
}
We can also do: String(describing: Self.self) in Swift 5.1.

Resources