Say I have the following scenario:
var str: String
var num: Int?
if let num = num {
str = "\(num) foo"
}
else {
str == "? foo"
}
can the flow control statements be simplified to one line? I.e. something along the lines of:
var str: String
var num: Int?
str = "\(String(num) ?? "?") foo"
You can use call the description property with optional chaining and then use the nil coalescing operator ?? to either unwrap that or replace it with "?" if num is nil (causing the optional chain to return nil):
str = "\(num?.description ?? "?") foo"
Example:
for num in [nil, 5] {
let str = "\(num?.description ?? "?") foo"
print(str)
}
? foo
5 foo
Here's one solution:
let str = "\(num.map { String($0) } ?? "?") foo"
This returns "? foo" if num is nil or it returns "42 foo" if num is set to 42.
Not exactly in a clean one liner.
But if you write a simple extension on Optionals, like so:
extension Optional where Wrapped: CustomStringConvertible {
var nilDescription: String {
switch self {
case .none: return "?"
case let .some(wrapped): return wrapped.description
}
}
}
you could write
let str = "\(num.nilDescription) foo"
I think this approach would be more readable.
You can also write it like below.
var number:Int? = 0
var str = String(format: "%# foo", (number != nil) ? NSNumber(integer: number!) : "?");
Hope it will help you.
Related
Given a value of type Any is it possible to check and see if it's an Optional or not?
This code doesn't work because instead of checking to see if it's optional or not it's trying to cast it, and it passes
let a: Any = "5"
switch a {
case let optional as Optional<Any>:
if case .some(let value) = optional {
print("wrapped value of `\(a)` is `\(value)`")
}
default:
print("\(a) is not an optional")
}
Base on #dfri's solution
private func isOptional(input: Any) -> Bool {
let mirror = Mirror(reflecting: input)
let style = mirror.displayStyle
switch style {
case .some(.optional):
return true
default:
return false
}
}
You can use runtime introspection using Mirror:
let foo: String? = "foo"
let bar: String = "bar"
var a: Any = foo
// if wrapping an optional, the reflection of the value has
// a displaystyle "optional"
if let displayStyle = Mirror.init(reflecting: a).displayStyle {
print(displayStyle) // optional
}
// for a non-optional fundamental native type: no displaystyle
a = bar
if let displayStyle = Mirror.init(reflecting: a).displayStyle {
print(displayStyle)
} // prints nothing
Optional/non-optional example where the underlying type is user-defined (non native):
struct Foo {}
let foo: Foo? = Foo()
let bar: Foo = Foo()
var a: Any = foo
// if wrapping an optional, the reflection of the value has
// a displaystyle "optional"
if let displayStyle = Mirror(reflecting: a).displayStyle {
print(displayStyle) // optional
}
// for a non-optional non-fundamental type:
a = bar
if let displayStyle = Mirror(reflecting: a).displayStyle {
print(displayStyle) // struct
}
If you don't want need to use the binded displayStyle variable (e.g. for printing) but simply want check whether the wrapped value is any kind of optional, you can add a boolean clause to the if statement that holds the optional binding of the displayStyle case,
if let displayStyle = Mirror(reflecting: a).displayStyle,
displayStyle == .optional {
// is an optional ...
}
... or remove the binding entirely in favour of a single conditional expression using the nil coalescing operator (??)
if Mirror(reflecting: a).displayStyle ?? .class == .optional {
// is an optional
}
Note however that for all the methods above, this simply tells you as dev whether the type wrapped by the Any instance is optional or not: Swifts typing system still knows nothing of the sort.
let a: Any = "5"
let b: Any? = "5"
if type(of: a) == Optional<Any>.self {
print("a is optional")
} else {
print("a is not optional")
}
if type(of: b) == Optional<Any>.self {
print("b is optional")
} else {
print("b is not optional")
}
/*
a is not optional
b is optional
*/
another example ...
let a: Any = 5
let b: Any? = 5
let c: Any = "5"
let d: Any? = "5"
let arr: [Any] = [a,b as Any,c,d as Any]
arr.forEach { (x) in
print(type(of: x))
}
/*
Int
Optional<Any>
String
Optional<Any>
*/
I want to satisfy multiple constraints in an if let construct. I know we can use a "," (comma) to unwrap multiple values but they both have to be successfully unwrapped.
For example :
var str: String? = "Hello"
var x: Int? = 10
if let intValue = x, stringValue = str {
// do something here.
} else {
}
I want if one of the conditions is successfully unwrapped, then a block will execute.
for example:
class CustomClass {
var x = 10
static func someValue() -> String? {
return "some"
}
}
var flag: Bool? = false
var x: Int? = 10
var status: String
in this i want if either customclass someValue function or x value any of successfully unwrapped and flag is true then code executes
You can create a tuple and use a switch like so:
switch (str, x) {
case (.Some,.Some):
print("Both have values")
case (.Some, nil):
print("String has a value")
case (nil, .Some):
print("Int has a value")
case (nil, nil):
print("Neither has a value")
}
I can use the following expression to prevent a variable from being nil:
let result: String = someTextField.text ?? ""
Is there also a shortcut to prevent result from being nil or empty in Swift?
You can try to check it using the .isEmpty method or else use a default value
let result = (someTextField.text ?? "").isEmpty ? "default" : string
?? is called Nil Coalescing Operator
The nil coalescing operator (a ?? b) unwraps an optional a if it contains a value, or returns a default value b if a is nil. The expression a is always of an optional type. The expression b must match the type that is stored inside a.
According to you question, ?? just is the shortcut of preventing result from being nil.
And there is no shortcut to preventing result from being both nil and empty. As empty is type string, it's different from nil.
Here is the solution:
let result: String =
someTextField.text?.isEmpty() != false ? "is null or empty" : someTextField.text!
Yes you can do it with ?? and ? but if you want you can define your own short cut.
// So we can apply to any type by extending the type to conform to the protocol
protocol CanBeEmpty
{
var isEmpty: Bool { get }
}
extension String: CanBeEmpty
{
// String doesn't need the var cos it already has it
}
extension Optional where Wrapped: CanBeEmpty
{
func unwrappedOrDefault(def: Wrapped) -> Wrapped
{
switch self
{
case .None:
return def
case .Some(let wrapped):
return wrapped.isEmpty ? def : wrapped
}
}
}
let foo: String? = "foo"
let fooUnwrapped = foo.unwrappedOrDefault("default") // "foo"
let bar : String? = ""
let barUnwrapped = bar.unwrappedOrDefault("default") // "default"
let baz: String? = nil
let bazUnwrapped = baz.unwrappedOrDefault("default") // "default"
If you think that is too much typing, create an operator
infix operator ??? {}
func ???<T: CanBeEmpty> (a: Optional<T>, b: T) -> T
{
return a.unwrappedOrDefault(b)
}
foo ??? "default" // "foo"
bar ??? "default" // "default"
baz ??? "default" // "default"
Is there a nicer way to do the assignment to DEF in the following example? I want to convert type A to Type B, but still preserve the nil possibility whenever I can.
Can't seem to stumble into a better way of doing this, however. Suggestions?
class ABC {
var DEF: Int?
func X (someValue: Int8?) {
DEF = someValue != nil ? Int(someValue) : nil
}
}
Swift 1:
class ABC {
var DEF: Int?
func X (someValue: Int8?) {
DEF = someValue.map{Int($0)}
}
}
Swift 2:
class ABC {
var DEF: Int?
func X (someValue: Int8?) {
DEF = someValue.map(Int.init)
}
}
map() takes an optional, unwraps it, and applies a function to it. If the optional resolves to nil, map() returns nil.
You are describing optional map:
var i: Int? = 2
let j = i.map { $0 * 2 } // j = .Some(4)
i = nil
let k = i.map { $0 * 2 } // k = nil
Think of this map like array or other collection map, where optionals are collections that have either zero (nil) or one (non-nil) element.
Note, if the operation you want to perform itself returns an optional, you need flatMap to avoid getting a double-optional:
let s: String? = "2"
let i = s.map { Int($0) } // i will be an Int??
let j = s.flatMap { Int($0) } // flattens to Int?
For example, instead of doing:
class Person {
var name: String
init(_ name: String) {
self.name = name
}
}
if let unwrappedName = p.name {
var greeting = “Hello “ + unwrappedName
} else {
var greeting = “Hello stranger”
}
Could I do this instead?
var upwrappedName = p.name ?? "default"
Or:
var unwrappedName = p.name ? p.name : "default"
"The nil coalescing operator (a ?? b) unwraps an optional a if it contains a value, or returns a default value b if a is nil. The expression a is always of an optional type. The expression b must match the type that is stored inside a." The documentation can be found here. So you can use the nil coalescing operator or even the ternary operator if you want if your statement before the operator is p.name != nil as your statement