Explicitly unwrapping optional nil does not cause crash - swift

A few folks asked this question before, yet no answer was accepted.
I have a UITableViewCell that contains a UITextField.
If I click slightly outside of the textField the row highlights. I want to prevent this.
To prevent it I do the following:
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath! {
return nil
}
This works perfectly fine. Here is my question. The author of the tutorial states:
Note that returning nil from a method is only allowed if there is a
question mark (or exclamation point) behind the return type.
What does he mean that you can put an exclamation mark behind the optional return type? Why doesn't the program crash? Returning nil after I place an exclamation mark after the IndexPath return type doesn't crash. I thought ! would explicitly unwrap the nil and fail?

As of Swift 3, “Implicitly unwrapped optional” is not a separate type,
but an attribute on the declaration of a regular/strong optional.
For the details, see SE-0054 Abolish ImplicitlyUnwrappedOptional type.
A function with an IUO return type can return nil,
and assigning the return value to a variable makes that a regular
optional:
func foo() -> Int! {
return nil
}
let x = foo() // Type of `x` is `Int?`
print(x) // nil
Only if evaluation as an optional is not possible then the value
will be forced-unwrapped (and cause a runtime exception is the
value is nil):
let y = 1 + foo() // Fatal error: Unexpectedly found nil while unwrapping an Optional value
In your case, your
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath!
method overrides the UITableViewController method
func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath?
and can return nil. This does not crash unless the caller unwraps
the value.
Remark: The above is meant as an explanation why your code compiles
and works. Generally, I do not see a good reason to use implicitly
unwrapped optional return types. The main use-cases of IUOs are
stated in SE-0054:
The ImplicitlyUnwrappedOptional ("IUO") type is a valuable tool for importing Objective-C APIs where the nullability of a parameter or return type is unspecified. It also represents a convenient mechanism for working through definite initialization problems in initializers.

One way to think this as a choise of the API Implementor. If implementor handles the input arguments it will not be any problem to the API User.
Lets create a drawing text class which just prints at console.
class TextDrawer {
var mustBeText: String!
func renderText(string: String) {
print(string)
}
func renderSafely() {
renderText(string: self.mustBeText ?? "Nothing found to be rendered")
// or
if let safeString = self.mustBeText {
renderText(string: safeString)
}
}
func renderUnsafely() {
renderText(string: mustBeText)
}
}
We have defined the mustBeText as String! means we are allowed to expect any string as well as nil argument.
Now, as we create a instance of the class as below:
let textDrawer = TextDrawer()
textDrawer.mustBeText = nil // this is allowed since the `mustBeText` is `String!`.
textDrawer.renderSafely() // prints -- Nothing found to be rendered
textDrawer.renderUnsafely() // crashes at runtime.
The renderUnsafaly() will crash since its not handling the nil case.

Related

How to avoid force casting (as!) in Swift

