How to test the Optionality of a String? - swift

I have two different scenarios where I need to test the "optionality" of an optional type. I have not been able to figure how to explicitly test if the variable is a .None or a .Some other than with an unwieldy switch statement. How can I test for Someness with an if statement?
Scenario 1
I am writing an address formatter and my inputs are a number of String? types. In this example a simple test for (str != nil) will work. However, since my other need is when dealing with a 'double optional' and a nil test can't distinguish between .Some(.None) and .None a solution to this problem will solve that problem too.
Here's a version that works using a switch
let address1:String? = "123 Main St"
let address2:String? = nil
let apt:String? = "101"
let components = [address1, address2, apt].filter( { (c) -> Bool in
switch c {
case .Some: return true
case .None: return false
}
}).map { return $0! } //Had to map because casting directly to [String] crashes
print(", ".join(components)) //"123 Main St, 101"
What's I'd like to see is something like with an if:
let nice = ["123 Main St", nil, "303"].filter { (c) -> Bool in
return (c == .Some)
}
print(", ".join(nice))
Scenario 2
This is where a nil test won't work. If something is a String?? it can be any of .None, .Some(.None), or .Some(.Some(String)). In my case, the variable is carrying the recordID from an api call which might either be missing entirely (.None), a value (.Some(.Some("ABDEFG")), or explicitly NULL (.Some(.None)).
let teamNoneNone: String?? = .None
let teamSomeNone: String?? = .Some(.None)
let teamSomeSome: String?? = "My favorite local sportsball team"
if teamNoneNone == nil {
print("teamNoneNone is nil but is it .None? We don't know!") //prints
} else {
print("teamNoneNone is not nil")
}
if teamSomeNone == nil {
print("teamSomeNone is nil")
} else {
print("teamSomeNone is not nil but is it .Some(.None)? We don't know!") //prints
}
if teamSomeSome == nil {
print("teamSomeSome is nil but is it .None? We don't know!")
} else {
print("teamSomeSome is not nil but is it .Some(.None) or .Some(.Some())? We don't know!") //prints
}
Via another SO post I found a workaround like this, but it's not very clear what's happening to a casual reader:
if let team: String? = teamSomeNone {
print("teamSomeNone is Some(.None)") //prints
} else {
print("teamSomeNone is .Some(.Some())")
}

if let tests if a value is .None, and if it isn’t, it unwraps it and binds it to a local variable within an if statement.
Using switch with .Some and .None is really a secondary way of handling optionals, if if let doesn’t cut it. But it almost always does, especially now you can do multiple if lets in a single statement, following the latest release of Swift 1.2 to production.
Wanting to filter out the nils in a collection is a common-enough task that Haskell has a standard function for it, called catMaybe. Here’s a version, which I’ll call catSome, that would do the trick in Swift:
func catSome<T>(source: [T?]) -> [T] {
var result: [T] = []
// iterate over the values
for maybe in source {
// if this value isn’t nil, unwrap it
if let value = maybe {
// and append it to the array
result.append(value)
}
}
return result
}
let someStrings: [String?] = ["123 Main St", nil, "101"]
catSome(someStrings) // returns ["123 Main St", "101"]
Doubly-wrapped optionals are a bit of a pain, so the best solution is to avoid them in the first place – often, via use of optional chaining or flatMap.
But if you do find yourself with some, and all you care about is the inner value, you can unwrap them using a double if let:
// later parts of the let can rely on the earlier
if let outer = teamSomeSome, teamName = outer {
println("Fully unwrapped team is \(teamName)")
}
If you want to explicitly know if a double-optional has an inner nil inside an outer value, but isn’t nil itself, you can use if let with a where clause:
if let teamSomeMaybe = teamSomeNone where teamSomeMaybe == nil {
// this will be executed only if it was .Some(.None)
println("SomeNone")
}
The where clause is an extra conditional that can be applied to the unwrapped value.

Related

if let with try? gives optional value [duplicate]

