Nested types in Swift

51 Views Asked by At

I have UICollectionView with cells: Image, Text, or Image+Text.

class ParentCell: UICollectionViewCell {
    class ImageCell: ParentCell {
        class TextCell: ImageCell { }
    }
    class TextCell: ParentCell { }
}
func createTextCellId(classPrefix: ParentCell.Type) -> String {
    return String(reflecting: classPrefix.TextCell)
}

Why Swift erases nested type?

// CORRECT: ParentCell.TextCell
createTextCellId(classPrefix: ParentCell.self)

// INCORRECT: ParentCell.TextCell
createTextCellId(classPrefix: ParentCell.ImageCell.self)

Another functions do not work:

func createTextCellId<ParentCellType: ParentCell>(classPrefix: ParentCellType.Type) -> String {
    return String(reflecting: classPrefix.TextCell)
}
1

There are 1 best solutions below

0
On

That's not how Swift works. classPrefix.TextCell in your code will always mean the TextCell declared in ParentCell, not the one in ImageCell.

To do this kind of dynamically resolution of types, you would need a protocol:

protocol TextCellProviding: UICollectionViewCell {
    associatedtype TextCell: UICollectionViewCell
}

Then you can do:

func createTextCellId<Cell: TextCellProviding>(classPrefix: Cell.Type) -> String {
    return String(reflecting: classPrefix.TextCell)
}

Then your design could be:

class ParentCell: UICollectionViewCell {
    
}

class ImageCell: ParentCell, TextCellProviding {
    typealias TextCell = ImageAndTextCell
}

// You can nest ImageAndTextCell in ImageCell if you want, but I think it is less readable that way
// Perhaps LabelledImageCell could be a better name? Or ImageCell.Labelled
class ImageAndTextCell: ImageCell { }

class TextOnlyCell: ParentCell, TextCellProviding {
    typealias TextCell = TextOnlyCell
}

Note that ParentCell cannot conform to TextCellProviding. Otherwise the same problem happens again - i.e. ParentCell decides what TextCell should be, for all its subclasses, and its subclasses can't change that.

Example:

createTextCellId(classPrefix: TextOnlyCell.self) // TextOnlyCell
createTextCellId(classPrefix: ImageCell.self) // ImageAndTextCell

Also consider whether you actually need ParentCell. If all the common functionalities can be (and makes sense to be) implemented with extensions on TextCellProviding, consider doing that instead.