EventSource

repository·main·Indexed 41 days ago

https://github.com/recouse/eventsource

A concurrency-safe Swift package for Server-Sent Events (SSE) that utilizes async/await and AsyncSequence to handle real-time updates from persistent HTTP connections. Supports standard SSE and a data-only mode optimized for streaming APIs like OpenAI.

Tokens
1.2K
Snippets
5
Records
6
Agent score
1%

What's inside EventSource

  1. Use EventSource to receive real-time updates

    main

    To connect to an SSE endpoint, create an instance of EventSource, initiate a dataTask(for:) with a URLRequest, and iterate over the events using an AsyncSequence via dataTask.events().

    Note: To explicitly close the connection, call task.cancel() on the parent Task. Note that if you cancel the task, the .closed event will not be emitted.

    import EventSource
    
    let task = Task {
        let eventSource = EventSource()
        let dataTask = eventSource.dataTask(for: urlRequest)
        
        for await event in dataTask.events() {
            switch event {
            case .open:
                print("Connection was opened.")
            case .error(let error):
                print("Received an error:", error.localizedDescription)
            case .event(let event):
                print("Received an event", event.data ?? "")
            case .closed:
                print("Connection was closed.")
            }
        }
    }
  2. Install EventSource via Swift Package Manager

    main

    Add the package to your Package.swift dependencies and include it in your target's dependencies:

    // In dependencies
    .package(url: "https://github.com/Recouse/EventSource.git"),
    
    // In your target
    .target(name: "<target>", dependencies: [
        .product(name: "EventSource", package: "EventSource"),
    ]),
    .package(url: "https://github.com/Recouse/EventSource.git"),
    
    .target(name: "<target>", dependencies: [
        .product(name: "EventSource", package: "EventSource"),
    ]),
  3. Use EventSource in Data-only mode

    main

    For APIs that primarily stream data (like OpenAI's completions API), initialize EventSource with mode: .dataOnly. This mode is optimized for handling streams where you only care about the data payload within the event.

    Task {
        var urlRequest = URLRequest(url: URL(string: "https://api.openai.com/v1/chat/completions")!)
        urlRequest.allHTTPHeaderFields = [
            "Content-Type": "application/json",
            "Authorization": "Bearer \(accessToken)"
        ]
        urlRequest.httpMethod = "POST"
        urlRequest.httpBody = """
        {
            "model": "gpt-4o-mini",
            "messages": [
                {"role": "user", "content": "Why is the sky blue?"}
            ],
            "stream": true
        }
        """.data(using: .utf8)!
        
        let eventSource = EventSource(mode: .dataOnly)
        let dataTask = eventSource.dataTask(for: urlRequest)
        
        var response: String = ""
        
        for await event in dataTask.events() {
            switch event {
            case .event(let event):
                if let data = event.data?.data(using: .utf8) {
                    let chunk = try? JSONDecoder().decode(ChatCompletionChunk.self, from: data)
                    let string = chunk?.choices.first?.delta.content ?? ""
                    response += string
                }
            default:
                break
            }
        }
        
        print(response)
    }