Extend "emptiness" test to Optional Collections

I wrote about a convenience method on Optional where Wrapped: Collection, such that "isEmpty" is evaluated to return true when an optional collection variable contains nil:

tl;dr: implementing isEmpty on Collection? facilitates tests on nullable properties like this:

if myTextField.text.isEmpty {
    // Do something
}

Whereas without such support, more cumbersome and hard to reasons tests such as these have to be used:

if myTextField.text?.isEmpty != false {
    // Do something
}

if (myTextField.text ?? "").isEmpty {
    // Do something
}

It seems to me that such an affordance is not only particularly useful in translating code from Objective-C idioms to Swift, but leads to overall easier to understand code in all circumstances where "emptiness" of an optional collection value is desired to be known.

I could also see an argument for extending other Collection properties such as "count" to Optional, but "isEmpty" in particular seems to fit the spirit of optionality and it doesn't seem like its meaning in that context would be ambiguous.

What do folks think? It would be a tiny addition to Optional in the standard library that I think would benefit a large number of developers, particularly when dealing with Apple frameworks where the choice of whether particular properties are nullable or not is not up to the developer.

Daniel

1 Like

I have extensions on Optional that do the same, but I prefer isNilOrEmpty to make it clear that the test is against nil as well.

2 Likes

I also have a similar extension.

Are there any examples you have where you use this when not interacting with UIKit/AppKit?


I ask because the times I've used this in the past are due to unfortunate UIKit APIs. For example, a UITextField's text property is a String?, but it's actually set to empty string by default. Even things like UILabel, UITextView, all could have text properties that just returned empty strings, but, for perhaps legacy reasons, can no longer change.

I personally use this with CoreData, various JSON responses, etc. The usage extends beyond AppKit/UIKit.

I think there's some room for improvement of Optional<Sequence> - if I had more time (or if the Evolution process would be less painful ;-), I would have written similar a proposal:

let test: [Int]? = nil

// this is possible now
if let test = test {
	for i in test {
		print(i)
	}
}

// how it could be written with a modified keyword
for i in? test {
	print(i)
}

// or alternatively
for i in test? {
	print(i)
}

When you can't avoid the case of .none for a collection, it would be very convenient to easily handle that like the empty case.

2 Likes

Here's another:

if myTextField.text?.isEmpty ?? true {
    // Do something
}

Is this also cumbersome for you?

1 Like

I have an extension on Optional<Collection> that contains a property values - this will return either [] or the values from the collection. This allows something like:

for i in test.values {
    print(i)
}

Which IMHO is better than extending the language.

1 Like

Is this also cumbersome for you?

It's all relative of course, but I think it's obviously more cumbersome than the proposed solution.

If only we had a way to say that a type could be treated as a Bool in a condition :wink:. we could possibly write the following with our magical protocol:

extension Optional: BooleanType where Wrapped == Bool {
  var boolValue: Bool {
    return self ?? false
  }
}

if myTextField.text?.isEmpty {

}
2 Likes

I'm trying to decide if .none for a Collection? meaning that it is empty is significantly different to 0 for an integer meaning it is false, etc. I think that the general Swift philosophy would be that equating empty collections and non-existent collections causes confusion. e.g. the feature that @Letan refers to to make types truthy in this way was removed in an early Swift version.

So if we were purely considering greenfield Swift code I would suggest that the Collection should just not be Optional in the first place, but I'm presuming this is a UIKit/AppKit/Objective-C issue, as @Scott mentions. In other words, this doesn't make sense to me in the abstract, but I guess it's possible that these external factors could make this worthwhile.

@Letan - Unfortunately, that would still require some ugly parentheses:

if (myTextField.text?.isEmpty).boolValue - and the assumption that nil implies false is not always correct, whereas isNilOrEmpty is self-explanatory.

@jawbroken - I don't think that this case is restricted to ObjC-based codebase - in JSONs, databases, etc., you can have optional collections. This would allow something like:

if (jsonDictionary["values"] as? [Any]).isNilOrEmpty { ... }

Agreed.

I also agree with @charlieMonroe that isNilOrEmpty is a more clear name for this, due to that idea.

FWIW it wasn't a serious suggestion as this actually refers to a removed feature from Swift.

Haven't checked the status for some time ;-)

But I guess the extension way is slower than if let - and although performance surely is good enough for nearly all cases, imho it's unfortunate when the most convenient way isn't the best way as well.

Bang! You're dead. I would think most people would want "someCollectionOptional?.isEmpty" to default to true.

We fundamentally can't support this because there isn't a universal default for all applications.

Application-specific defaults is what we have "someOptional ?? myDefault" for. If there isn't an applicable default, then the OP should have used a guard and bailed when s/he got a nil.