Hey gang ... SKIE question (or I guess maybe techn...
# touchlab-tools
b
Hey gang ... SKIE question (or I guess maybe technically a swift question) ... I'm having some confusion consuming this Flow in my swift code:
Copy code
data class UserInfo(val name: String, val userId: Int)

class UserRepo {
    val userInfo: Flow<UserInfo?> = someFunThatReturnsAUserInfoFlow()
}
In my swift code, I want to get the "first" userId value. My first attempt went something like this:
Copy code
let userInfo = await userRepo.userInfo.first(where: { _ in true})
let userId = userInfo?.userId
But this results in an error on the last line:
Value of optional type 'SkieSwiftOptionalFlow<UserInfo>.Element' (aka 'Optional<UserInfo>') must be unwrapped to refer to member 'userId' of wrapped base type 'UserInfo'
That seems odd. It says the value must be unwrapped, but I thought that's what the
?.
syntax effectively does? But I guess not. I can get things working by doing:
Copy code
let userId = userInfo??.userId
What is this
??
sorcery? I am familiar with
??
as the nil coalescing operator (
let myInt = optionalInt ?? 0
). But here
??
is doing something different. How is this working, and what is it doing? Is this documented anywhere?
t
In Swift it's possible to get
Optional<Optional<Value>>
, because
Optional
is an enum with either a value or no value. On JVM every object is optional and the
Optional<Value>
in Kotlin is just syntactic compile-time safety thing, therefore
Optional<Optional<Value>>
doesn't mean anything. IIRC Swift tries to automatically flatten most of nested optionals, but when generics are involved it's not that simple. The
.first
operator returns
Optional<Element>
, but since the flow can contain optional elements already, it results in the type being
Optional<Optional<Element>>
. The
??
here isn't a nil coalescing operator, but instead two unwraps, one after another.
b
Thanks @Tadeas Kriz that does make sense. This will be fun to explain to my ios devs 😄
t
You could perhaps declare an extension that'd solve it for you:
Copy code
extension SkieSwiftOptionalFlow where Element == UserInfo? {
  var first: UserInfo? {
    get async {
      if let value = await first(where: { _ in true }) { return value } else { return nil }
    }
  }
}
That way you can then use
await userRepo.userInfo.first
to get it
If you make it
public
, you can put it in
src/iosMain/swift
and have it part of the API without having to declare it in the consuming code