Photo by Gary Butterfield on Unsplash
This has started as a code review comment at work and a comment from my colleague that I should make a package out of it. This does not save the world is fancy or clever. But it’s something that removes some annoying thing that I had wrote in code many times.
I almost forgot the most important part. It’s optional, so you do not have to use it ๐ค
OptionalAPI Swift Package
Link to the repository is there.
Installation
Just copy and paste contents of OptionalAPI.swift
file ๐ Or use SPM search in Xcode ๐or add to the dependencies:
.package(path: "https://github.com/sloik/OptionalAPI.git")
Examples
Running some code if none or some
Old:
someOptional == nil ? True branch : False branch
New:
someOptional.isSome ? True branch : False branch someOptional.isNone ? True branch : False branch someOptional.isNotSome ? True branch : False branch someOptional.isNotNone ? True branch : False branch
Sequencing of operations that might also return optional
Operation that returns optional:
func maybeIncrement(_ i: Int) -> Int? { i + 1 }
Old the terrible way:
if let trueInt = someIntOptional { let incrementedOnce = maybeIncrement(trueInt) { // you get the idea ;) } }
New:
someOptional .andThen(maybeIncrement) .andThen(maybeIncrement) // ... you get the idea :)
In this case result of this chaining is an instance of Int?
. If the someOptional
was nil then whole computation results with nil. If it had some value (42) then it would be incremented so many times.
Recovering from none
case
Let’s say you have a chain of operations and there is a chance that the result might return none
.
func returningNone(_ i: Int) -> Int? { Bool.random() ? .none : i } someOptional .andThen(maybeIncrement) .andThen(returningNone) // <-- returns nil .andThen(maybeIncrement)
Final result is nil
. And you can’t use a ??
. Use mapNone
it’s like normal map
on Optional but for the nil
case.
func returningNone(_ i: Int) -> Int? { .none } someOptional .andThen(maybeIncrement) .andThen(returningNone) .mapNone(42) .andThen(maybeIncrement)
If someOptional
started with 10
and we had luck (returningNone did not return nil) then the final result is 12
. But if we’re not so lucky then the mapNone
would take over and the final result would be 43
.
You can also use more than one mapNone to handle any failures along the way. Oh, and you can use a more friendly name defaultSome
like so:
someOptional // if someOptional is nil then start computation with defaultSome value .defaultSome(5) // increment whatever is there .andThen(maybeIncrement) // are you feeling lucky? .andThen(returningNone) // cover your ass if you had bad luck .defaultSome(42) // do some work with what's there .andThen(maybeIncrement) // what... again .andThen(returningNone) // saved .defaultSome(10)
I hope you can see that this gives you a very flexible API to handle Optionals in your code.
But wait there’s more!
Sometimes you are working with an Optional collection. The most common case is a String and Optional Array of something. This Optional API has you covered too!
In the examples below I will be using those Optionals:
let noneString : String? = .none let emptySomeString: String? = "" let someSomeString : String? = "some string" let noneIntArray : [Int]? = .none let emptyIntArray: [Int]? = [] let someIntArray : [Int]? = [11, 22, 33]
I think this should cover all the cases.
Optional collection has values, is nil or empty
A lot of ifology is made when working whit a collection inside an Optional context. Those properties should help.
hasElements
noneString.hasElements // false emptySomeString.hasElements // false someSomeString.hasElements // true noneIntArray.hasElements // false emptyIntArray.hasElements // false someIntArray.hasElements // true
isNoneOrEmpty
noneString.isNoneOrEmpty // true emptySomeString.isNoneOrEmpty // true someSomeString.isNoneOrEmpty // false noneIntArray.isNoneOrEmpty // true emptyIntArray.isNoneOrEmpty // true someIntArray.isNoneOrEmpty // false
recoverFromEmpty
This is called only if the underlying collection is empty. That is if your optional is nil
or has some value this will not be called. As String is a collection I will only show examples for [Int]?
๐
noneIntArray.recoverFromEmpty([42]) // nil emptyIntArray.recoverFromEmpty([42]) // [42] someIntArray.recoverFromEmpty([42]) // [11, 22, 33]
If you need a defaultSome value for the none and empty case then defaultSome is the thing you want.
noneIntArray.defaultSome([42]) // [42] emptyIntArray.defaultSome([42]) // [42] someIntArray.defaultSome([42]) // [11, 22, 33]
That’s it
I hope it will help you ๐ Checkout the repository README and tests to get a better understanding of what you do not have to type anymore.
Cheers ๐ธ