grpc-swift
grpc-swift copied to clipboard
Support NIOTSNetworkEvents.WaitingForConnectivity
Is your feature request related to a problem? Please describe it.
At the moment a client a client attempts to establish a connection it may be waiting for connectivity (e.g. when Airplane mode is enabled). During this time we still allow users to make RPCs; requests will be sent when a connection is established (if one is eventually established).
In most cases waiting for connectivity is the right thing do to, however, it can be useful to know when we're in this state and to disallow the connection from entering this state entirely.
Related Issues:
- #831
- #712
Describe the solution you'd like
Note: this only applies when NIOTransportServices is being used.
- Allow users to configure whether they're willing to wait for connectivity.
- Surface the waiting-for-connectivity events (
NIOTSNetworkEvents.WaitingForConnectivity
was added in https://github.com/apple/swift-nio-transport-services/pull/95) to the user via the connectivity delegate.
Describe alternatives you've considered
None.
Additional context
None.
I am using 1.0.0-alpha.12 and i had the following setup to detect connectivity:
I tried to reduce the code to the minimum.
I am using SwiftUI and Combine on iPadOS btw.
This doesn't work on 1.0.0-alpha.13 - presumably the update when the connection management was rewritten killed it? How is the ConnectivityStateDelegate being used? Does it make sense to open a new issue or is this being addressed?
class MyStuff: ObservableObject {
private(set) var client: MyClient
private let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
private let delegate: Delegate = Delegate()
@Published private(set) public var connectivity: ConnectivityState = .idle
private var cancellables: Set<AnyCancellable> = []
init(_ ip: String, port: Int) {
let channel = ClientConnection
.insecure(group: self.group)
.withConnectionBackoff(maximum: .seconds(1))
.withErrorDelegate(self.delegate)
.withConnectivityStateDelegate(self.delegate)
.connect(host: ip, port: port)
self.client = MyClient(channel: channel)
self.delegate.connectivity.sink(receiveValue: { newState in
DispatchQueue.main.async {
self.connectivity = newState
}
})
.store(in: &self.cancellables)
}
deinit {
self.cancellables.removeAll()
do {
try self.client.channel.close().wait()
try self.group.syncShutdownGracefully()
} catch {
// don't deal with errors here
}
}
class Delegate: ConnectivityStateDelegate, ClientErrorDelegate {
var connectivity = CurrentValueSubject<ConnectivityState, Never>(.idle)
func connectivityStateDidChange(from oldState: ConnectivityState, to newState: ConnectivityState) {
self.connectivity.value = newState
}
func didCatchError(_ error: Error, logger: Logger, file: StaticString, line: Int) {
print(error)
print(file)
print(line)
}
}
}
Thanks in advance,
Greetings Konstantin
Hey @krjw-eyev, when you say "doesn't work", can you please elaborate a little? What does happen?
Hey @Lukasa I have investigated this more and I was able to fix it on my end. The behaviour in 1.0.0.alpha.12 was that the delegate got .ready
state even without actually calling a grpc. This behaviour changed, so I was getting .idle
all the time.
I will upgrade now to 1.0.0.alpha.14 but I think there should be some kind of example or a documentation on how this exactly works because people who are not very familiar with grpc (like me) may misinterpret how the mechanism works. I really appreciate the work you have done here!
My issue obviously had nothing to do with this issue so you could either delete the comments or leave them for documentation.
Thanks
Konstantin