Tailor forced casts conflicts - ios

let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! CustomTableViewCell
It's standard sentence that implements the table view's cell's properties. But Tailor (it's a Swift analyzer/linter) warns about you shouldn't forced the CustomTableViewCell as as! If I used to as as?, I have to implement cell's properties as cell!. But Tailor don't warn about [forced-type-cast] Force casts should be avoided. What's the reason of this? How can I implement cell's without unwrap of cell as cell! What's the correct programming paradigms for forced casts operations in Swift?

I am not familiar with "Tailor" but most likely the reason it is giving you this warning is because if a force cast fails then obviously your program will crash and thats never good.
The as! operator does have its place if you are 100% sure that what you are casting is of that type. But, even then its better to be safe than sorry and you should use a guard or if let statement instead in order to handle a failed cast.
if let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as? CustomTableViewCell {
//do what you like with cell
}
or
guard let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as? CustomTableViewCell else {
//abort current scope, return, break, etc. from scope.
}
//do what you like with cast cell

Related

What action when tableview can't dequeue a cell

When I was learning about tableviews the tutorials I followed used the following code-
guard let cell = tableView.dequeueReusableCell(withIdentifier: Self.reminderListCellIdentifier, for: indexPath) as? ReminderListCell else {
return UITableViewCell()
}
I just came across example code from Apple which is;
guard let cell = tableView.dequeueReusableCell(withIdentifier: Self.reminderListCellIdentifier, for: indexPath) as? ReminderListCell else {
fatalError("Unable to dequeue ReminderCell")
}
What should I implement? fatalError causes a crash I believe. Is the the desired behaviour?
None of the suggestions. Force unwrap the cell
let cell = tableView.dequeueReusableCell(withIdentifier: Self.reminderListCellIdentifier, for: indexPath) as! ReminderListCell
In practice it causes the same behavior as the fatalError.
The code must not crash if everything is hooked up correctly. The potential mistake is a design mistake.
The first snippet is pretty silly because in case of the mentioned design mistake nothing will be displayed and you have no idea why.
Force unwrapping is not evil per se.

Swift: Cast tableCell as variable class

I'm trying to refine some working but ugly code.
My app has five TableViews, each one displaying a different type of data (with different cell layouts). Because the datatypes are similar-ish and require many similar methods (for downloading, encoding, etc), I have set up a TableViewController:UITableViewController class to serve as a superclass for the five TableViewController subclasses. Within this superclass, I have the standard "cellForRowAt" method, but it's bloated and repetitive. I want to simplify it.
My problem (I think) is the multiple "let cell = " statements, which all cast as a different type of TableViewCell depending on the datatypes. For example, my DataType.SCHEDULES datatype needs to get a SchedulesTableViewCell with reuseID of "SchedulesCell". I can't make them all the same TableViewCell class, because they each have their own IBOutlet views.
Making things uglier, each tableView has two cell prototypes, and I need to be able to generate an ARTICLE cell and a DETAIL cell for each datatype.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// get the article and row type
let article = getArticleFor(indexPath: indexPath)
let cellType = getCellTypeFor(indexPath: indexPath)
// create either an ARTICLE row or a DETAIL row.
// (simplified for SO posting. Each "case" is actually
// 5-6 lines of nearly identical code)
switch cellType {
// for the ARTICLE cell prototype
case CellType.ARTICLE:
// get the right table cell matching the datatype
switch self.datatype {
case DataType.SCHEDULES:
let cell = tableView.dequeueReusableCell(withIdentifier: "SchedulesCell") as! SchedulesTableViewCell
cell.fillCellWith(article: article)
cell.otherMethod2()
cell.otherMethod3()
return cell
case DataType.LUNCH:
let cell = tableView.dequeueReusableCell(withIdentifier: "LunchCell") as! LunchTableViewCell
cell.fillCellWith(article: article)
cell.otherMethod2()
cell.otherMethod3()
return cell
case DataType.EVENTS:
let cell = tableView.dequeueReusableCell(withIdentifier: "EventsCell") as! EventsTableViewCell
cell.fillCellWith(article: article)
cell.otherMethod2()
cell.otherMethod3()
return cell
case DataType.DAILY_ANN:
let cell = tableView.dequeueReusableCell(withIdentifier: "DailyannCell") as! DailyannTableViewCell
cell.fillCellWith(article: article)
cell.otherMethod2()
cell.otherMethod3()
return cell
case DataType.NEWS:
let cell = tableView.dequeueReusableCell(withIdentifier: "NewsCell") as! NewsTableViewCell
cell.fillCellWith(article: article)
cell.otherMethod2()
cell.otherMethod3()
return cell
}
// or for the DETAIL cell prototype
case CellType.DETAIL:
// get the right table cell matching the datatype
switch self.datatype {
case DataType.SCHEDULES:
let cell = tableView.dequeueReusableCell(withIdentifier: "SchedulesDetailsCell") as! ScheduleDetailTableViewCell
cell.fillCellWith(article: article)
cell.otherMethod2()
cell.otherMethod3()
return cell
case DataType.LUNCH:
let cell = tableView.dequeueReusableCell(withIdentifier: "LunchDetailsCell") as! LunchDetailsTableViewCell
cell.fillCellWith(article: article)
cell.otherMethod2()
cell.otherMethod3()
return cell
case DataType.EVENTS:
let cell = tableView.dequeueReusableCell(withIdentifier: "EventsDetailsCell") as! EventsDetailTableViewCell
cell.fillCellWith(article: article)
cell.otherMethod2()
cell.otherMethod3()
return cell
case DataType.DAILY_ANN:
let cell = tableView.dequeueReusableCell(withIdentifier: "DailyannDetailCell") as! DailyannDetailsTableViewCell
cell.fillCellWith(article: article)
cell.otherMethod2()
cell.otherMethod3()
return cell
case DataType.NEWS:
let cell = tableView.dequeueReusableCell(withIdentifier: "NewsDetailCell") as! NewsDetailTableViewCell
cell.fillCellWith(article: article)
cell.otherMethod2()
cell.otherMethod3()
return cell
}
}
}
I originally had each "let cell =" case within the subclasses' own "cellForRowAt" methods, but I was repeating very similar code in every subclass, which seemed silly. On the other hand, the code above moved the repetition into a single class, but didn't remove the repetition, so it's still silly, but in a different place.
I feel like if I could make a dictionary of classes, something like this...
let tableCellClasses = [DataType.SCHEDULES : ScheduleTableViewCell,
DataType.LUNCH : LunchTableViewCell
etc.
...then I could make my "let cell = " statements more generic, like...
let cell = tableView.dequeueReusableCell(withIdentifier: identifier[dataType]) as! tableCellClasses[dataType]
but can't seem to find a way to make it work.
As I said, it works but it's ugly. I work in a high school, so I'd like for students viewing the repo to see clean, well-structured code -- so I'm shooting for better than just "it works."
Any suggestions?
You could use Swift's meta types and what not, but looking at your code, all your cell subclasses share the same methods:
cell.fillCellWith(article: article)
cell.otherMethod2()
cell.otherMethod3()
Why not:
Have a base class from which all custom cell classes inherit, that implements the above interface (the three methods you use after dequeuing a cell, with the possibility of them being overriden on each concrete subclass), so dequeue once and force-cast into the base type (I believe the right implementation of the methods will be executed, for each subclass. The cast is only to make the compiler happy: UITableViewCell does not have those methods).
Have a switch clause on the data type that gives you the specific cell identifier
Have each prototype cell set to the specific class on the storyobard, and assign the specific identifier too.
Does it make sense?
Now, form looking at your code, it doesn't look like you really need different subclasses. It's perfectly okay to have several different protoypes of the same UITableViewCell subclass, each with a different subview layout and a different reuse identifier, as long as they all can work with the same number and type of subviews and other custom properties/methods.
There is no way to do what you assume. You have to casting the classes one by one for your need. Or you can use base class which implemented all methods you need and calling them by the datatype.

How to define a weak variable within a Swift guard statement

How do I change the following closure so that cell is 'weak'? :
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath as IndexPath) as? PlayerTableViewCell else {
fatalError("The dequeued cell is not an instance of PlayerTableViewCell")
}
I am sure there is a simple way to achieve this but I have been unable to determine the correct way to handle this.
Thanks
A duplicate call to dequeueReusableCell was causing a memory consumption problem, which led me to think I had a strong reference that needed to be weakened.
Removing this erroneous extra call solved the underlying issue I thought needed a fix.

