Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Please add a small example how to use this. #75

Open
houmie opened this issue Apr 2, 2024 · 4 comments
Open

Please add a small example how to use this. #75

houmie opened this issue Apr 2, 2024 · 4 comments
Labels
waiting for feedback Indicates LaunchDarkly is waiting for customer feedback before issue is closed due to staleness.

Comments

@houmie
Copy link

houmie commented Apr 2, 2024

Hello,

Thank you first for this amazing project. I'm desperately looking for a SSE library in Swift.
I have difficulties understanding how to utilise this library in Swift. There is an API doc, but still very difficult to understand where to get started. A simple example would be amazing.

     guard let urlFull = URL(string: Constants.apiServiceUrlFull) else {
         print("Invalid Full URL")
         return
    }
        var request = URLRequest(url: urlFull)
        request.httpMethod = "POST"
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
   
        let requestBody: [String: Any] = [
            "max_new_tokens": 256,
            "max_tokens": 256,
            "stream": true,
        ]

        do {
            let jsonData = try JSONSerialization.data(withJSONObject: requestBody, options: [])
            request.httpBody = jsonData
        } catch {
            print("Error serializing JSON: \(error)")
            return
        }

For example if I had this POST that requires Streaming as response, how would I use your library please?

Thanks

@keelerm84
Copy link
Member

👋🏼 Hi @houmie

I'm sorry you're having trouble getting started. I think the below sample should be close to what you need based on your code sample. I hope this helps!

import Foundation
import LDSwiftEventSource

class MyEventHandler: EventHandler {
    func onOpened() {
        print("** onOpened")
    }

    func onClosed() {
        print("** onClosed")
    }

    func onMessage(eventType: String, messageEvent: LDSwiftEventSource.MessageEvent) {
        print("** onMessage received event type \(eventType) and event \(String(describing: messageEvent))")
    }

    func onComment(comment: String) {
        print("** onComment with comment \(comment)")
    }

    func onError(error: any Error) {
        print("** onError with error \(String(describing: error))")
    }
}

let body: [String: Any] = [
    "max_new_tokens": 256,
    "max_tokens": 256,
    "stream": true,
]

let json = try? JSONSerialization.data(withJSONObject: body, options: [])

guard let json = json else { exit(1) }
guard let url = URL(string: "your-url") else { exit(1) }

var eventHandler = MyEventHandler()
var config = EventSource.Config(handler: eventHandler, url: url)
config.method = "POST"
config.headers = ["Content-Type": "application/json"]
config.body = json;

var eventSource = EventSource(config: config)
eventSource.start()

var semaphore = DispatchSemaphore(value: 0)
semaphore.wait()

@keelerm84 keelerm84 added the waiting for feedback Indicates LaunchDarkly is waiting for customer feedback before issue is closed due to staleness. label Apr 2, 2024
@houmie
Copy link
Author

houmie commented Apr 2, 2024

Hello @keelerm84

Thank you so much for this example. It was incredibly helpful. Yes it works. I have noticed without the semaphore, there is no guarantee that it would be working.

var semaphore = DispatchSemaphore(value: 0)
semaphore.wait()

I have spent hours attempting to replace semaphores with different asynchronous patterns in SwiftUI, but without success. My struggles may stem from a lack of experience with Server-Sent Events (SSE). I'm considering whether to retain the semaphores and instead invoke eventSource.stop() at a specific moment. However, without knowing when the streaming will conclude, determining the perfect timing poses a challenge.

I hope you can offer some advice on how to address this issue.
Thank you.

@keelerm84
Copy link
Member

I have noticed without the semaphore, there is no guarantee that it would be working.

Yes. This is because the main thread would exit otherwise. The event process work is all done asynchronously so you have to keep the application alive some other way.

I have spent hours attempting to replace semaphores with different asynchronous patterns in SwiftUI, but without success.

I'm not sure I follow. If you have an application that is launching and you can instantiate the event source independent of its lifecycle, you shouldn't have a problem with the events coming in. As long as you retain a reference to the event source instance of course. Otherwise, it is going to shut everything down.

I'm considering whether to retain the semaphores and instead invoke eventSource.stop() at a specific moment. However, without knowing when the streaming will conclude, determining the perfect timing poses a challenge.

Under what conditions do you want to stop the event source? If the SSE server disconnects? You could just call eventSource.stop() from the onClosed handler method. Do it when there is an error? onError should work fine.

@houmie
Copy link
Author

houmie commented Apr 11, 2024

Hello Mathew,

Sorry for the late reply.

I finally managed to replace the semaphore with AsyncThrowingStream pattern in Swift. And it works very well.
Thank you for this project. After a lot of analysis this is the only SSE project for iOS that works flawlessly, although a bit difficult at first to understand it. Many thanks for the hard work.

I only noticed one thing in the console when running it.

State: raw -> connecting
Starting EventSource client
Initial reply received
State: connecting -> open
** onOpened
Connection unexpectedly closed.
** onClosed
State: open -> closed
Waiting 1.570 seconds before reconnecting...
State: closed -> shutdown
  1. When the stream finishes by itself (and FYI I'm not even calling eventSource.stop() from onClosed()) it seems that it throws a Connection unexpectedly closed. warning in the console. It is unusual since the connection is supposed to be on its way to be closed, so why is the library throwing this warning?

  2. And the second message is also a bit confusing. Waiting 1.570 seconds before reconnecting.... Not quite sure what that means. So when the SSE connection finishes, there is a small cooldown period before it can be used again?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
waiting for feedback Indicates LaunchDarkly is waiting for customer feedback before issue is closed due to staleness.
Projects
None yet
Development

No branches or pull requests

2 participants