without optional binding,we use optional like this,it seems tedious
func doSomething(str: String?)
{
let v: String! = str
if v != nil
{
// use v to do something
}
}
with optional binding,it seems the if let doesn't do any thing to make it less tedious. we still have a if statement to test before use it.
func doSomething(str: String?)
{
if let v = str
{
// use v to do something
}
}
Is there any other examples can show some benefits to use optional bindings ?
Advantages of the Optional binding over If Statements and Forced Unwrapping:
local variable which is not an optional and a shortcut when a structure is deeper than one level
Context:
You have three techniques to work with an optionals:
Optional binding
If statements and forced unwrapping
Optional chaining
Optional binding
You use optional binding to find out whether an optional contains a
value, and if so, to make that value available as a temporary constant
or variable. Optional binding can be used with if and while statements
to check for a value inside an optional, and to extract that value
into a constant or variable, as part of a single action.
If Statements and Forced Unwrapping
You can use an if statement to find out whether an optional contains a
value by comparing the optional against nil. You perform this
comparison with the “equal to” operator (==) or the “not equal to”
operator (!=).
Optional chaining
Optional chaining is a process for querying and calling properties,
methods, and subscripts on an optional that might currently be nil. If
the optional contains a value, the property, method, or subscript call
succeeds; if the optional is nil, the property, method, or subscript
call returns nil. Multiple queries can be chained together, and the
entire chain fails gracefully if any link in the chain is nil.
source
struct Computer {
let keyboard: Keyboard?
}
struct Keyboard {
let battery: Battery?
}
struct Battery {
let price: Int?
}
let appleComputer: Computer? = Computer(keyboard: Keyboard(battery: Battery(price: 10)))
func getBatteryPriceWithOptionalBinding() -> Int {
if let computer = appleComputer {
if let keyboard = computer.keyboard {
if let battery = keyboard.battery {
if let batteryPrice = battery.price {
print(batteryPrice)
return batteryPrice
}
}
}
}
return 0
}
func getBatteryPriceWithIfStatementsAndForcedUnwrapping() -> Int {
if appleComputer != nil {
if appleComputer!.keyboard != nil {
if appleComputer!.keyboard!.battery != nil {
if appleComputer!.keyboard!.battery!.price != nil {
print(appleComputer!.keyboard!.battery!.price!)
return appleComputer!.keyboard!.battery!.price!
}
}
}
}
return 0
}
func getBatteryPriceWithOptionalChainingAndForcedUnwrapping() -> Int {
if appleComputer?.keyboard?.battery?.price != nil {
print(appleComputer!.keyboard!.battery!.price!)
return appleComputer!.keyboard!.battery!.price!
}
return 0
}
func getBatteryPriceWithOptionalChainingAndOptionalBinding() -> Int {
if let price = appleComputer?.keyboard?.battery?.price {
print(price)
return price
}
return 0
}
func getBatteryPriceWithOptionalChainingAndNilCoalescing() -> Int {
print(appleComputer?.keyboard?.battery?.price ?? 0)
return appleComputer?.keyboard?.battery?.price ?? 0
}
Related
func myFunc(array:[Int]) -> (min: Int, max: Int)?
{
if array.isEmpty {return nil}
var minNumber = array[0]
var maxNumber = array[0]
for number in array {
if number < minNumber {
minNumber = number
}
else if number > maxNumber{
maxNumber = number
}
}
return (minNumber, maxNumber)
}
let tempArray:[Int] = [1,2,3,4,5,6,7]
let value = myFunc(array: tempArray)
print("The minima is: \(value?.min != nil ? value!.min : nil) the maxima is \(value?.max != nil ? value!.max : nil)")
In the given code I just wanted to make if for example, the code contains some value it will force unwrap but if it is not contained it will just print "nil". But in my code, if it contains number it will print Optional(some number).
value?.min != nil ? value!.min : nil
is a (conditional) expression and evaluates to some value which has a type.
The first expression value!.min has the type Int, but the second expression nil is an optional and has the type Int?. Therefore the type of the conditional expression becomes Int? and that is printed as "Optional(1)".
What you want is the string "nil", or the non-nil value as a string:
print("The minimum is: \(value?.min != nil ? "\(value!.min)" : "nil")")
(and similarly for the maximum). Now both expression in the conditional expression
value?.min != nil ? "\(value!.min)" : "nil")
are strings, and the result is a string as well. This can be abbreviated to
print("The minimum is: \(value.map {"\($0.min)"} ?? "nil")")
If you need this frequently then you can define an extension method on the optional type
extension Optional {
var descriptionOrNil: String {
switch self {
case .some(let wrapped): return "\(wrapped)"
case .none: return "nil"
}
}
}
and use it as
print("The minimum is: \((value?.min).descriptionOrNil)")
So if i don't understand the question wrong. You can do:
extension Optional where Wrapped == Int {
var valueOrEmpty: String {
guard let unwrapped = self else {
return "nil"
}
return "\(unwrapped)"
}
}
print("The minima is: \(value?.min.valueOrEmpty) the maxima is \(value?.max.valueOrEmpty)")
Right?
I've got this piece of code
class MyObject<T> {
func start(_ value: T?) {
if let value = value {
doSomething(value)
}
}
func doSomething(_ value: T) {
print(value)
}
}
MyObject<String>().start("some")
// prints "some"
MyObject<String?>().start(nil)
// doesn't print anything
I need doSomething() to be called for every valid value passed to start(). And when T is already an optional type like String?, then nil is a valid value.
Do I need to write two versions of start() in extensions of MyObject with conditions on the type of T? And how to do it?
You can make your start function take a non-optional T and just always call doSomething instead of trying to unwrap it first. This would only allow you to call start(nil) if T itself was an optional type:
class MyObject<T> {
func start(_ value: T) {
doSomething(value)
}
func doSomething(_ value: T) {
print(value)
}
}
MyObject<String>().start("some")
// prints "some"
MyObject<String?>().start(nil)
// prints "nil"
If you want to leave the parameter to start as optional then your original code will actually work, but you need to change the way you are passing your value in your second example.
Since T is String?, the parameter in your method is of type String?? and passing a nil String?? is different then passing one with a String? that happens to contain nil.
If you call it as:
MyObject<String?>().start(.some(nil))
or
let string: String? = nil
MyObject<String?>().start(string)
Then it will print "nil"
Since let value = value fails with the second input you need to handle that case separately.
func start(_ value: T?) {
if let value = value {
doSomething(value)
}
else
{
print("nil input")
}
}
In case value is nil, when you unwrap the value will not enter in the if statement. Instead, you can do this:
class MyObject<T> {
func start(_ value: T?) {
if let value = value {
doSomething(value)
} else {
print("nil")
}
}
func doSomething(_ value: T) {
print(value)
}
}
Optional in Swift is just a generic enum that have two cases:
enum Optional<T> {
case some(T)
case none
}
For example String? is the same thing of Optional<String>.
If you declare MyObject<String?> basically you're creating this MyObject<Optional<String>>, so your concrete start method will be
func start(_ value: Optional<Optional<String>>) { ... }
This means that if you call it like start(nil), the whole object will be of course nil and the if let will fail.
You can however call that function in this way
MyObject<String?>().start(Optional.some(Optional.none))
MyObject<String?>().start(.some(.none)) // -> shorter version with type inference
Basically now the outer optional exists and the unwrap works, but the inner one is nil.
However I still can't understand why would you need to do something like that
Is it possible to test whether a specific enum type can be initialized by rawValue when switching on a string, instead of using if let?
static func getCurrency(from code: String) -> Currency? {
if let fiatCurrency = Fiat(rawValue: code) {
return fiatCurrency
} else if let cryptoCurrency = Blockchain(rawValue: code) {
return cryptoCurrency
} else {
return nil
}
}
This may be similar to type casting, where currency adheres to my Currency protocol:
switch currency {
case let fiatCurrency as Fiat:
return getFiatFormatting(for: value, fiatCurrency: fiatCurrency)
case let blockchain as Blockchain:
return getCryptoFormatting(for: value, blockchain: blockchain)
case let token as Token:
return getTokenFormatting(for: value, token: token)
default:
return nil
}
Thanks!
If I understand correctly what you want, you can use the nil coalescing operator instead of if let.
static func getCurrency(from code: String) -> Currency? {
return Fiat(rawValue: code) ?? Blockchain(rawValue: code)
}
You can add as many other possible enum initializations as you want. It'll evaluate in order and return the first that it's not nil. If all are nil, it returns nil. So, it has exactly the same behaviour as a series of if-let-else.
I have this code in Swift:
guard let user = username else{
return nil
}
But I'm getting the following errors:
Nil is incompatible with return type String
Any of you knows why or how I return nil in this case?
I'll really appreciate your help
Does your function declare an optional return type?
func foo() -> String? { ...
See more on: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html
NOTE
The concept of optionals doesn’t exist in C or Objective-C. The
nearest thing in Objective-C is the ability to return nil from a
method that would otherwise return an object, with nil meaning “the
absence of a valid object.”
You have to tell the compiler that you want to return nil. How do you that? By assigning ? after your object. For instance, take a look at this code:
func newFriend(friendDictionary: [String : String]) -> Friend? {
guard let name = friendDictionary["name"], let age = friendDictionary["age"] else {
return nil
}
let address = friendDictionary["address"]
return Friend(name: name, age: age, address: address)
}
Notice how I needed to tell the compiler that my object Friend, which I'm returning, is an optional Friend?. Otherwise it will throw an error.
*Does your function declare an optional return type?
func minAndmax(array:[Int])->(min:Int, max:Int)? {
if array.isEmpty {
return nil
}
var currentMin = array[0]
var currentMax = array[0]
for value in array {
if value < currentMin {
currentMin = value
}
else if value > currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}
if let bounds = minAndmax(array: [8, -6, 2, 109, 3, 71]) {
print(bounds)
}
I'm writing an extension to Dictionary so that when I give it a String key, it'll return me a String only if the value associated with the key is non-nil and not empty.
extension Dictionary {
subscript(key: String) -> String? {
if let string = super.subscript(key) {
if string.isEmpty == false {
return string
}
}
return nil
}
}
However, at the if let string = super.subscript(key) { line, I get the following compile error and I don't know what it means--neither is there a Google result that explains it:
Expected -> for subscript element type
I am doing this because I'm working with an API that returns a JSON where a key's value may be an empty string--which is an invalid value to the app by our requirements, and hence, as good as nil.
Of course the longer way works, but I'm looking for a way to make this shorter.
if let value = dict["key"] as? String {
if value.isEmpty == false {
// The value is non-nil and non-empty.
}
}
You're going to think this is very silly, but my suggestion would be: do more or less exactly what you're doing, but encapsulate it as a separate function rather than trying to deal with the implications of defining a new subscript:
extension Dictionary {
func nes(key:Key) -> String? {
var result : String? = nil
if let s = self[key] as? String {
if !s.isEmpty {
result = s
}
}
return result
}
}
(nes stands for "non-empty string".)
Now call it like d.nes("foo").