Force downcast when programming a custom UITableViewCell in Swift

In the following code:
let cell = tableView.dequeueReusableCell(
withIdentifier: "MyCell",
for: indexPath
) as MyTableViewCell // 'UITableViewCell' is not convertible to 'MyTableViewCell'; did you mean to use 'as!' to force downcast?
We have the error complaining that UITableViewCell is not convertible to a MyTableViewCell.
So the compiler suggests to do a force cast:
let cell = tableView.dequeueReusableCell(
withIdentifier: "MyCell",
for: indexPath
) as! MyTableViewCell // ?!?!?!
However, that feels ugly.
Is there no alternative to force casting when dealing with tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)? Is this really the most idiomatic way to accomplish this in Swift?
Thanks!
Is this really the most idiomatic way
Absolutely. It's perfectly standard.
You could downcast safely like this:
if let cell = tableView.dequeueReusableCell(
withIdentifier: "MyCell",
for: indexPath
) as? MyTableViewCell {
But this is one situation where I don't think that's worth doing, because if this turns out not to be a MyTableViewCell, there's a very real sense in which you want to crash.
You can do it like this:
guard let cell = tableView.dequeueReusableCell(
withIdentifier: "MyCell",
for: indexPath
) as? MyTableViewCell else {
// Log an error, or fatalError("Wrong cell type."), etc.
// or maybe return UITableViewCell()
}

Ambiguous reference to member 'tableView' when dequeuing a cell

I'm running into an odd error. This line of code functions correctly:
let cell = self.tableView.dequeueReusableCellWithIdentifier("FeedCell", forIndexPath: indexPath) as UITableViewCell!
However when I cast to FeedCell! (a subclass of UITableViewCell):
let cell = self.tableView.dequeueReusableCellWithIdentifier("FeedCell", forIndexPath: indexPath) as FeedCell!
Xcode throws the error: Ambiguous reference to member 'tableView'
I'm not sure how it can be ambiguous, let alone triggered by a different casting!
Try changing the line to
let cell = tableView.dequeueReusableCellWithIdentifier("FeedCell", forIndexPath: indexPath) as! FeedCell
Just remove the ! from FeedCell! and put it in as! and check if there is any difference.
Beside as #redent84 mentioned in his comment you should use tableView instead of self.tableView.

Resources