Roi Mulia
Roi Mulia

Reputation: 5896

Cast object using type stored in a parameter

How can I cast an object using a type (stored in a parameter)?

Example:

Let's assume I have a simple enum like so:

enum SettingsAction {
    case contactUs

    var cellType: UITableViewCell.Type {
        return TitleSettingsTableViewCell.self
    }
}

And then I'm trying to create an object with cellType, like so:

let settingsAction = SettingsAction.contactUs
let cellType = settingsAction.cellType
let cell = tableView.dequeueReusableCell(withIdentifier: "\(cellType)", for: indexPath) as! cellType

I'm getting the following error:

Cannot find type 'cellType' in scope

Seems like it's impossible to use a parameter to cast, resulting in some weird pre-complier error.

Is there a right way to achieve what I'm trying?

Upvotes: 1

Views: 165

Answers (2)

Tarun Tyagi
Tarun Tyagi

Reputation: 10112

Try something like this -

enum SettingsAction {
    case contactUs

    func cellForRow(at indexPath: IndexPath, in tableView: UITableView) -> UITableViewCell {
        switch self {
        case .contactUs:
            return tableView.dequeueReusableCell(withIdentifier: "TitleSettingsTableViewCell", for: indexPath) as! TitleSettingsTableViewCell
        }
    }
        
}

Update Swift won't allow you to work with Type information stored in a variable that's only available at runtime. This information is needed at compile time for the type inference. You have to give up flexibility in one way or another. Here's another attempt at solving the same problem - far from ideal - it does work as needed for this use case.

class UserInfoTableViewCell: UITableViewCell {
    var userProperty: Bool = false
}
class ContactUsTableViewCell: UITableViewCell {
    var contactProperty: Bool = false
}
class LegalTableViewCell: UITableViewCell {
    var legalProperty: Bool = false
}

class SettingsCellCreator<T: UITableViewCell> {
    func cellForRow(at indexPath: IndexPath, in tableView: UITableView) -> T {
        tableView.dequeueReusableCell(withIdentifier: "\(T.self)", for: indexPath) as! T
    }
}

private let userInfoCellCreator = SettingsCellCreator<UserInfoTableViewCell>()
private let contactUsCellCreator = SettingsCellCreator<ContactUsTableViewCell>()
private let legalCellCreator = SettingsCellCreator<LegalTableViewCell>()

class ViewController: UIViewController, UITableViewDataSource {
    
    enum CellType {
        case userInfo
        case contactUs
        case legal
    }
    
    private var rows: [CellType] = [
        .userInfo,
        .contactUs,
        .contactUs,
        .legal,
        .legal
    ]
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        rows.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        switch rows[indexPath.row] {
        case .userInfo:
            let cell = userInfoCellCreator.cellForRow(at: indexPath, in: tableView)
            cell.userProperty = true
            return cell
            
        case .contactUs:
            let cell = contactUsCellCreator.cellForRow(at: indexPath, in: tableView)
            cell.contactProperty = true
            return cell
            
        case .legal:
            let cell = legalCellCreator.cellForRow(at: indexPath, in: tableView)
            cell.legalProperty = true
            return cell
        }
    }
    
}

Upvotes: 1

Blazej SLEBODA
Blazej SLEBODA

Reputation: 9925

In Swift the right side of downcasting as and type checking is operators must be given in place of use of those operators.

So, it's impossible to cast or type check an object using a meta type stored or computed.

Upvotes: 1

Related Questions