I'm trying to read a class from a String and pass its type to a generic function. But it seems like there's no way to achieve this:
protocol Person { ... }
class Student: Person { ... }
class Teacher: Person { ... }
func foo<SomePerson: Person>(param: String, type: SomePerson.Type) { ... }
// Get class from string and pass class type to foo()
let someClass = NSClassFromString("MyApp.Teacher")
foo("someParam", type: someClass.dynamicType)
Trying this I'm getting an error:
Cannot invoke 'foo' with an argument list of type '(String, type: AnyClass.Type)
Is it actually possible to obtain the 'real' type 'Teacher' instead of the generic type 'AnyClass' and pass it to foo()? The classes are read on runtime from a file - so using hard class names when calling foo() is not possible.
Thanks for any advice!
If Person is a class as the original question was written:
Since you are making the life for the compiler considerably harder that it needs to be you have to help him a bit. You have to make sure the type is correct before calling the function:
if let actualType = someClass as? Person.Type {
foo("someParam", type: actualType)
} else {
// error handling here
}
Which yields the desired working code:
If Person is a protocol:
You are going to have a bad day. The above solution will not work. As far as I can remember your only option is to check for every possible subclass:
if let actualType = someClass as? Student.Type {
foo("someParam", type: actualType)
} else if let actualType = someClass as? Teacher.Type {
foo("someParam", type: actualType)
}
// etc.
Related
This is my code:
class Person<T> {
func printMyType() {
// I have access to type T.
print(T.self)
}
}
class Child {
let person = Person<Any>()
func printPersonType() {
// Where did type T went to? It doesn't compile.
print(person.T.self)
}
}
I am wondering why I don't have access to type T anymore of instance Person in instance Child. Why is type T gone? Why is it available in the method printMyType, but not outside of the instance?
Above was my question. Below is how I want to use it.
I want to know if it is possible to acquire the generic used type of Person, because now in my 'real' code, I need to repeat generic constraints:
class Person<T: Child> {}
class Child {}
// In this class, I want to know what Child is used.
// To accomplish this, I now need to create a type U to access Child
// Since I do not know how to access the generic type of Person.
class BirthGiver<T: Person<U>, U: Child> {
let child: U.Type
let person: Person<U>.Type
init(person: T.Type, child: U.Type) {
self.person = person
self.child = child
}
}
And I need to call it very ugly (duplicate code, twice Child, possible type unsafe?):
BirthGiver(person: Person<Child>.self, child: Child.self)
I feel like this should work
class BirthGiver<T: Person<U>> {
let child: Person.T
let person: Person<U>.Type
init(person: T.Type) {
self.person = person
self.child = person.U
}
}
So I can call it like this:
BirthGiver(person: Person<Child>.self)
But this doesn't compile, since there is no such type as U. I understand that, but is there any workaround? Repeating the generics all the time just doesn't feel right.
After running this line
let person = Person<Any>()
person represents this static class
class Person {
func printMyType() {
print(Any.self)
}
}
At the moment the class is instantiated T is (fixed) replaced by the type within the angle brackets
I am trying to practice "class with generic". I encountered 2 errors:
Generic parameter 'T' could not be inferred
Reference to generic type 'GenericObject' requires arguments in <...>
The 2 errors in GenericManager class. Please reference the following code. How do I solve this issue?
class User {
var name: String
init(name: String) {
self.name = name
}
}
class Employee {
var name: String
var position: String
init(name: String, position: String) {
self.name = name
self.position = position
}
}
class GenericObject<T> {
var items = [T]()
init(forType: T.Type) {}
func addObject(_ obj: T) {
self.items.append(obj)
}
}
class GenericManager {
//issue: Generic parameter 'T' could not be inferred
var objects = [GenericObject]()
//issue: Reference to generic type 'GenericObject' requires arguments in <...>
func addObject(_ obj: GenericObject) {
self.objects.append(obj)
}
}
let u = User(name: "User")
let uo = GenericObject(forType: User.self)
uo.addObject(u)
let e = Employee(name: "Employee", position: "session manager")
let eo = GenericObject(forType: Employee.self)
eo.addObject(e)
let manager = GenericManager()
manager.addObject(uo)
manager.addObject(eo)
The compiler needs to know the type of T, and in this case you haven't supplied it.
You can do it like this:
var objects = [GenericObject<YourTypeHere>]()
For example, if GenericObject will hold an array of Int, it would look like this:
var objects = [GenericObject<Int>]()
I noticed you updated your question. It would be helpful to know what you're trying to achieve, but I'll try to help you anyway.
When you have a generic object, you need to tell the compiler the type of the generic at compile time, that's why it's complaining that the type can't be inferred, it needs to know.
Since you want to be able to add objects to the GenericManager array, you need the generic in those two cases to be the same, so you can modify your class like this:
class GenericManager<T> {
var objects = [GenericObject<T>]()
func addObject(_ obj: GenericObject<T>) {
self.objects.append(obj)
}
}
However, since the objects have to be of the same generic, you can't add a GenericObject<User> and GenericObject<Employee> to the same manager, what you can do is to implement those as GenericObject<Any>, and do the same with the GenericManager, then it will look like this:
let u = User(name: "User")
let uo = GenericObject(forType: Any.self)
uo.addObject(u)
let e = Employee(name: "Employee", position: "session manager")
let eo = GenericObject(forType: Any.self)
eo.addObject(e)
let manager = GenericManager<Any>()
manager.addObject(uo)
manager.addObject(eo)
Keep in mind that this will lose you any advantage that generics would do, what you could do is to create a protocol or common superclass and use that instead of Any, but that depends on what you're trying to achieve.
If you have any further questions, please add a comment instead of silently updating your question.
The problem you are having is that you are trying to use generics, but want to ignore that in GenericManager and store references to objects of different types.
Consider this - when you call manager.objects[0] what would you expect to be returned?
You can solve this by type-erasure using Any as EmilioPelaez suggested. However this is often a codesmell which leads to casting hacks throughout your code.
One alternative would be to use an enum to specify the different types of data you want to represent:
enum GenericObject {
case users([User])
case employees([Employee])
}
...
let uo = GenericObject.users([ u ])
...
let eo = GenericObject.employees([ e ])
Now when you access the properties inside GenericManager you would be required to switch over the different supported types, and when you add a new type you would be required to implement code whenever you use a GenericObject
Long time listener, first time caller.
I'm getting the following error:
Cannot convert value of type MyClass<Model<A>, OtherClass> to expected argument type MyClass<Protocol, OtherClass>
Despite the fact that MyClass<T> conforms to Protocol
I've attached a snippet that can be run in Playgrounds that resembles what I am actually trying to achieve.
protocol DisplayProtocol {
var value: String { get }
}
class DataBundle<T: CustomStringConvertible>: DisplayProtocol {
var data: T
var value: String {
return data.description
}
init(data: T) {
self.data = data
}
}
class Mapper<DisplayProtocol, Data> {
// ...
}
class MapperViewModel<Data> {
let mapper: Mapper<DisplayProtocol, Data>
init(mapper: Mapper<DisplayProtocol, Data>) {
self.mapper = mapper
}
}
let dataBundle = DataBundle<Int>(data: 100)
let mapper = Mapper<DataBundle<Int>, Bool>()
let viewModel = MapperViewModel<Bool>(mapper: mapper) // <- This fails w/error
Is this the expected behavior? If it is it feels like its breaking the contract of allowing me to have the DisplayProtocol as a type in Mapper.
This is caused by the fact that Swift generics are invariant in respect to their arguments. Thus MyClass<B> is not compatible with MyClass<A> even if B is compatible with A (subclass, protocol conformance, etc). So yes, unfortunately the behaviour is the expected one.
In your particular case, if you want to keep the current architecture, you might need to use protocols with associated types and type erasers.
I try to call a class method on a generic T: BaseModel where T can be a subclass of BaseModel.
For example Car.
In the case T should be Car, I want my class method to be called on the Car class.
However, It always ends up calling the BaseModel class method instead.
class func parse<T: BaseModel>(json: JSON, context: NSManagedObjectContext) throws -> T? {
return T.classParseMethod(json: json) //This never calls the Car.classParseMethod()
}
where
let carObject = parse(json:json, context:context) as? Car
Any help?
The casting is done after the function call so the generic constraint resolves to T = BaseModel. You want the function to know of the type so it can properly resolve the generic constraint:
func parse<T: BaseModel>(_ str: String) -> T? {
print(T.Type.self) // should print like: Car.Type
return T.parse(str) as? T
}
// Make the desired type known to swift
let car: Car? = parse("My Car Format String")
One solution that seems to work is:
to add
func myFunc<T: BaseModel>(_ type: T.Type,..) -> T? {
type.aClassFunc()
{
If I call the following, it works.
if let obj = myFunc(Car.self, ...) {
// obj will be of type Car
}
It seems really too much just to achieve this but there might be an underlying reason for it.
Building on previous question which got resolved, but it led to another problem. If protocol/class types are stored in a collection, retrieving and instantiating them back throws an error. a hypothetical example is below. The paradigm is based on "Program to Interface not an implementation" What does it mean to "program to an interface"?
instantiate from protocol.Type reference dynamically at runtime
public protocol ISpeakable {
init()
func speak()
}
class Cat : ISpeakable {
required init() {}
func speak() {
println("Meow");
}
}
class Dog : ISpeakable {
required init() {}
func speak() {
println("Woof");
}
}
//Test class is not aware of the specific implementations of ISpeakable at compile time
class Test {
func instantiateAndCallSpeak<T: ISpeakable>(Animal:T.Type) {
let animal = Animal()
animal.speak()
}
}
// Users of the Test class are aware of the specific implementations at compile/runtime
//works
let t = Test()
t.instantiateAndCallSpeak(Cat.self)
t.instantiateAndCallSpeak(Dog.self)
//doesn't work if types are retrieved from a collection
//Uncomment to show Error - IAnimal.Type is not convertible to T.Type
var animals: [ISpeakable.Type] = [Cat.self, Dog.self, Cat.self]
for animal in animals {
//t.instantiateAndCallSpeak(animal) //throws error
}
for (index:Int, value:ISpeakable.Type) in enumerate(animals) {
//t.instantiateAndCallSpeak(value) //throws error
}
Edit - My current workaround to iterate through collection but of course it's limiting as the api has to know all sorts of implementations. The other limitation is subclasses of these types (for instance PersianCat, GermanShepherd) will not have their overridden functions called or I go to Objective-C for rescue (NSClassFromString etc.) or wait for SWIFT to support this feature.
Note (background): these types are pushed into array by users of the utility and for loop is executed on notification
var animals: [ISpeakable.Type] = [Cat.self, Dog.self, Cat.self]
for Animal in animals {
if Animal is Cat.Type {
if let AnimalClass = Animal as? Cat.Type {
var instance = AnimalClass()
instance.speak()
}
} else if Animal is Dog.Type {
if let AnimalClass = Animal as? Dog.Type {
var instance = AnimalClass()
instance.speak()
}
}
}
Basically the answer is: correct, you can't do that. Swift needs to determine the concrete types of type parameters at compile time, not at runtime. This comes up in a lot of little corner cases. For instance, you can't construct a generic closure and store it in a variable without type-specifying it.
This can be a little clearer if we boil it down to a minimal test case
protocol Creatable { init() }
struct Object : Creatable { init() {} }
func instantiate<T: Creatable>(Thing: T.Type) -> T {
return Thing()
}
// works. object is of type "Object"
let object = instantiate(Object.self) // (1)
// 'Creatable.Type' is not convertible to 'T.Type'
let type: Creatable.Type = Object.self
let thing = instantiate(type) // (2)
At line 1, the compiler has a question: what type should T be in this instance of instantiate? And that's easy, it should be Object. That's a concrete type, so everything is fine.
At line 2, there's no concrete type that Swift can make T. All it has is Creatable, which is an abstract type (we know by code inspection the actual value of type, but Swift doesn't consider the value, just the type). It's ok to take and return protocols, but it's not ok to make them into type parameters. It's just not legal Swift today.
This is hinted at in the Swift Programming Language: Generic Parameters and Arguments:
When you declare a generic type, function, or initializer, you specify the type parameters that the generic type, function, or initializer can work with. These type parameters act as placeholders that are replaced by actual concrete type arguments when an instance of a generic type is created or a generic function or initializer is called. (emphasis mine)
You'll need to do whatever you're trying to do another way in Swift.
As a fun bonus, try explicitly asking for the impossible:
let thing = instantiate(Creatable.self)
And... swift crashes.
From your further comments, I think closures do exactly what you're looking for. You've made your protocol require trivial construction (init()), but that's an unnecessary restriction. You just need the caller to tell the function how to construct the object. That's easy with a closure, and there is no need for type parameterization at all this way. This isn't a work-around; I believe this is the better way to implement that pattern you're describing. Consider the following (some minor changes to make the example more Swift-like):
// Removed init(). There's no need for it to be trivially creatable.
// Cocoa protocols that indicate a method generally end in "ing"
// (NSCopying, NSCoding, NSLocking). They do not include "I"
public protocol Speaking {
func speak()
}
// Converted these to structs since that's all that's required for
// this example, but it works as well for classes.
struct Cat : Speaking {
func speak() {
println("Meow");
}
}
struct Dog : Speaking {
func speak() {
println("Woof");
}
}
// Demonstrating a more complex object that is easy with closures,
// but hard with your original protocol
struct Person: Speaking {
let name: String
func speak() {
println("My name is \(name)")
}
}
// Removed Test class. There was no need for it in the example,
// but it works fine if you add it.
// You pass a closure that returns a Speaking. We don't care *how* it does
// that. It doesn't have to be by construction. It could return an existing one.
func instantiateAndCallSpeak(builder: () -> Speaking) {
let animal = builder()
animal.speak()
}
// Can call with an immediate form.
// Note that Cat and Dog are not created here. They are not created until builder()
// is called above. #autoclosure would avoid the braces, but I typically avoid it.
instantiateAndCallSpeak { Cat() }
instantiateAndCallSpeak { Dog() }
// Can put them in an array, though we do have to specify the type here. You could
// create a "typealias SpeakingBuilder = () -> Speaking" if that came up a lot.
// Again note that no Speaking objects are created here. These are closures that
// will generate objects when applied.
// Notice how easy it is to pass parameters here? These don't all have to have the
// same initializers.
let animalBuilders: [() -> Speaking] = [{ Cat() } , { Dog() }, { Person(name: "Rob") }]
for animal in animalBuilders {
instantiateAndCallSpeak(animal)
}