Bradleycorn
01/30/2025, 9:03 PMdata 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:
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:
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?Tadeas Kriz
01/30/2025, 9:06 PMOptional<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.Bradleycorn
01/30/2025, 10:05 PMTadeas Kriz
01/30/2025, 10:09 PMextension 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 itTadeas Kriz
01/30/2025, 10:09 PMpublic
, you can put it in src/iosMain/swift
and have it part of the API without having to declare it in the consuming code