I am using an SQLite library in which queries return optional values as well as can throw errors. I would like to conditionally unwrap the value, or receive nil if it returns an error. I'm not totally sure how to word this, this code will explain, this is what it looks like:
func getSomething() throws -> Value? {
//example function from library, returns optional or throws errors
}
func myFunctionToGetSpecificDate() -> Date? {
if let specificValue = db!.getSomething() {
let returnedValue = specificValue!
// it says I need to force unwrap specificValue,
// shouldn't it be unwrapped already?
let specificDate = Date.init(timeIntervalSinceReferenceDate: TimeInterval(returnedValue))
return time
} else {
return nil
}
}
Is there a way to avoid having to force unwrap there? Prior to updating to Swift3, I wasn't forced to force unwrap here.
The following is the actual code. Just trying to get the latest timestamp from all entries:
func getLastDateWithData() -> Date? {
if let max = try? db!.scalar(eventTable.select(timestamp.max)){
let time = Date.init(timeIntervalSinceReferenceDate: TimeInterval(max!))
// will max ever be nil here? I don't want to force unwrap!
return time
} else {
return nil
}
}
Update: As of Swift 5, try? applied to an optional expression does not add another level of optionality, so that a “simple” optional binding is sufficient. It succeeds if the function did not throw an error and did not return nil. val is then bound to the unwrapped result:
if let val = try? getSomething() {
// ...
}
(Previous answer for Swift ≤ 4:) If a function throws and returns an optional
func getSomething() throws -> Value? { ... }
then try? getSomething() returns a "double optional" of the
type Value?? and you have to unwrap twice:
if let optval = try? getSomething(), let val = optval {
}
Here the first binding let optval = ... succeeds if the function did
not throw, and the second binding let val = optval succeeds
if the return value is not nil.
This can be shortened with case let pattern matching to
if case let val?? = try? getSomething() {
}
where val?? is a shortcut for .some(.some(val)).
I like Martin's answer but wanted to show another option:
if let value = (try? getSomething()) ?? nil {
}
This has the advantage of working outside of if, guard, or switch statements. The type specifier Any? isn't necessary but just included to show that it returns an optional:
let value: Any? = (try? getSomething()) ?? nil

Getting the value of a property using it's string name in pure Swift using reflection

I want to use Swift (not Objective-C runtime) Reflection to create a method like this:
func valueFor(property:String, of object:Any) -> Any? {
...
}
To some extent, I can do this using:
func valueFor(property:String, of object:Any) -> Any? {
let mirror = Mirror(reflecting: object)
return mirror.descendant(property)
}
With
class TestMe {
var x:Int!
}
let t = TestMe()
t.x = 100
let result = valueFor(property: "x", of: t)
print("\(result); \(result!)")
This prints out what I'd expect:
Optional(100); 100
When I do:
let t2 = TestMe()
let result2 = valueFor(property: "x", of: t2)
print("\(result2)")
The output is:
Optional(nil)
This might seem reasonable, except that if I do:
var x:Int!
print("\(x)")
This prints out:
nil
and not Optional(nil). The bottom line is that I'm having difficulty programmatically determining that the value of t2.x is nil using my valueFor method.
If I continue the above code with:
if result2 == Optional(nil)! {
print("Was nil1")
}
if result2 == nil {
print("Was nil2")
}
Neither of these print statements output anything.
When I put a breakpoint into Xcode and look at the value of result2 with the debugger, it shows:
▿ Optional<Any>
- some : nil
So, my question is: How can I determine if the original member variable was nil using the result from valueFor?
Additional1:
If I do:
switch result2 {
case .some(let x):
// HERE
break
default:
break
}
and put a breakpoint at HERE, the value of x turns out to be nil. But, even if I assign it to an Any?, comparing it to nil is not true.
Additional2:
If I do:
switch result2 {
case .some(let x):
let z:Any? = x
print("\(z)")
if z == nil {
print("Was nil3")
}
break
default:
break
}
This prints out (only):
Optional(nil)
I find this especially odd. result2 prints out exactly the same thing!
This is a bit of a hack, but I think it's going to solve the problem for me. I'm still looking for better solutions:
func isNilDescendant(_ any: Any?) -> Bool {
return String(describing: any) == "Optional(nil)"
}
func valueFor(property:String, of object:Any) -> Any? {
let mirror = Mirror(reflecting: object)
if let child = mirror.descendant(property), !isNilDescendant(child) {
return child
}
else {
return nil
}
}
well, i know it has been 4 years, but I am on Xcode 12 and still facing the same issue. since this question seems to be unanswered, I will add what worked for me.
func valueFor(property: String, of object: Any) -> Any? {
let optionalPropertyName = "some"
let mirror = Mirror(reflecting: object)
if let child = mirror.descendant(property) {
if let optionalMirror = Mirror(reflecting: child), optionalMirror.displayStyle == DisplayStyle.optional {
return optionalMirror.descendant(optionalPropertyName)
} else {
return child
}
} else {
return nil
}
}
by using Mirror to check for optional and then extract the optional using "some" you get back either a true object or nil. when this is returned to the caller via the Any? return, you are now able to nil check the value and have that work appropriately.

