Create Instance of Class Known at Runtime in Swift

Create instance of class known at runtime in Swift

This cannot be done purely in Swift. You can only do this by creating the "class instance by name creator" in Objective C and calling this code from Swift.

For more information you can read this article.
http://ijoshsmith.com/2014/06/05/instantiating-classes-by-name-in-swift/

And check out this github repo
https://github.com/ijoshsmith/swift-factory

How to create an instance of a class from a String in Swift

You can try this:

func classFromString(_ className: String) -> AnyClass! {

/// get namespace
let namespace = Bundle.main.infoDictionary!["CFBundleExecutable"] as! String

/// get 'anyClass' with classname and namespace
let cls: AnyClass = NSClassFromString("\(namespace).\(className)")!

// return AnyClass!
return cls
}

use the func like this:

class customClass: UITableView {}   

let myclass = classFromString("customClass") as! UITableView.Type
let instance = myclass.init()

How do I create an instance of a class from the child of a Mirror object in Swift

Your code won't work but the error you get is wrong so you should ignore it.

The real problem is you can't just blindly call init() on the type of an Any. There are a lot of types that don't have an init() at all. It works on type(of: s) in your first example because the compiler knows at compile-time that the type is String (and that String has an init()). But if you wrap it in Any then it fails as well:

let s = String("Foo") as Any
let typeClone = type(of: s).init()

Unfortunately this means there's no way to do what you're trying to do.

Create object of Objective-C class at runtime in Swift, which conforms to Objective-C protocol

Let's define a noise method on Animal for testing:

@protocol Animal <NSObject>
- (NSString *)noise;
@end

Also, let's use a normal Swift array to hold the class list:

let allClassesCount = objc_getClassList(nil, 0)
var allClasses = [AnyClass](repeating: NSObject.self, count: Int(allClassesCount))
allClasses.withUnsafeMutableBufferPointer { buffer in
let autoreleasingPointer = AutoreleasingUnsafeMutablePointer<AnyClass>(buffer.baseAddress)
objc_getClassList(autoreleasingPointer, allClassesCount)
}

Then, when we find a class that conforms to Animal, let's convert it to the appropriate Swift type ((NSObject & Animal).Type) so that when we instantiate it, we get an object of the appropriate type (NSObject & Animal):

for aClass in allClasses {
if class_conformsToProtocol(aClass, Animal.self) {
let animalClass = aClass as! (NSObject & Animal).Type

// Because animalClass is `(NSObject & Animal).Type`:
// - It has the `init()` of `NSObject`.
// - Its instances are `NSObject & Animal`.
let animal = animalClass.init()

// Because animal is `NSObject & Animal`, it has the `noise` method of `Animal`.
print(animal.noise())
}
}

Output:

woof
meow

Side note. You might think you can avoid using class_conformsToProtocol by doing this:

if let animalClass = aClass as? (NSObject & Animal).Type {
let animal = animalClass.init()
print(animal.noise())
}

But you will crash at runtime:

*** CNZombie 3443: -[ conformsToProtocol:] sent to deallocated instance 0x7fffa9d265f0

Under the covers, the as? test sends the conformsToProtocol: message to aClass using normal Objective-C messaging. But there are various “zombie classes” in the system frameworks that crash when any message is sent to them. These classes are used to detect use-after-free errors. The class_conformsToProtocol function doesn't use Objective-C messaging, so it avoids these crashes.

How to add a method to a class at runtime in swift

You could add a compare method to NSNull like this:

Objective-C:

#import <objc/runtime.h>

static inline NSComparisonResult compareNulls(id self, SEL _cmd, NSNull *other) {
if([other isKindOfClass:[NSNull class]]) {
return NSOrderedSame; // Nulls are always the same.
}

return NSOrderedDescending;
}

@implementation NSNull (Comparisons)

+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
const char *encoding = [[NSString stringWithFormat:@"%s@:@", @encode(NSComparisonResult)] UTF8String];
class_addMethod([self class], @selector(compare:), (IMP)compareNulls, encoding);
});
}

@end

Swift:

// Add this code to your AppDelegate.swift file:
import ObjectiveC

fileprivate func compareNulls(_ self: AnyObject, _ _cmd: Selector, _ other: AnyObject) -> ComparisonResult {
if other is NSNull {
return .orderedSame
}

return .orderedDescending
}

fileprivate func addNSNullCompareImplementationIfNecessary() {
let sel = NSSelectorFromString("compareNulls:")
guard class_getMethodImplementation(NSNull.self, sel) == nil else {
return
}

let types = "i@:@"
class_addMethod(NSNull.self, sel, imp_implementationWithBlock(compareNulls), types)
}

// Add this line to your -didFinishLaunching: function:
addNSNullCompareImplementationIfNecessary()

This is only a temporary solution that will stop the crashes.

I would nevertheless encourage you to a) file a bug report, and b) continue investigating why this happened - clearly having an NSNull in this case wasn't expected by Parse...

How to instantiate class by name in Swift without using the objective-c runtime

This isn't currently possible in pure Swift. Not because Swift is "statically typed", but because it just hasn't been implemented yet. When using the Objective C runtime (which is dynamic, yes) to do this, you're required to provide the compiler static type information. There is a very limited portion of the program with dynamic behaviour. In a sense, one of the compiler's jobs is to require you to get back into the realm of static type rainbows and safety butterflies by requiring you make compile-time assertions about the runtime behaviour, using explicit casts.

I may be wrong, but I don't think there's a C/C++ workaround for this. Ultimately, you need some sort of runtime system with knowledge of all loaded classes, that can be queried by name. Swift relies on the Objective C runtime for this.

Reflection is a goal for Swift, and the development team has stated that they've increased the reflection meta-data being included as of Swift 3. As they work towards increasing reflective capabilities in Swift. Swift is currently under very rapid evolvement, and the team implementing Swift find reflection to be a lower priority to other goals (conditional conformances, improved generic, ABI stability, API guideline standardization, etc.).

Loading a instance of a class dynamically in swift

Your question, with its repeated and meaningless "doesn't work" and "the below", is confused and confusing, so I will respond by assuming that what you want to do is instantiate and push a view controller given its class, and I will simply demonstrate in a few lines of code that this is possible.

Here is a function that returns a UIViewController subclass:

func provideVCClass() -> UIViewController.Type {
return ViewController.self
}

And here are lines from a button's action handler where we call that function and use the class to instantiate that view controller and push it onto the current navigation controller:

let vcclass = provideVCClass()
let vc = vcclass(nibName: "View", bundle: nil)
self.navigationController?.pushViewController(vc, animated: true)

I assure you that this does work: the correct view controller subclass is instantiated, its nib is loaded, and its view does appear in the interface.

So all I can say in answer to your question, which I don't fully understand, is Go Ye And Do Likewise.

EDIT Here's a little postscript. If your code actually says:

let arr = [MyClass1.self, MyClass2.self]

I would suggest you try saying

let arr : [UIViewController] = [MyClass1.self, MyClass2.self]

...just in case Swift is not inferring the correct type here.



Related Topics



Leave a reply



Submit