Optional Chaining with Swift Strings

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



Leave a reply



Submit