extension ActionSheetViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sheetActions.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: TableCellIds.ActionSheet.actionSheetTableCellIdentifier, for: indexPath) as! ActionsSheetCell
cell.actionCellLabel.text = "My cell content goes here"
return cell
}
}
Above code gives me 'Force Cast Violation: Force casts should be avoided. (force_cast)' error. How can I avoid it?
Some force-casts are unavoidable, especially when interacting with Objective C, which has a much more dynamic/loose type system.
In some cases like this, a force-cast would be self-explanatory. If it crashes, clearly you're either:
getting back nil (meaning there's no view with that reuse identifier),
or you're getting back the wrong type (meaning the cell exists, but you reconfigured its type).
In either case your app is critically mis-configured, and there's no much graceful recovery you can do besides fixing the bug in the first place.
For this particular context, I use a helper extension like this (it's for AppKit, but it's easy enough to adapt). It checks for the two conditions above, and renders more helpful error messages.
public extension NSTableView {
/// A helper function to help with type-casting the result of `makeView(wihtIdentifier:owner:)`
/// - Parameters:
/// - id: The `id` as you would pass to `makeView(wihtIdentifier:owner:)`
/// - owner: The `owner` as you would pass to `makeView(wihtIdentifier:owner:)`
/// - ofType: The type to which to cast the result of `makeView(wihtIdentifier:owner:)`
/// - Returns: The resulting view, casted to a `T`. It's not an optional, since that type error wouldn't really be recoverable
/// at runtime, anyway.
func makeView<T>(
withIdentifier id: NSUserInterfaceItemIdentifier,
owner: Any?,
ofType: T.Type
) -> T {
guard let view = self.makeView(withIdentifier: id, owner: owner) else {
fatalError("This \(type(of: self)) didn't have a column with identifier \"\(id.rawValue)\"")
}
guard let castedView = view as? T else {
fatalError("""
Found a view for identifier \"\(id.rawValue)\",
but it had type: \(type(of: view))
and not the expected type: \(T.self)
""")
}
return castedView
}
}
Honestly, after I got experienced enough with the NSTableView APIs, investigating these issues became second nature, and I don't find this extension as useful. Still, it could save some debugging and frustration for devs who are new the platform.
The force cast is actually correct in this situation.
The point here is that you really don't want to proceed if you can't do the cast, because you must return a real cell and if it's the wrong class, the app is broken and you have no cell, so crashing is fine.
But the linter doesn't realize that. The usual way to get this past the linter is to do a guard let with as?, along with a fatalError in the else. That has the same effect, and the linter will buy into it.
I really like the approach suggested by Alexander at https://stackoverflow.com/a/67222587/341994 - here's an iOS modification of it:
extension UITableView {
func dequeue<T:UITableViewCell>(withIdentifier id:String, for ip: IndexPath) -> T {
guard let cell = self.dequeueReusableCell(withIdentifier: id, for: ip) as? T else {
fatalError("could not cast cell")
}
return cell
}
}
So now you can say e.g.:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : MyTableViewCell = tableView.dequeue(withIdentifier: "cell", for: indexPath)
// ...
return cell
}
And everyone is happy including the linter. No forced unwraps anywhere and the cast is performed automatically thanks to the generic and the explicit type declaration.
As others have said, a force cast is appropriate in this case, because if it fails, it means you have a critical error in your source code.
To make SwiftLint accept the cast, you can surround the statement with comments as described in this issue in the SwiftLint repo:
// swiftlint:disable force_cast
let cell = tableView.dequeueReusableCell(withIdentifier: TableCellIds.ActionSheet.actionSheetTableCellIdentifier, for: indexPath) as! ActionsSheetCell
// swiftlint:enable force_cast
The right thing to do is: remove force_cast from swift lint’s configuration file. And be professional: only write force casts where you mean “unwrap or fatal error”. Having to “get around the linter” is a pointless waste of developer time.

How can I implement didSelectRowAt method without relying on indexPath

So I'm very new to the Swift programming language and iOS development in general and I'm trying to implement a simple tableview for navigation.
I didn't like the idea of relying on the indexPath parameter to determine which action to perform in my code as changing the order of the cells will need me to go back and refactor the method too. so I was looking at implementing a multi-dimensional array to store my different items for the table.
This works absolutely fine for the cell contents but I'm running into issues when trying to implement the didSelectRowAt method.
Note this is all within a UIViewController class with the UITableViewDataSource delegate.
private let options: [[( title: String, action: (() -> ())? )]] = [
[
("title" , action)
]
]
func action() {
//perform logic here
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let action = self.options[indexPath.section][indexPath.row].action else { return }
action()
}
However I am recieving a build error:
Cannot convert value of type '("MyViewControllerName") -> () -> ()' to expected element type '(() -> ())?'
What am I missing here? Is this even the best way to implement this method?
The issue is that:
1 - action is a method not a simple function.
2 - you are using action before your view controller is actually instantiated (since it's in the init of a stored property).
So there is one simple way to solve your issue:
Replace private let options
by
private lazy var options
for instance.
That way the options will be initialised after your ViewController so the action method will be available as a function.
Overall, it's not a bad idea to try to tie the action to your data.

Is there a memory cycle when assigning variable with a function from another class?

Is there a memory cycle in the code below? As i know AnswersCollectionViewDelegate has an implicit reference to ViewController because of closure cellClickHandler is a reference types but i cant mark var cellClickHandler: ((Int) -> Void))? as a weak because of error 'weak' may only be applied to class and class-bound protocol types, not '(Int) -> Void'
class AnswersCollectionViewDelegate: NSObject, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
var cellClickHandler: ((Int) -> Void))?
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
cellClickHandler(indexPath.Item)
}
}
class ViewController: UIViewController{
let answersDelegate = AnswersCollectionViewDelegate()
#IBOutlet weak var answersCollectionView: UICollectionView!{
didSet{
answersDelegate.cellClickHandler = showNextPost
answersCollectionView.delegate = answersDelegate
answersCollectionView.dataSource = answersDelegate
}
}
func showNextPost(answer: Int){
//analyzeAnswer&showNextPost
}
}
Yes it does, and it's a subtle thing to notice, so I think you've really understood how reference cycle happen. There is some current work in the compiler to try to detect this and provide a warning, but currently it's up to the developer to reason through this by hand.
The solution is to not use a function reference here but instead use a closure:
answersDelegate.cellClickHandler = { [weak self] self?.showNextPost(answer: $0) }
Regarding matt's comments below: properties with "delegate" in their name are almost always weak (or at most have a retain loop that ends after some operation completes). That said, I generally like the composition pattern you're doing here, and I don't think this is wrong (the one thing I'd probably do is pass the cellClickHandler in AnswersCollectionViewDelegate.init rather than setting it later). It's worth remembering, though, that when other developers see the word "delegate" in the property name, they're going to think it's weak and that can lead to bugs later. I'd probably call it clickHandler rather than answersDelegate.

