How is optional binding used in swift?
First someOptional
is checked to see if it's nil or has data. If it's nil, the if-statement just doesn't get executed. If there's data, the data gets unwrapped and assigned to constantName
for the scope of the if-statement. Then the code inside the braces is executed.
if let constantName = someOptional {
statements
}
There's no way to chain this functionality in one if-statement. let constantName = someOptional
does not directly evaluate to a boolean. It's best to think of "if let" as a special keyword.
Why use optional binding?
You use optional binding (if let
) if you have a block of code that you only want to run if the variable is not nil
.
You use optional chaining (the ?
) only when accessing the properties/methods of an optional variable.
But there are situations where optional chaining is not possible (i.e. you're not accessing a property/method of the optional variable, but rather using that variable for other purposes). For example
// let's assume `data` is a `NSData?` optional
if let imageData = data {
let image = UIImage(data: imageData)
// now do something with `image`
}
We do this because in this context, we can't use optional chaining, and using forced unwrapping (e.g. let image = UIImage(data: data!)
) would crash if data
was nil
.
what's the meaning of optional binding in swift
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
}
Bridging Optional Binding to Non-Optional Child (SwiftUI)
Child
should take a Binding
to the non-optional string, rather than using @State
, because you want it to share state with its parent:
struct Child: View {
// within Child, I'd like the value to be NonOptional
@Binding var text: String
var body: some View {
TextField("OK: ", text: $text).multilineTextAlignment(.center)
}
}
Binding
has an initializer that converts a Binding<V?>
to Binding<V>?
, which you can use like this:
if let binding = Binding<String>($model.name) {
Child(text: binding)
}
If you're getting crashes from that, it's a bug in SwiftUI, but you can work around it like this:
if let text = model.name {
Child(text: Binding(
get: { model.name ?? text },
set: { model.name = $0 }
))
}
Converting optional Binding to non-optional Binding
Binding
has an associated type Value
already, so by trying to use T
, you're putting a new generic on top of Value
, which already exists.
But, you will still end up using T
because you'll want to constrain Value
to scenarios where it is Optional:
extension Binding {
func safeBinding<T>(defaultValue: T) -> Binding<T> where Value == Optional<T> {
.init {
self.wrappedValue ?? defaultValue
} set: { newValue in
self.wrappedValue = newValue
}
}
}
As noted in the comments, the Xcode compiler has difficulty if just Binding.init
is used (note that I used just .init
). This can be solved by explicitly using Binding<T>.init
:
extension Binding {
func safeBinding<T>(defaultValue: T) -> Binding<T> where Value == Optional<T> {
Binding<T>.init {
self.wrappedValue ?? defaultValue
} set: { newValue in
self.wrappedValue = newValue
}
}
}
OR condition for optional binding?
Unfortunately I don't think this is possible in a single line (like if let x = y || let z = a {}
). Logically it wouldn't make sense, because you would end up in a state where both variables would remain optional (if either is unwrapped, you don't know which is unwrapped or if both are). I think you'll need to take some other approach to this problem. I think the simplest form would be something like
if let unwrappedProperty1 = property1 {
handleProperty(unwrappedProperty1)
} else if let unwrappedProperty2 = property2 {
handleProperty(unwrappedProperty2)
}
or you could do something like
if let unwrappedProperty = property1 ?? property2 {
handleProperty(unwrappedProperty)
}
which would give priority to property1
How to assign an optional Binding parameter in SwiftUI?
@Binding var searchTxt: String?
init(searchTxt: Binding<String?>?) {
self._searchTxt = searchTxt ?? Binding.constant(nil)
}
Update: I prefer this one. TextField("", text: $text ?? "default value")
https://stackoverflow.com/a/61002589/4728060
func ??<T>(lhs: Binding<Optional<T>>, rhs: T) -> Binding<T> {
Binding(
get: { lhs.wrappedValue ?? rhs },
set: { lhs.wrappedValue = $0 }
)
}
Optional State or Binding in SwiftUI
Here is working example of optional with State and Binding:
import SwiftUI
struct ContentView: View {
@State private var progress: Int?
var body: some View {
CustomView(progress: $progress)
}
}
struct CustomView: View {
@Binding var progress: Int?
var body: some View {
Button("update") {
if let unwrappedInt = progress { progress = unwrappedInt + 1 }
else { progress = 0 } //<< █ █ Here: initializing! █ █
}
.onChange(of: progress) { newValue in
if let unwrappedInt = progress { print(unwrappedInt) }
}
}
}
Optional Binding, what exactly the word Binding means here?
Does the Binding mean the action of assigning the valid value into the temporary constant/variable? I.e. "binding" those two things together?
Yes. Basically, assignment of a value to a variable name is a binding — it "binds" the name to the value. So even this is a binding:
let x = 1
What's special with if let
is that the binding takes place only if the value is an Optional that can be safely unwrapped (that is, it is not nil
). If it can't be unwrapped safely, it is not unwrapped and no binding takes place (and the if
condition fails).
Related Topics
Scncamera Limit Arcball Rotation
Difference Between Associated and Raw Values in Swift Enumerations
Getting String Name of Objective-C @Objc Enum Value in Swift
Swift Ternary Operator Compilation Error
How to Rewrite Swift ++ Operator in : Ternary Operator
Swift 2 Array of Tuples Not Working as in Swift 1
New @Convention(C) in Swift 2: How to Use It
Showing Cells in Demands in Uicollectionview with Vertical Infinite Scroll
Prevent Nsurlsession from Caching Responses
Swiftui Exporting or Sharing Files
Swift Update Label (With HTML Content) Takes 1Min
How to Print a String from Plist Without "Optional"
Swiftui List Empty State View/Modifier
Higher Order Function: "Cannot Invoke 'Map' with an Argument List of Type '((_) -> _)'"
Format Realtime Stopwatch Timer to the Hundredth Using Swift