Idiomatic way to test Swift Optionals
I'm not sure how useful these methods are, but they make a fine starting point for discussion.
Take a look at the current implementation of Optional:
https://github.com/apple/swift/blob/ecd3c07a86394aa6b1372f1370f470842b39ea6e/stdlib/public/core/Optional.swift
Near the top, you can see that its own most primitive representation uses .None and .Some. So Solution 1 is the most direct approach, has the least overhead, and follows the pattern used in the implementation of Optional itself. I'd say that counts as idiomatic.
Using operator ==
just adds unnecessary indirection and wouldn't even work the way you presented it.
Swift idiomatic way to guard both aren't nil
A guard
is an if
, really, so you can do this the same way. This is clear and uses guard, which seems to be part of the fun. I present OR and AND options so you can pick one.
func doItOr(a: Int?, b:Int?) {
guard (a != nil || b != nil) else { return }
print("either A and B is not nil");
}
func doItAnd(a: Int?, b:Int?) {
guard (a != nil && b != nil) else { return }
print("both A and B are not nil");
}
doItOr(nil, b: nil)
doItOr(nil, b: 5)
doItOr(4, b: 5) // prints
doItAnd(nil, b: nil)
doItAnd(nil, b: 5)
doItAnd(nil, b: nil)
doItAnd(4, b: 5) // prints
In Swift, how can you test if an object implements an optional protocol method which differs by signature without actually calling that method?
As also shown in How do I resolve "ambiguous use of" compile error with Swift #selector syntax?, you can explicitly coerce a function reference to its expected type in order to resolve such ambiguities.
The only difference being, as such function references are to @optional
protocol requirements done through optional chaining, you need to coerce to the optional type of the function. From there, you can do a comparison with nil
in order to determine if both the delegate is non-nil, and it implements the given requirement.
For example:
import Foundation
@objc public protocol TestDelegate : AnyObject {
@objc optional func test()
// Need to ensure the requirements have different selectors.
@objc(testWithString:) optional func test(with string: String)
@objc(testWithInt:) optional func test(with int: Int)
}
class C : TestDelegate {
func test() {}
func test(with someString: String) {}
func test(with someInt: Int) {}
}
var delegate: TestDelegate? = C()
if delegate?.test as (() -> Void)? != nil {
print("supports 'test'")
}
if delegate?.test(with:) as ((String) -> Void)? != nil {
print("supports 'test w/ String'")
}
if delegate?.test(with:) as ((Int) -> Void)? != nil {
print("supports 'test w/ Int'")
}
// supports 'test'
// supports 'test w/ String'
// supports 'test w/ Int'
Note that I've given the test(with:)
requirements unique selectors in order to ensure they don't conflict (this doesn't affect the disambiguation, only allowing class C
to conform to TestDelegate
).
Determine if Any.Type is Optional
Assuming that what you are trying to do is something like this:
let anyType: Any.Type = Optional<String>.self
anyType is Optional<Any>.Type // false
Sadly swift currently (as of Swift 2) does not support covariance nor contravariance and type checks directly against Optional.Type
cannot be done:
// Argument for generic parameter 'Wrapped' could not be inferred
anyType is Optional.Type // Causes error
An alternative is to make Optional
extend an specific protocol, and check for that type:
protocol OptionalProtocol {}
extension Optional : OptionalProtocol {}
let anyType: Any.Type = Optional<String>.self
anyType is OptionalProtocol.Type // true
Downcasting and optional: is this code idiomatic?
This is the Swift way (it is both safe and nice):
if (response as? NSHTTPURLResponse)?.statusCode == 200 {
It uses conditional casting and optional chaining to both test that the class is NSHTTPURLResponse
and that the statusCode
is 200
. Note that you don't need !
after NSHTTPURLResponse
.
If response
is another class, then (response as? NSHTTPURLResponse)
will return nil
, thus the entire optional chain will return nil
and since nil != 200
the test will fail.
Check if string contains optional string in Swift, but only if it's not nil
There is a map
on Optional that executes the provided closure only if the optional is not nil
. You can use map
along with the nil coalescing operator ??
to provide the default value of true
if the map
returns nil
because the filter
is nil
:
func process(items: [String], filter: String?) {
for item in items {
if filter.map(item.contains) ?? true {
// process item
}
}
}
how to compare value in swift optional
I presume you mean that you would like to test for someString != nil
and someString != "a"
in a single logical expression (and not two in and).
No, I don't think that's possible using the built in operators, but doable implementing a String extension like this:
extension String {
func isDifferentThan(value: String?) -> Bool {
return value != nil && self != value?
}
}
and you can use as follows:
someString = nil
"a".isDifferentThan(someString) // Return false
someString = "b"
"a".isDifferentThan(someString) // Return true
someString = "a"
"a".isDifferentThan(someString) // Return false
Addendum: A more elegant solution is to define your own logical operator. I have used !~=
but feel free to use your own.
infix operator !~= { associativity left }
func !~= (a: String?, b: String?) -> Bool {
if a == nil || b == nil {
return false
}
return a != b
}
Tested as follows:
someString = nil
someString !~= "a" // Returns false
someString = "b"
someString !~= "a" // Returns true
someString = "a"
someString !~= "a" // Returns false
someString = nil
someString !~= nil // Returns false
You can fine tune it when dealing with nil
values (such as add a check for both having nil
and returning true
, in case you want the condition "both sides are nil" to evaluate to true)
Idiomatic way to unwrap an integer string input
There are two optionals at play here.
First, readLine(strippingNewline: true)
is optional. It can return nil
if there's no input recieved prior to the End of File
(EOF
) character being received. It must be unwrapped before being passed into Int()
Secondly, Int()
is optional, because the String
it was given may not be a valid string representation of a number.
Do not use -1
in Swift to represent "no value". This is called a sentinel value, and it's exactly what optionals are invented to prevent. How do you distinguish between a -1
meaning "no/invalid input", and a -1
meaning "the user's input was -1
?
Here is how I would write this code:
guard let userInput = readLine(strippingNewline: true) else {
// If we got to here, readLine(strippingNewLine:) returned nil
fatalError("Received EOF before any input was given")
}
// If we got to here, then userInput is not nil
if let n = Int(userInput) {
// If we got to here, then userInput contained a valid
// String representation of an Int
print("The user entered the Int \(n)")
}
else {
// If we got to here, then userInput did not contain a
// valid String representation of an Int.
print("That is not a valid Int.")
}
Check for non-optional values for nil in Swift
Even Apple's APIs sometimes return nil
for a type that is not marked in the API as Optional. The solution is to assign to an Optional.
For example, for a while traitCollectionDidChange
returned a UITraitCollection even though it could in fact be nil
. You couldn't check it for nil
because Swift won't let you check a non-Optional for nil
.
The workaround was to assign the returned value immediately to a UITraitCollection?
and check that for nil
. That sort of thing should work for whatever your use case is as well (though your mail example is not a use case, because you're doing it wrong from the get-go).
Related Topics
How to Pass Text from Cell to Textview in Another View Controller
Urlsession Datatask Method Returns 0 Bytes of Data
How to Upload Images from The Browser to Amazon S3 Using Vapor and Leaf
Proper Way of Editing a Cocoapod Library
How to Prevent SQL Injections with User-Search-Terms in Vapor 4 (Fluent 4)
Linking Pages in Swift Playgrounds [Xcode]
No Trailing Closures Support for Methods with Default Parameter Values
Parse Migration Error to Mlabs and Heroku
Do Not Copy Swift Libraries with Xcode 8
Swift Equivalent of Objective-C for Flutter Native Ads
How to Delay a for Loop in Swift Without Interrupting The Main Thread
The "Funk" Sound When Hitting Escape Key in App
Swift: Get The Compile Time Name of Variable (Referencing to a Class)
How to Detect When Two Objects Touch in Spritekit
Realitykit - Keep Object Always in Front of Screen
Map Dictionary Keys to Add Values - Swift
Xcode + Swift + Darwin.Ncurses = "A_Bold Not Found" Compilation Error. I Can't Get Bright Colors