When to use Never [duplicate]

I read the code block enclosed in curly braces after the keyword else in the context of a guard-else flow, must call a function marked with the noreturn attribute or transfer control using return, break, continue or throw.
The last part is quite clear, while I don't understand well the first.
First of all, any function returns something (an empty tuple at least) even if you don't declare any return type. Secondly, when can we use a noreturn function? Are the docs suggesting some core, built-in methods are marked with noreturn?
The else clause of a guard statement is required, and must either call
a function marked with the noreturn attribute or transfer program
control outside the guard statement’s enclosing scope using one of the
following statements:
return
break
continue
throw
Here is the source.
First of all, any function returns something (an empty tuple at least) even if you don't declare any return type.
(#noreturn is obsolete; see Swift 3 Update below.)
No, there are functions which terminate the process immediately
and do not return to the caller. These are marked in Swift
with #noreturn, such as
#noreturn public func fatalError(#autoclosure message: () -> String = default, file: StaticString = #file, line: UInt = #line)
#noreturn public func preconditionFailure(#autoclosure message: () -> String = default, file: StaticString = #file, line: UInt = #line)
#noreturn public func abort()
#noreturn public func exit(_: Int32)
and there may be more.
(Remark: Similar annotations exist in other programming languages
or compilers, such as [[noreturn]] in C++11, __attribute__((noreturn)) as a GCC extension, or _Noreturn for the
Clang compiler.)
You can mark your own function with #noreturn if it also terminates
the process unconditionally, e.g. by calling one of the built-in functions, such as
#noreturn func myFatalError() {
// Do something else and then ...
fatalError("Something went wrong!")
}
Now you can use your function in the else clause of a guard statement:
guard let n = Int("1234") else { myFatalError() }
#noreturn functions can also be used to mark cases that "should not
occur" and indicate a programming error. A simple example
(an extract from Missing return UITableViewCell):
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: MyTableViewCell
switch (indexPath.row) {
case 0:
cell = tableView.dequeueReusableCellWithIdentifier("cell0", forIndexPath: indexPath) as! MyTableViewCell
cell.backgroundColor = UIColor.greenColor()
case 1:
cell = tableView.dequeueReusableCellWithIdentifier("cell1", forIndexPath: indexPath) as! MyTableViewCell
cell.backgroundColor = UIColor.redColor()
default:
myFatalError()
}
// Setup other cell properties ...
return cell
}
Without myFatalError() marked as #noreturn, the compiler would
complain about a missing return in the default case.
Update: In Swift 3 (Xcode 8 beta 6) the #noreturn attribute
has been replaced by a Never return type, so the above example
would now be written as
func myFatalError() -> Never {
// Do something else and then ...
fatalError("Something went wrong!")
}
simple playground to see how it works ...
//: Playground - noun: a place where people can play
import Foundation
#noreturn func foo() {
print("foo")
exit(1)
}
var i: Int?
guard let i = i else {
foo()
}
print("after foo") // this line will never executed
//prints foo and finish
For instance, consider an assync operation that return either a value or error in result type. We usually write this as follows.
enum Result<Value, Error> {
case success(Value)
case failure(Error)
}
func fetch(_ request: URLRequest,
completion: (Result<(URLResponse, Data), Error>) -> Void) {
// ...
}
Now, if you know a function that always return value, never an error, then we could write :
func alwaysSucceeds(_ completion: (Result<String, Never>) -> Void) {
completion(.success("yes!"))
}
When compiler see Never, it won't force you to write all the switch cases to be exhaustive, so you can skip .failure as follows.
alwaysSucceeds { (result) in
switch result {
case .success(let string):
print(string)
}
}
Ref : https://nshipster.com/never/

I keep getting error after I added UISearchController in tableview

I keep getting this message fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)
on this line(marked with */ */)
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
*/ if resultSearchController.active */
{
return self.filtered.count
}
else
{
return self.entries.count
}
}
The table view is loaded from coreData
class NotesListTableViewController: UITableViewController ,UISearchResultsUpdating {
var managedObjectContext : NSManagedObjectContext!
var entries: [NSManagedObject]!
var filtered :[NSManagedObject] = []
var resultSearchController:UISearchController!
entries is used to load from coredata and filtered is the used to store data after searching
can anybody please help me with this
The issue is that resultSearchController is nil.
You can either, change the ! on the property to a ?, then the compiler will force you to handle the case where it might be nil whenever you try to access it. Or, you can review why it is nil and fix that. For example, are you never creating it in the first place?