52

In Object-C I store Class objects in an array and init them dynamically like this:

self.controllers=@[[OneViewController class],[TwoViewController class]];
Class clz = self.controllers[index];
UIViewController *detailViewController = [[clz alloc] init];

In Swift i try this way but it raises an error:

var controllers:AnyClass[] = [OneViewController.self,TwoViewController.self]
var clz : AnyClass = controllers[index]
var instance = clz() // Error:'AnyObject' is not constructible with ()

I wonder Whether there is a way to convert AnyClass to a specific Class? Or any other good ideas?

drinking
  • 1,533
  • 3
  • 15
  • 22

5 Answers5

56

You can specify the array to be of the common superclass' type, then type deduction does the right thing (Beta-3 syntax):

let classArray: [UIViewController.Type] = [
    OneViewController.self, TwoViewController.self
]
let controllerClass = classArray[index]
let controller = controllerClass.init()
iwasrobbed
  • 46,496
  • 21
  • 150
  • 195
DarkDust
  • 90,870
  • 19
  • 190
  • 224
  • 5
    "Initializing from a metatype value must reference 'init' explicitly", so `controllerClass.init()` it is now. – ctietze Dec 19 '15 at 10:50
12

i have found a way to solve this problem:

var controllers:AnyClass[] = [OneViewController.self,TwoViewController.self]
var clz: NSObject.Type = controllers[0] as NSObject.Type
var con = clz()

remember to add @objc in the class of ViewController

Xianng
  • 342
  • 3
  • 14
12

An AnyClass variable must first be casted into a specific type in order to initialize an instance:

// Code checked to run on xCode 7.1.1
import UIKit

var aClass: AnyClass = UIButton.self

// Error: 'init' is a member of the type...
// let doesNotWork = aClass.init()

// aClass must be casted first
var buttonClass = aClass as! UIButton.Type
var oneButton = buttonClass!.init()
var otherButton = buttonClass!.init(frame: CGRectZero)
Maic López Sáenz
  • 10,385
  • 4
  • 44
  • 57
8

Here is a pure swift implementation of dynamic class types. It does require the classes to extend the same protocol.

protocol ILayout { init(_ a:S tring) }   
class A: ILayout { required init(_ a: String) }  
class B: ILayout { required init(_ a:String) }   
var instance: ILayout   
var classType: ILayout.Type

classType = A.self   
instance = classType.init("abc")

classType = B.self   
instance = classType.init("abc")
Sentry.co
  • 5,355
  • 43
  • 38
  • @bazyl87 Thanks for the edit. I totally agree that your edit is more readable in code. But this isn't production code , it's just meant as a quick way to glean a methodology, where compactness is preferred. IMO – Sentry.co Oct 04 '18 at 10:18
0

If the classes are all Objective-C NSObject subclasses, you can do something like this (yes, the backticks are intentional):

var constructors: (Void -> NSObject!)[] = [ NSMutableString.`new`, NSMutableArray.`new`, NSMutableDictionary.`new` ]

let string = constructors[0]()
(string as NSMutableString).appendString("Hello")

let array = constructors[1]()
(array as NSMutableArray).addObject("Swift")

let dictionary = constructors[2]()
(dictionary as NSMutableDictionary).setObject("Objective-C", forKey: "Goodbye")

NSLog("dynamically created a string: \(string), an array: \(array), and a dictionary: \(dictionary)")

It should output:

dynamically created a string: Hello, an array: (
    Swift
), and a dictionary: {
    Goodbye = "Objective-C";
}

It does seem to me that there should be a more elegant way to do this.

marcprux
  • 9,845
  • 3
  • 55
  • 72