Блог :. Избавляемся от текстовых id для ячеек и вьюконтроллеров

Избавляемся от текстовых id для ячеек и вьюконтроллеров

18 Oct, 2018

Недавно затащил в проект SwiftLint, чтобы код как можно ближе соответствовал Swift Style Guide, ну чтобы в общем был по всем канонам. Куча ворнингов, и довольно часто встречается вот такой:

Line Length Violation: Line should be 120 characters or less: currently 139 characters (line_length)

Вылазит для куска кода, где в wireframe инстанциируется View-слой для VIPER-модуля. Аналогично и для получения ячейки по идентификатору из UICollectionView / UITableView

// for storyboard
let viewController = storyboard.instantiateViewController(withIdentifier: "MyViperModuleViewController") as! MyViperModuleViewController
// for collection view
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyViperModuleCollectionViewCell", for: indexPath) as! MyViperModuleCollectionViewCell

Казалось бы, куда уж короче, ведь строчку некуда сокращать. Нашлась достаточно удобная оптимизация, позволяющая сделать код более читаемым, а также избавиться от строковых констант, для которых автокомплит не сработает. Кроме повышения читаемости кода, исключаем риск опечататься в строковом идентификаторе, который задан в Storyboard


Глядя на код выше, мы-таки можем что-то сделать, чтобы не писать 2 раза 2 раза MyViperModuleViewController. Как минимум идея заключается в том, чтобы давать названия для вьюконтроллеров в самих сторибоардах ровно так, как они и называются

Для начала научимся получать текстовый идентификатор из имени нужного класса:

protocol Descriptable {
static var id: String { get }
}
extension Descriptable where Self: Any {
static var id: String {
return String(describing: Self.self)
}
}
extension NSObject: Descriptable { }

Ну и сделать укороченные версии методов для UIStoryboard, UICollectionView и UITableView:

extension UICollectionView {
func dequeueCell<T: UICollectionViewCell>(for indexPath: IndexPath) -> T {
return dequeueReusableCell(withReuseIdentifier: T.id, for: indexPath) as! T
}
}
extension UITableView {
func dequeueCell<T: UITableViewCell>(for indexPath: IndexPath) -> T {
return dequeueReusableCell(withIdentifier: T.id, for: indexPath) as! T
}
func dequeueCell<T: UITableViewCell>() -> T {
return dequeueReusableCell(withIdentifier: T.id) as! T
}
}
extension UIStoryboard {
func instantiate<T: UIViewController>() -> T {
return instantiateViewController(withIdentifier: T.id) as! T
}
}

Получаем более читаемый код, в котором опущены id вьюконтроллеров и ячеек:

// collection view cell
let cell: MyViperModuleCollectionViewCell = collectionView.dequeueCell(for: indexPath)
// table view header view
let cell: MyViperModuleTableHeaderCell = tableView.dequeueCell()
// viper module view
let viewController: MyViperModuleViewController = genericStoryboard.instantiate()

??????

PROFIT