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
Need Self to Set All Constants of a Swift Class in Init
Iterate an Array W/ Explicit Object Type in Swift
Do Local Notifications Need User Permission on iOS
Xcode10 - Dyld: Library Not Loaded for Pod Installed in Framework
iOS 11 Black Bar Appears on Navigation Bar When Pushing View Controller
iOS 10 App Crashes When Trying to Save Image to Photo Library
Module File's Deployment Target Is iOS9.0 V9.0 with Xcode 7/Swift 2
Get Resource Url from Project Specific Path
How to Remove Quotes from String on Swift
How to Avoid Migration in Realmswift
Is Removing a Notificationcenter Observer That Was Created with Closure Syntax by Name Adequate
Swiftui - How to Make a Start/Stop Timer
Swift - How to Save Audio from Avaudioengine, or from Audioplayernode? If Yes, How
Swiftui MACos Nswindow Instance
Swift Error When Trying to Access Dictionary: 'Could Not Find Member 'Subscript''
Difference Between Switch Cases "@Unknown Default" and "Default" in Swift 5