Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid force unwrapping a variable?

Tags:

swift

How do I avoid using the ! operation doing a force unwrap as using this is usually a bad option.

What is the better option with code like the following where using it makes the code look simpler and because of the if check the variable ! is called on will never be nil and so cannot crash.

My instructor introduced us to the bang(!) operator and then told us to never use it again. Telling us why of course, that it will crash our app if the optional is nil.

However I find myself in situations like these where the bang operator seems to be the most concise and safe option.

func fullName() -> String {     if middleName == nil {         return "\(firstName) \(lastName)"     }else{         return "\(firstName) \(middleName!) \(lastName)"     } } 

Is there a better way to do something like this?

Also, here's the full class if anybody is wondering.

class CPerson{     var firstName: String     var middleName: String?     var lastName: String      init(firstName: String, middleName: String?, lastName: String) {         self.firstName = firstName         self.middleName = middleName         self.lastName = lastName     }     convenience init(firstName: String, lastName: String) {         self.init(firstName: firstName, middleName: nil, lastName: lastName)     }     func fullName() -> String {         if middleName == nil {             return "\(firstName) \(lastName)"         }else{             return "\(firstName) \(middleName!) \(lastName)"         }     } } 

My instructor said "If I see you using the bang operator, we're going to fight" O_O

like image 665
Trevor Wood Avatar asked Sep 12 '16 16:09

Trevor Wood


People also ask

How do I stop forced unwrapping in Swift?

Your function may crash if you make some spelling error, and type the name of a different variable instead of middleName, but that would be a bug anyway and the crash would lead you to the bug. But usually "if let ... " is the better way to handle this, because it combines the test and the unwrapping.

What is force unwrapping?

In these cases, Swift lets you force unwrap the optional: convert it from an optional type to a non-optional type. For example, if you have a string that contains a number, you can convert it to an Int like this: let str = "5" let num = Int(str)

What will happen if you try to unwrap an optional that contains nil like so?

Unwrap an optional type with the nil coalescing operator If a nil value is found when an optional value is unwrapped, an additional default value is supplied which will be used instead. You can also write default values in terms of objects.


2 Answers

Use the if let or guard constructs:

func fullName() -> String {     if let middleName = middleName {         return "\(firstName) \(middleName) \(lastName)"      } else {         return "\(firstName) \(lastName)"     } }  func fullName() -> String {     guard let middleName = middleName else {         return "\(firstName) \(lastName)"     }     return "\(firstName) \(middleName) \(lastName)" } 

I've put the guard statement in for completeness but as others have commented this is more commonly used in an error/failure case.

I would also advise against using string interpolation for Strings. They are already strings, there is no need to use the description of each name in a new string.

Consider return firstName + " " + lastName. See Difference between String interpolation and String initializer in Swift for cases when string interpolation could return an unexpected result.

like image 64
JAL Avatar answered Oct 07 '22 15:10

JAL


Your instructor is, broadly speaking, correct. Definitely in this case. There's no reason for making this so special case and forcing duplicated code.

func fullName() -> String {     return [firstName, middleName, lastName] // The components         .flatMap{$0}            // Remove any that are nil         .joined(separator: " ") // Join them up } 

This just joins all the non-nil parts of the name with spaces. The other answers here are also fine, but they don't scale as well to adding more optional pieces (like a "Mr." title or "Jr." suffix).

(This is in Swift3 syntax. Swift 2 is very similar, it's joinWithSeparator(_:) instead.)

like image 26
Rob Napier Avatar answered Oct 07 '22 14:10

Rob Napier