Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift equivalent for java return type : Class<? extends MyClass<?>>

My question is about a complex use case of generics in Swift. I'll try to explain as clearly as possible.

User of my iOS chat app can send different type of messages : text, images, gif, questions etc. The class design of the data holding these messages is :

  • an "abstract" class Message that hold info that all type of message have (date, sender etc)
  • final subclasses of Message, like TextMessage or ImageMessage that hold the specific info for each type of message (fileName for ImageMessage, text for TextMessage etc.)

When a message is retrieved from server, it has a JSON prop called service, which I decode to a case of a Service enum. Each Service case has (1) one data class, and (2) one cell class. The data class is used to store the right T:Message class, the cell class is prompted by the collectionView to know which type of cell it needs to dequeue depending on the message service. Here is my Service enum :

public enum Service : String, CaseIterable
{
    case CHAT = "CHAT"
    case IMAGE = "IMAGE"
    // ...
    

    public func getSubclass() -> Message.Type {
        switch(self){
            case .TEXT                   : return TextMessage.self
            case .IMAGE                : return ImageMessage.self
            // ...
        }
    }
    
    /* problem here */ 
    public func getCellClass <T> () -> MessageCollectionViewCell<T>.Type {
        switch(self){
            case .CHAT : return MessageCollectionViewCellChat.self 
            case .SENDING : return MessageCollectionViewCellChat.self 
      
        }
    }
}

The class design for the cells is :

  • An abstract class called MessageCollectionViewCell<T:Message> that create the actual bubble view, with the date under it. A function recycle is declared in this class, to be overriden by final sub cells.
  • The final subcell classes : MessageCollectionViewCellText, MessageCollectionViewCellImage etc., children of MessageCollectionViewCell<T:Message>. T needs to be the data type associated with the cell. They override the method recycle() so each type of the message can recycle in its own way (changing the text in the label for TextMessages, change the bitmap in the ImageView for ImageMessage..)

Here is the code, so it's more clear :

class MessageCollectionViewCell<T:Message> : UICollectionViewCell {
    // sub cells needs to override this identifier (used by the CollectionView to register cell)
    class var cellIdentifier:String { return "MessageCollectionViewCell" }
    
    init() {
        // init chat bubble UI here..
    }

    public func recycle(withMessage: T) {
        // UI actions to recycle the message that are common to all messages type ..
    }
}

class MessageCollectionViewCellText : MessageCollectionViewCell<TextMessage>
{
    override class var cellIdentifier:String { return "MessageCollectionViewCellText" }
    
    let label = UILabel()
    override init(frame:CGRect) {
        super.init(frame:frame)
        
        // create bubble content (put a label in the bubble, in this particular case of TextMessage)
    }
    
    
    override func recycle(message msgText: TextMessage){
         // do all the things that are common to all message types (change date label for example)
        super.setMessage(message: msg)
        
        // specific things to do for TextMessage
        label.text = msgText.text
    }
    
}

The problem is the function returning the cell type in Service enum. In my Android app (Java), the return type is : Class<? extends MessageCollectionViewCell<?>>. Here, for the code I shared of getCellClass function above, Swift says "Cannot convert return expression of type 'MessageCollectionViewCellText.Type' to return type 'MessageCollectionViewCell<T>.Type'"

I also tried this :

// MT for Message Type, CT for Cell Type 
public func getCellClass<MT:Message, CT:MessageCollectionViewCell<MT>> () -> CT.Type
{
    switch(self) {
        case .CHAT  : return MessageCollectionViewCellChat.self as! CT.Type
        case .IMAGE : return MessageCollectionViewCellImage.self as! CT.Type
    }
}

It compiles properly without warning, but the app crashes because it cannot cast to CT.Type. Has someone an idea to implement this function ?

Thanks for reading

like image 329
Jerem Lachkar Avatar asked Mar 22 '26 02:03

Jerem Lachkar


1 Answers

Instead of working with types the possible solution is to work with protocols, and then it is possible to avoid generics type mismatch problem in your code, like (with some replications)

public protocol Message {}
public struct TextMessage: Message {}
public struct ImageMessage: Message {}

public protocol MessageCollectionViewCell {}
public class MessageCollectionViewCellChat: MessageCollectionViewCell {}

public enum Service : String, CaseIterable
{
    case CHAT = "CHAT"
    case IMAGE = "IMAGE"
    // ...
    

    public func getMessage() -> Message {
        switch(self){
            case .CHAT : return TextMessage()
            case .IMAGE : return ImageMessage()
            // ...
        }
    }
    
    /* problem here */
    public func getCell() -> MessageCollectionViewCell {
        switch(self){
            case .CHAT : return MessageCollectionViewCellChat()
            case .IMAGE : return MessageCollectionViewCellChat()
      
        }
    }
}
like image 109
Asperi Avatar answered Mar 24 '26 22:03

Asperi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!