Function throws AND returns optional.. possible to conditionally unwrap in one line?

I am using an SQLite library in which queries return optional values as well as can throw errors. I would like to conditionally unwrap the value, or receive nil if it returns an error. I'm not totally sure how to word this, this code will explain, this is what it looks like:
func getSomething() throws -> Value? {
//example function from library, returns optional or throws errors
}
func myFunctionToGetSpecificDate() -> Date? {
if let specificValue = db!.getSomething() {
let returnedValue = specificValue!
// it says I need to force unwrap specificValue,
// shouldn't it be unwrapped already?
let specificDate = Date.init(timeIntervalSinceReferenceDate: TimeInterval(returnedValue))
return time
} else {
return nil
}
}
Is there a way to avoid having to force unwrap there? Prior to updating to Swift3, I wasn't forced to force unwrap here.
The following is the actual code. Just trying to get the latest timestamp from all entries:
func getLastDateWithData() -> Date? {
if let max = try? db!.scalar(eventTable.select(timestamp.max)){
let time = Date.init(timeIntervalSinceReferenceDate: TimeInterval(max!))
// will max ever be nil here? I don't want to force unwrap!
return time
} else {
return nil
}
}
Update: As of Swift 5, try? applied to an optional expression does not add another level of optionality, so that a “simple” optional binding is sufficient. It succeeds if the function did not throw an error and did not return nil. val is then bound to the unwrapped result:
if let val = try? getSomething() {
// ...
}
(Previous answer for Swift ≤ 4:) If a function throws and returns an optional
func getSomething() throws -> Value? { ... }
then try? getSomething() returns a "double optional" of the
type Value?? and you have to unwrap twice:
if let optval = try? getSomething(), let val = optval {
}
Here the first binding let optval = ... succeeds if the function did
not throw, and the second binding let val = optval succeeds
if the return value is not nil.
This can be shortened with case let pattern matching to
if case let val?? = try? getSomething() {
}
where val?? is a shortcut for .some(.some(val)).
I like Martin's answer but wanted to show another option:
if let value = (try? getSomething()) ?? nil {
}
This has the advantage of working outside of if, guard, or switch statements. The type specifier Any? isn't necessary but just included to show that it returns an optional:
let value: Any? = (try? getSomething()) ?? nil

swift reflection causes impossible nil value for any

