Optional chaining with Swift strings
When you say:
My understanding of optional chaining is that, once you start using
?.
in a dotted expression, the rest of the properties are made optional and are typically accessed by?.
, not.
.
I would say that you are almost there.
It’s not that all the properties are made optional, it’s that the original call is optional, so it looks like the other properties are optional.
characters
is not an optional property, and neither is count
, but the value that you are calling it on is optional. If there is a value, then the characters
and count
properties will return a value; otherwise, nil
is returned. It is because of this that the result of s?.characters.count
returns an Int?
.
If either of the properties were optional, then you would need to add ?
to it, but, in your case, they aren’t. So you don’t.
Edited following comment
From the comment:
I still find it strange that both
s?.characters.count
and(s?.characters)?.count
compile, but(s?.characters).count
doesn't. Why is there a difference between the first and the last expression?
I’ll try and answer it here, where there is more room than in the comment field:
s?.characters.count
If s
is nil, the whole expression returns nil
, otherwise an Int
. So the return type is Int?
.
(s?.characters).count // Won’t compile
Breaking this down: if s
is nil
, then (s?.characters)
is nil
, so we can’t call count
on it.
In order to call the count
property on (s?.characters)
, the expression needs to be optionally unwrapped, i.e. written as:
(s?.characters)?.count
Edited to add further
The best I can get to explaining this is with this bit of playground code:
let s: String? = "hello"
s?.characters.count
(s?.characters)?.count
(s)?.characters.count
((s)?.characters)?.count
// s?.characters.count
func method1(s: String?) -> Int? {
guard let s = s else { return nil }
return s.characters.count
}
// (s?.characters).count
func method2(s: String?) -> Int? {
guard let c = s?.characters else { return nil }
return c.count
}
method1(s)
method2(s)
Optional Chaining in one step?
Strictly spoken Example 2 is neither optional binding nor optional chaining because String(format...)
returns a non-optional String
and the format
parameter must be non-optional, too.
Example 1 is the correct and recommended syntax to handle the optionals.
Edit: I totally agree with Alexander's answer (except that String(format:)
returns String?
)
Can I use Optional chaining to give a default value for the property of an optional?
You don't have to unwrap it to get it to work. That is not why the error occurred. The ??
is designed to handle such cases, after all.
The error occurs because of operator precedence. The compiler thinks that it should evaluate the +
first, concatenating the two strings, THEN do the nil-coalescing. It sees that the second operand is not unwrapped and complains.
To make it produce the intended result, explicitly tell the compiler to evaluate the ??
first by adding brackets:
print("Meeting host: " + (meeting.host?.email ?? “No host”))
optional chaining in Swift 3: why does one example work and not the other?
The problem in a nutshell ? the function for fractions reports a fault whereas the function for decimal numbers fails to detect bad input.
The function for decimal numbers does detect “bad” input. However, "700"
does not contain "."
, and you only call processDecimal(s:)
if the string does contain "."
. If the string doesn't contain "."
and also doesn't contain "/"
, doubleFromDecimalOrFraction(s:)
doesn't call any function to parse the string.
Optional chaining the binding value in SwiftUI textfield
When use the "dot chaining" syntax on a Binding
its a shortcut syntax for creating a Binding
. It doesn't have the same semantics "lookup a property of this thing" that the dot syntax usually holds.
So $food.name
is not going to resolve the binding of food
then reference one of its properties. It going to create a two-way Binding<String>
to the name
property of the food.
Similarly, when you have $food.macroProfile
the value of that expression is a Binding<MacroNutrientProfile?>
... a binding that will directly change a value in food
(and the value it can change is an optional). It is not a resolution of the binding $food
followed by referencing one of that object's properties.
$food.macroProfile?.carb
is nonsensical because $food.macroProfile
is of type Binding<MacroNutrientProfile?>
which is not an optional type. So you see errors.
$food.name
is not nonsensical because it is a Binding<String>
and you're not trying to treat a non-optional value as an optional.
One way to change it is to use a custom binding:
struct Food: Codable, Identifiable {
var id = UUID()
var name: String = ""
var energy: Float?
var water: Float?
var macroProfile: MacronutrientProfile?
}
struct MacronutrientProfile: Codable {
var carb: Float?
var protein: Float?
var fat: Float?
}
struct SomeView : View {
@State var food: Food
let gram = NumberFormatter()
var body : some View {
let carbBinding = Binding<Float?>(get: { food.macroProfile?.carb },
set: { newValue in food.macroProfile?.carb = newValue })
return HStack {
Text("Carbohydrates")
Spacer()
TextField("Grams", value: carbBinding, formatter: gram)
.multilineTextAlignment(.trailing)
.keyboardType(.numberPad)
}
}
}
swift optional chaining with cast
There are 2 problems: the first is that in this line:
let name: String = self.detailItem?.valueForKey("name") as String
the right part is an optional (detailItem
is optional), so whatever the expression returns, it cannot be assigned to a non-optional variable having type String
The second problem is that valueForKey
returns AnyObject!
, which can be an instance of any class - but String
is not a class, you'd need Any
in order to be able to cast that into a String
.
I presume that NSManagedObject
returns an NSString
, so you can achieve what you need with this line:
let name: String? = (self.detailItem?.valueForKey("name") as? NSString) as? String
Swift: Optional chaining for optional subscripts
let value = key.flatMap { map[$0] }
would to the trick, using the
/// Returns `nil` if `self` is nil, `f(self!)` otherwise.
@warn_unused_result
public func flatMap<U>(@noescape f: (Wrapped) throws -> U?) rethrows -> U?
method from struct Optional
.
Alternatively, you can wrap that into a custom subscript method
extension Dictionary {
subscript(optKey : Key?) -> Value? {
return optKey.flatMap { self[$0] }
}
}
and the simply write
let value = map[key]
To avoid confusion with the "normal" subscript method, and to make
the intention more clear to the reader of your code, you can define
the subscript method with an external parameter name:
extension Dictionary {
subscript(optional optKey : Key?) -> Value? {
return optKey.flatMap { self[$0] }
}
}
let value = map[optional: key]
How to use optional chaining while searching through a dictionary in swift?
Seems like swift 1.2 has added this feature.
"More powerful optional unwrapping with if let — The if let construct can now unwrap multiple optionals at once, as well as include intervening boolean conditions. This lets you express conditional control flow without unnecessary nesting."
https://developer.apple.com/swift/blog/
Related Topics
Performseguewithidentifier in Swift
How to Zip More Than 4 Publishers
How to Rounded the Corners When I Draw Rectangle Using Uibezierpath Points
Swiftui: Detect Finger Position on MAC Trackpad
Forcing Nspersistentcontainer Change Core Data
Realitykit - Set Text Programmatically of an Entity of Reality Composer
One-Way Platform Collisions in Sprite Kit
How to Highlight a Uitextview's Text Line by Line in Swift
How to Override Layerclass in Swift
Changing Tab Bar Font in Swift
Difference Between Switch Cases "@Unknown Default" and "Default" in Swift 5
Combining Two Conditions in Nspredicate
How to Add Auto-Complete Comment in Xcode (Swift)
Convert Int to Uint32 in Swift
Swift Delegate for a Generic Class
Trying to Use Keychainitemwrapper by Apple "Translated" to Swift
How to Cancel Alamofire.Upload
iOS 10 App Crashes When Trying to Save Image to Photo Library