Brynjar
Brynjar

Reputation: 1262

Array of generics with metatypes (using AlamofireObjectMapper)

I suspect I may be making the same mistake as described by Rob in this post here in that I should be doing this whole thing another way, but with that in mind:

I'm trying to use AlamofireObjectMapper in a generic way. It has a protocol

public protocol Mappable

I then have various model classes that adopt it

class Dog: Mappable
class Cat: Mappable
class Bird: Mappable

I have this method

func loadEntityArray<T: Mappable>(type: T.Type)

and the reason this is generic is because it calls a function load() that needs a completion block that uses this generic param. The 'type' argument is never actually used, but you can't make a func generic without the generic type being in the func's parameter list.

func load(completion:(Response<T, NSError> -> Void))

loadEntityArray is called from another method

func letsgo() { loadEntityArray(Dog.self); loadEntityArray(Cat.self) }

So far so good, this all works. But I want to pass an array of which models to load to letsgo() and I can't work out how to do this. If I change letsgo() to

func letsgo<T:Mappable>(models: [T.Type]) {
  for mod in models {
    loadEntityArray(mod)
  }
}

and then call letsgo() with 1 param like

letsgo([Dog.self])

it works, but as soon as I have an array of 2 or more, I get a compiler error 'cannot convert value of type NSArray to expected argument type [_.Type]' I don't now how I would explicitly type this array either.

letsgo([Dog.self, Cat.self])

I've tried various permutations and nothing seems to work. Am I doing something impossible here? It seems to me the compiler has enough information at compile time for this to work, so I'm not sure if this is a syntax thing or I'm doing something wrong here with generics.

Upvotes: 1

Views: 451

Answers (2)

Brynjar
Brynjar

Reputation: 1262

So in the end I came to the conclusion that this is not possible. I changed the code such that I pass in an enum to letsgo() and in a switch statement on that enum I call loadEntityArray(Dog.self) etc. etc. with an explicitly coded call, for each of my possible types. Then the compiler can see all the possible types and is happy.

Upvotes: 0

Jos&#233;
Jos&#233;

Reputation: 471

Looking at your function :

func letsgo<T:Mappable>(models: [T.Type])

Its model parameter should be an Array of all the same Type. So an array of only Dog Types for example. That's why

letsgo([Dog.self])

works but

letsgo([Dog.self, Cat.self])

won't as it has multiple Types.

The solution :

Use Mappable.Type directly :

func loadEntityArray(type: Mappable.Type) {}

func letsgo(models: [Mappable.Type]) {
    for mod in models {
        loadEntityArray(mod.self)
    }
}

And cast your array of Types as an array of Mappable Types :

letsgo([Dog.self, Cat.self] as [Mappable.Type])

Hope this achieves what you're looking for !

Upvotes: 1

Related Questions