I need to convert my model to fit data in tableview cell finding better approach
My Model
class MyResponseModel {
var question: String = ""
var id: String = ""
var answer: String = ""
//Other variables also but not required to display
init(fromDictionary dictionary: [String:Any]) {
self.question = (dictionary["question"] as? String) ?? ""
self.id = (dictionary["id"] as? String) ?? ""
self.answer = (dictionary["answer"] as? String) ?? ""
}
//MARK: Converting model to cell data
func toModel() -> MyTblCellDetail {
return MyTblCellDetail(title:self.question,detail:self.answer)
}
}
//MARK: Initialize cell data with custom model
struct MyTblCellDetail {
var title: String = ""
var detail: String = ""
var isSelected:Bool = false
init(title:String,detail:String) {
self.title = title
self.detail = detail
}
init(data:MyResponseModel) {
self.title = data.question
self.detail = data.answer
}
}
Note: I am reusing same cell to display most of the data so i will use the same method to converting all class model to MyTblCellDetail struct
So my question is which is better way to do the same or any other approach i can use for the same. Thank you!
1 Answer 1
Here is suggestions. Every approach has pros/cons, depending your app architecture and model complexity.
class MyResponseModel {
// I think values in response model should be let instead of var
// By definition response model should be immutable
let question: String
let id: String
let answer: String
// I guess MyResponseModel will be initialized from JSON and you can just conform it to Codable
// Then you don't need this init(from dictionary: [String: Any])
init(from dictionary: [String: Any]) {
self.question = (dictionary["question"] as? String) ?? ""
self.id = (dictionary["id"] as? String) ?? ""
self.answer = (dictionary["answer"] as? String) ?? ""
}
}
struct MyTableCellViewModel {
// I'm not sure what logic do you have, but I think it is better to define properties using let instead of var
let title: String
let detail: String
let isSelected: Bool
init(title: String, detail: String, isSelected: Bool) {
self.title = title
self.detail = detail
self.isSelected = isSelected
}
// Approach #1: create initializer from necessary response model
init(responseModel: MyResponseModel, isSelected: Bool) {
self.init(title: responseModel.question, detail: responseModel.answer, isSelected: isSelected)
}
}
// Approach #2: create separate mapper class (useful when you need to have external dependencies in order to map
// For example you need to add some info that is not relevant to MyResponseModel.
// Let's take cell selection info as an example
protocol MyTableCellSelectionProvider {
func isCellSelected(for responseModel: MyResponseModel) -> Bool
}
struct MyTableCellViewModelMapper {
let cellSelectionProvider: MyTableCellSelectionProvider
func map(responseModel: MyResponseModel) -> MyTableCellViewModel {
return MyTableCellViewModel(
title: responseModel.question,
detail: responseModel.answer,
isSelected: cellSelectionProvider.isCellSelected(for: responseModel)
)
}
}
// Approach #3: define cell view model protocol
protocol MyCellViewModel {
var title: String { get }
var detail: String { get }
}
extension MyResponseModel: MyCellViewModel {
var title: String { return question }
var detail: String { return answer }
}
MyTblCellDetail
entirely with a protocol.MyResponseModel
then conforms to that protocol by providing computed properties calledtable
,detail
andisSelected
. \$\endgroup\$