Ondrej Rafaj
Ondrej Rafaj

Reputation: 4417

Generic class type doesn't conform to Any

I have a problem with storing my generic classes in an array. How should I format the type for my array while keeping the reference to the original type (I know I could do var myClasses: [Any] = [] but that wouldn't be helpful when retrieving the variable from my array :(

Example is below:

import UIKit

protocol Reusable { }

extension UITableViewCell: Reusable { }
extension UICollectionViewCell: Reusable { }

class SomeClass<T> where T: Reusable {
    init() { }
}

var myClasses: [SomeClass<Reusable>] = []

myClasses.append(SomeClass<UITableViewCell>())
myClasses.append(SomeClass<UICollectionViewCell>())
myClasses.append(SomeClass<UITableViewCell>())
myClasses.append(SomeClass<UICollectionViewCell>())

Edit: Just to clarify, I have used the collection and table cells as an example, I am not actually planning on storing them together :)

Edit 2 var myClasses: [SomeClass<Reusable>] = [] generates error: using 'Reusable' as a concrete type conforming to protocol 'Reusable' is not supported

Edit 3 var myClasses: [SomeClass<AnyObject>] = [] generates error: type 'AnyObject' does not conform to protocol 'Reusable'

Upvotes: 2

Views: 150

Answers (4)

AleX
AleX

Reputation: 167

My suggestion is adding parent protocol SomeClass Container for your SomeClass generic. Then put an array of SomeClass objects inside SomeClass.

protocol Reusable { func cakePlease() }

extension UITableViewCell: Reusable {
    func cakePlease() { }
}

extension UICollectionViewCell: Reusable {
    func cakePlease() { }
}

protocol SomeClassContainer { 
    func teaPlease()
    func afternoonPlease()
}

class SomeClass<T: Reusable>: SomeClassContainer {
    var item: T?

    init() { }

    func teaPlease() { }

    func afternoonPlease() {
        teaPlease()
        item?.cakePlease()
    }
}

var myClasses = [SomeClassContainer]()
myClasses.append(SomeClass<UITableViewCell>())
myClasses.append(SomeClass<UICollectionViewCell>())
myClasses.append(SomeClass<UITableViewCell>())
myClasses.append(SomeClass<UICollectionViewCell>())
myClasses[0].teaPlease()

if let item = (myClasses[0] as? SomeClass<UITableViewCell>)?.item {
    item.cakePlease()
}

for myClass in myClasses {
    if let tableCell = (myClass as? SomeClass<UITableViewCell>)?.item {
        tableCell.cakePlease()
    } else if let collectionCell = (myClass as SomeClass<UICollectionViewCell>)?.item {
        collectionCell.cakePlease()
    }
}

myClasses.forEach({ $0.afternoonPlease() })

Upvotes: 1

Eugene Laminskiy
Eugene Laminskiy

Reputation: 704

You should use array of AnyObject in your case. Because as you know swift is strong typed language and for example

 SomeClass<UITableViewCell>

and

SomeClass<UICollectionViewCell>

are different types of objects. As for example Array< Int > and Array< String >, they are both arrays, but still it's a different types of objects. So in this case you'll have to use declaration:

var myClasses: [AnyObject] = []

and check type of object or typecast them every time you'll need.

if (myClasses[0] is SomeClass<UICollectionViewCell>) { do something }

or

if let cell = myClasses[0] as? SomeClass<UICollectionViewCell> { do something }

Upvotes: 1

pacification
pacification

Reputation: 6018

I think you can create some sort of Holder class that can accept and retrieve your object:

class Holder {

    lazy var classes: [Any] = []

    func get<T>(_ type: T.Type) -> [T]? {
        return classes.filter({ $0 is T }) as? [T]
    }

}

And the main part:

let holder = Holder()
holder.classes.append(SomeClass<UITableViewCell>())

if let someTableViewCells = holder.get(SomeClass<UITableViewCell>.self)?.first {
    // or filter out again to get specific SomeClass of UITableViewCell
    print(someTableViewCells)
}  

Or without holder class:

var classes: [Any] = []
classes.append(SomeClass<UITableViewCell>())

if let someTableViewCell = classes.filter({ $0 is SomeClass<UITableViewCell> }).first as? SomeClass<UITableViewCell> {
    print(someTableViewCell)
}

Upvotes: 1

Jacob King
Jacob King

Reputation: 6159

Generally the way to type your array would be to go as specific as possible whilst still covering all bases.

What I mean by this for example is storing an array of UIViewController objects, even though each will actually be a different type. You wouldn't use Any here because you really don't need to be that general.

In your case, why not use Reusable? Since all your generic classes conform to it anyway, it is as general as you can go whilst still maintaining convenience.

Upvotes: 0

Related Questions