1

I'm using the logic below to check the state of my subject using RxBlocking. I'm getting a weird value of Int?? out of try? subject.verifier.toBlocking().first().

The syntax below pleases the compiler but makes my eyes bleed.

How do I get an unwrapped value out of RXBlocking expectation?

func checkSubjectState() -> Int
    {
      let updatedChecksum = try? subject.verifier.toBlocking().first() ?? 0
      return updatedChecksum ?? 0
    }

let expectedCheckSum = checkSubjectState()
expect(expectedCheckSum).to(equal(knownValue))
Alex Stone
  • 46,408
  • 55
  • 231
  • 407
  • There you go, three different answers. Accept whichever you like best. :-) – Daniel T. Sep 01 '18 at 11:28
  • Strictly speaking this is a duplicate of https://stackoverflow.com/questions/33049246/how-to-unwrap-double-optionals – Daniel T. Sep 01 '18 at 11:34
  • I want this question to be related to RxBlocking, as this syntax pops up in some examples and people may be confused as to how to deal with it. I think Quick/Nimble handles comparison between Int and Int? as expected, but problems arise as I use the result in print statements and such. I expect ?? 0 to produce a real zero Int, but it seems to produce an optional zero Int – Alex Stone Sep 01 '18 at 11:38
  • I agree which is why I didn't recommend removing the question. I mentioned the relationship because there are several different ways to solve this problem as outlined on that question. – Daniel T. Sep 01 '18 at 16:28
  • Possible duplicate of [How to unwrap double optionals?](https://stackoverflow.com/questions/33049246/how-to-unwrap-double-optionals) – E-Riddie Sep 03 '18 at 14:57

4 Answers4

2

Yea, so first() can throw and it returns an Optional type, so try? first() returns an Optional<Optional<Int>> or Int??. Frankly, I think the first() function is poorly written. It should either throw or return optional, not both.

You could write a flattened operator:

public protocol OptionalType {
    associatedtype Wrapped
    var value: Wrapped? { get }
}

extension Optional: OptionalType {
    public var value: Wrapped? {
        return self
    }
}

extension Optional where Wrapped: OptionalType {
    var flattened: Wrapped.Wrapped? {
        switch self {
        case let .some(opt):
            return opt.value
        case .none:
            return nil
        }
    }
}

Which would allow you to do:

let expectedCheckSum = (try? subject.verifier.toBlocking().first()).flattened ?? 0

Whether you think it's worth it or not is your call.

Daniel T.
  • 32,821
  • 6
  • 50
  • 72
2

Here's an answer that doesn't require you to define any of your own functions:

return (try? subject.verifier.toBlocking().first()).flatMap { $0 } ?? 0
Daniel T.
  • 32,821
  • 6
  • 50
  • 72
2

There are two other alternatives avoiding the double optional by handling the error

The first hands over the error to the caller

func checkSubjectState() throws -> Int
{
  return try subject.verifier.toBlocking().first() ?? 0
}

The second adds a do - catch block

func checkSubjectState() -> Int
{
    do { return try subject.verifier.toBlocking().first() ?? 0 } 
    catch { return 0 }
}
vadian
  • 274,689
  • 30
  • 353
  • 361
1

I came up with another solution that I think you might like more:

infix operator ???: NilCoalescingPrecedence
func ???<T>(opt: T??, val: @autoclosure () -> T) -> T {
    return ((opt ?? val()) ?? val())
}

The above will allow you to simply:

return (try? subject.verifier.toBlocking().first()) ??? 0

I tried to give the operator a higher precedence than the try? so the parens wouldn't be needed, but I couldn't find a way to do that.

Daniel T.
  • 32,821
  • 6
  • 50
  • 72