I'm trying to use swift reflection to check for changes in objects so I can send only changed properties up to the server. Some of my properties are optional. To compare those values, I need to unwrap them but, of course, you can ONLY unwrap actual values, not nil values. So, I need to check if one of the values is nil before I compare them.
In my playground, I tried the following:
import UIKit
class myClass
{
var fieldOne:String?
var fieldTwo:Int?
var fieldThree:Float?
}
var oneMyClass = myClass()
oneMyClass.fieldOne = "blah"
oneMyClass.fieldThree = 3.5
var oneOtherClass = myClass()
oneOtherClass.fieldOne = "stuff"
oneOtherClass.fieldTwo = 3
let aMirror = Mirror(reflecting: oneMyClass)
let bMirror = Mirror(reflecting: oneOtherClass)
for thing in aMirror.children
{
for thing2 in bMirror.children
{
if thing.label! == thing2.label!
{
print("property: \(thing.label!)")
print("before: \(thing.value)")
print("after: \(thing2.value)")
print("")
//let myTest = thing.value == nil ? "nil" : "not nil"
}
}
}
And it generates the following output:
property: fieldOne
before: Optional("blah")
after: Optional("stuff")
property: fieldTwo
before: nil
after: Optional(3)
property: fieldThree
before: Optional(3.5)
after: nil
As you can see, the expected properties are displayed as "nil". However, if you uncomment the let statement, you get an error stating:
playground52.swift:37:38: error: value of type 'Any' (aka 'protocol<>') can never be nil, comparison isn't allowed
And yet, we know from the output that it IS nil. How can this be and what can I do about it?
Based on this answer, I recommend using if case Optional<Any>.some(_).
For example:
aMirror.children.forEach {
guard let propertyName = $0.label else { return }
if case Optional<Any>.some(_) = $0.value {
print("property: \(propertyName) is not nil")
} else {
print("property: \(propertyName) is nil")
}
}
Thats look like some sort of bug. Look at that
let x = childMirror.value == nil ? "Nil" : "Not Nil" //dont compile.
let y = { (value:Any?) in
return value == nil ? "Nil" : "Not Nil"
}
let z = y(childMirror.value) //compile, but doesn't evaluate.
I guess the problem is because Any can store a Optional, but can't be wrapped around one. Try this:
func getValue(unknownValue:Any) -> Any {
let value = Mirror(reflecting: unknownValue)
if value.displayStyle != .Optional || value.children.count != 0 {
return "Not Nil"
} else {
return "Nil"
}
}

unwrapping multiple optionals in if statement

I want to unwrap two optionals in one if statement, but the compiler complaints about an expected expression after operator at the password constant.
What could be the reason?
if let email = self.emailField?.text && let password = self.passwordField?.text
{
//do smthg
}
Done in Swift.
Great news. Unwrapping multiple optionals in a single line is now supported in Swift 1.2 (XCode 6.3 beta, released 2/9/15).
No more tuple/switch pattern matching needed. It's actually very close to your original suggested syntax (thanks for listening, Apple!)
if let email = emailField?.text, password = passwordField?.text {
}
Another nice thing is you can also add where for a "guarding condition":
var email: String? = "baz#bar.com"
var name: String? = "foo"
if let n = name, e = email where contains(e, "#") {
println("name and email exist, email has #")
}
Reference: XCode 6.3 Beta Release Notes
Update for Swift 3:
if let email = emailField?.text, let password = passwordField?.text {
}
each variable must now be preceded by a let keyword
How about wrapping the optionals in a tuple and using switch to pattern match?
switch (self.emailField?.text, self.passwordField?.text) {
case let (.Some(email), .Some(password)):
// unwrapped 'email' and 'password' strings available here
default:
break
}
It's definitely a bit noisier, but at least it could also be combined with a where clause as well.
The usage
if let x = y {
}
is not equivalent to
if (let x = y) { // this is actually not allowed
}
"if let" is effectively a two-word keyword, which is equivalent to
if y != nil {
let x = y!
// rest of if let block
}
Before Swift 1.2
Like #James, I've also created an unwrap function, but this one uses the existing if let for control flow, instead of using a closure:
func unwrap<T1, T2>(optional1: T1?, optional2: T2?) -> (T1, T2)? {
switch (optional1, optional2) {
case let (.Some(value1), .Some(value2)):
return (value1, value2)
default:
return nil
}
}
This can be used like so:
if let (email, password) = unwrap(self.emailField?.text, self.passwordField?.text)
{
// do something
}
From: https://gist.github.com/tomlokhorst/f9a826bf24d16cb5f6a3
Note that if you want to handle more cases (like when one of the two fields is nil), you're better off with a switch statement.
Swift 4
if let suggestions = suggestions, let suggestions1 = suggestions1 {
XCTAssert((suggestions.count > suggestions1.count), "TEST CASE FAILED: suggestion is nil. delete sucessful");
}
I can't explain why the above code doesn't work, but this would be good a replacement:
if let email = self.emailField?.text
{
if let password = self.passwordField?.text
{
//do smthg
}
}
Based on #Joel's answer, I've created a helper method.
func unwrap<T, U>(a:T?, b:U?, handler:((T, U) -> ())?) -> Bool {
switch (a, b) {
case let (.Some(a), .Some(b)):
if handler != nil {
handler!(a, b)
}
return true
default:
return false
}
}
// Usage
unwrap(a, b) {
println("\($0), \($1)")
}