Hacker News new | past | comments | ask | show | jobs | submit login
Extending optionals in Swift (swiftbysundell.com)
69 points by zacwood on Nov 18, 2018 | hide | past | favorite | 18 comments



I’m not particularly impressed by of any of these extensions, because they don’t seem to actually fix the underlying problems. I used to write code that was overly defensive around Optionals, and I’d carefully do things like wrapping in flatMap or throw guards in, but I’ve come to realize that this just ends up hiding errors in the cases where you know something is supposed to have a value. For this article in particular, I think there are better ways to do what he’s trying to do:

For the converting an Optional to an error example, he could just put both statements in one guard. Sure, being able to show that you’re passing a function into another is pretty, but it really is overkill for something that doesn’t even need an extension to Optional.

I think isNilOrEmpty, personally, just encourages papering over bad APIs: it is never correct to represent an empty Collection with nil. UITextField.text should never be nil (to the point where I think that this method is improperly annotated; I can go check though); if it is your view hierarchy is in a state where asking it for its text returns nil this should be a hard error because you must have messed up somewhere. Just force unwrap in this case, and if you’re designing an API, think twice about returning Collection?.

Lastly, get(orSet:) is just a skirting around the issue. Forget to set the value of this view? Just set it again! Just ignore the fact that a clear logic error that must have occurred if I ended up at a point where I’m trying to access a view that doesn’t exist.


These are my rules of thumb in priority order:

1. Don’t use optionals.

2. If you must use optionals due to technicalities (e.g. IBOutlet, weak reference, etc.), make it implicitly-unwrapped.

3. If you can’t make it implicitly unwrapped due to ‘nil’ being a legitimate value, then use regular optionals, but force-unwrap it everywhere except where `nil` is expected to occur.

There’s nothing worse than a codebase unnecessarily littered with

    guard let x = x else { return }
If my assumptions about some value were wrong, I want it to break as loud as possible. Silently returning (or making up some silly default value like an empty string or zero) is the worst thing you can do for yourself and your colleagues. (Except if you’re a manager whose bonus is tied to your app’s crash stats)


Your approach seems to be a completely wrong use of optionals (have you been writing in Java or any other language with implicit nullability?) since you’re giving up type-safety of Swift. Do you use implicitly unwrapped optionals for delegates too?


>since you’re giving up type-safety of Swift

Giving up type-safety? Sorry, but I don't understand this in the current context.

As for delegates: if the delegate can be 'nil' during normal operations, then it should be a regular optional as per rule #3.


Delegates should be nil, so it totally makes sense to keep them as an optional. I don’t see this conflicting with the parent’s set of “rules”.


isNilOrEmpty is great, though I prefer my variation nilEmpty that turns empty things into nils. Slightly more flexible/chainable. The reality is that there are a lot of cases where the empty state could be nil or an empty collection/string/whatever. I try not to add to them, preferring to use the empty collection over nil, however handling both is generally a lot easier and safer than trying to prove that the data from some other system can't be dumb.


Agree with most of it, having .(unwrap)orThrow() seems nice syntax sugar though.


I'm not a huge fan of Optional.matching (also commonly called Optional.filter) because you may want to "filter" non-optionals as well, i.e. T -> Optional<T> given a predicate. Unfortunately Swift doesn't (yet?) allow writing extensions on any generic T and the alternatives are a free function and an Optional initialiser, neither of which allows you to easily chain them. (Another alternative is a custom operator, but let's not.)

So perhaps the best we can do is Optional.matching as defined in the post, combined with turning a non-optional into an optional using Optional(...) in case we want to "filter" non-optional values using a predicate.


> Unfortunately Swift doesn't (yet?) allow writing extensions on any generic T

I'm keen to understand the use case for this kind of code, where T literally conforms to zero protocols. Would you not create a protocol with a default implementation and then conform to that in this case? An example would help me a lot!


Uniform Call Syntax would solve this elegantly: https://en.m.wikipedia.org/wiki/Uniform_Function_Call_Syntax


The way I do this in Rust is `Some(...).filter(...)`. I personally like this over having generic extensions, as it makes it very clear that its doing filtering that produces an optional, rather than filtering through a list or other iterable.


I'm somewhat surprised that the Swift Optional doesn't come with `orElseThrow` or `filter`. I mean, even the Java Optional has those combinators.


filter -> flatMap / compactMap without arguments

orElseThrow is something you might need more often in Java, Swift handles optionals with if let / else checks or Result enums instead of functions that either result an object or throw an Exception. Throwing errors / exceptions is pretty rare in the language compared to the Java / C# world.

Only when doing stuff like mapping JSON I set up try / throw trees because in the end I might not care about the Error at all and just discard it.


And with Codable times of manually mapping JSON to objects are over, at least you have to deal with something sane.


I really needed stuff like snake_case support



Right, I said needed, not need ;)


CodableKeys should let you handle this.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: