Is force cast really bad and should always avoid it?
Update
After using Swiftlint for a while, I am now a total convert to the Zero Force-Unwrapping Cult (in line with @Kevin's comment below).
There really isn't any situation where you need to force-unwrap an optional that you can't use if let...
, guard let... else
, or switch... case let...
instead.
So, nowadays I would do this:
for media in mediaArray {
if let song = media as? Song {
// use Song class's methods and properties on song...
} else if let movie = media as? Movie {
// use Movie class's methods and properties on movie...
}
}
...or, if you prefer the elegance and safety of an exhaustive switch
statement over a bug-prone chain of if/else
s, then:
switch media {
case let song as Song:
// use Song class's methods and properties on song...
case let movie as Movie:
// use Movie class's methods and properties on movie...
default:
// Deal with any other type as you see fit...
}
...or better, use flatMap()
to turn mediaArray
into two (possibly empty) typed arrays of types [Song]
and [Movie]
respectively. But that is outside the scope of the question (force-unwrap)...
Additionally, I won't force unwrap even when dequeuing table view cells. If the dequeued cell cannot be cast to the appropriate UITableViewCell
subclass, that means there is something wrong with my storyboards, so it's not some runtime condition I can recover from (rather, a develop-time error that must be detected and fixed) so I bail with fatalError()
.
Original Answer (for the record)
In addition to Paulw11's answer, this pattern is completely valid, safe and useful sometimes:
if myObject is String {
let myString = myObject as! String
}
Consider the example given by Apple: an array of Media
instances, that can contain either Song
or Movie
objects (both subclasses of Media):
let mediaArray = [Media]()
// (populate...)
for media in mediaArray {
if media is Song {
let song = media as! Song
// use Song class's methods and properties on song...
}
else if media is Movie {
let movie = media as! Movie
// use Movie class's methods and properties on movie...
}
This question is probably opinion based, so take my answer with a grain of salt, but I wouldn't say that force downcast is always bad; you just need to consider the semantics and how that applies in a given situation.
as! SomeClass
is a contract, it basically says "I guarantee that this thing is an instance of SomeClass". If it turns out that it isn't SomeClass then an exception will be thrown because you violated the contract.
You need to consider the context in which you are using this contract and what appropriate action you could take if you didn't use the force downcast.
In the example you give, if dequeueReusableCellWithIdentifier
doesn't give you a MyOffersViewCell
then you have probably misconfigured something to do with the cell reuse identifier and an exception will help you find that issue.
If you used a conditional downcast then you are going to get nil and have to handle that somehow - Log a message? Throw an exception? It certainly represents an unrecoverable error and something that you want to find during development; you wouldn't expect to have to handle this after release. Your code isn't going to suddenly start returning different types of cells. If you just let the code crash on the force downcast it will point straight to the line where the issue occurred.
Now, consider a case where you are accessing some JSON retrieved from a web service. There could be a change in the web service that is beyond your control so handling this more gracefully might be nice. Your app may not be able to function but at least you can show an alert rather than simply crashing:
BAD - Crashes if JSON isn't an array
let someArray=myJSON as! NSArray
...
Better - Handle invalid JSON with an alert
guard let someArray=myJSON as? NSArray else {
// Display a UIAlertController telling the user to check for an updated app..
return
}