# Guide to Implementing Server-Sent Events (SSE) in JavaScript, Android, and iOS for Client-Side Developers

### Overview
  * `Server-Sent Events`(**SSE**) can provide effective and powerful **Server Push Notification**s when used in conjunction with the `HTTP/2` protocol. Over the years, I have successfully and frequently used SSE in production-level scenarios for various business requirements. Recognizing the lack of examples on the internet that cater to each client's specific situations, I decided to write and introduce them myself. (All the example codes provided below have been validated at the production level.)

### Using curl Command for SSE Connection and Event Reception
  * For testing and debugging purposes, it's possible to create an **EventSource** connection and receive events using the `curl` command as follows. The connection remains active until termination and receives new events.

```bash
curl -N --http2 \
    -H "Accept:text/event-stream" \
    -H "Authorization:Bearer {token}" \
  '{server-url}'
```

### SSE Connection and Event Reception in Browser JavaScript
  * In the browser environment, it's possible to create the **HTML5** standard **EventSource** object to receive **SSE** connection events. However, standard APIs cannot send custom request headers for authentication, etc. Using a third-party library as below makes this possible.

```html
// Using the Yaffle EventSource Polyfill library to enable custom header requests not possible with vanilla EventSource
<script src="https://raw.githubusercontent.com/Yaffle/EventSource/master/src/eventsource.min.js"></script>
<script>
    // Creating the EventSource object
    const eventSource = new EventSourcePolyfill('{server-url}', {
        // Specifying custom request headers
        headers: {
            'Authorization': 'Bearer {token}'
        },
        // Specifying the name of the Last-Event-ID to be passed via QueryString
        lastEventIdQueryParameterName: 'Last-Event-ID',
        // Setting the maximum connection time in ms, longer than the maximum connection time set by the server
        heartbeatTimeout: 600000
    })

    // Writing a listener to handle Message arrival events for a specific Channel
    eventSource.addEventListener("{channel}", function(event) {
        console.log(event.data)
    })
</script>
```

### SSE Connection and Event Reception in Android Kotlin
  * In Android and **JVM** environments, implementation can be done using third-party libraries as follows.
  * First, add the following to the project's root **build.gradle.kts**.

```kotlin
dependencies {
    // Using the OkHttp-based EventSource library by LaunchDarkly, known for SSE-based server push notifications in Silicon Valley
    implementation("com.launchdarkly:okhttp-eventsource:4.1.1")
}
```

  * Write a listener to handle **Message** arrival events for a specific **Channel**.

```kotlin
class SseEventHandler : BackgroundEventHandler {

    override fun onOpen() {
        // Write logic for handling successful SSE connection
    }

    override fun onClosed() {
        // Write logic for handling SSE connection closure
    }

    override fun onMessage(event: String, messageEvent: MessageEvent) {
        // Write logic for handling arrival of SSE events
        
        // event: String = Name of the channel or topic the event belongs to
        // messageEvent.lastEventId: String = ID of the arrived event
        // messageEvent.data: String = Data of the arrived event
    }

    override fun onComment(comment: String) {
    }

    override fun onError(t: Throwable) {
        // Write logic for handling errors before or after SSE connection
    	
        // If the server responds with an error other than 2XX, com.launchdarkly.eventsource.StreamHttpErrorException: Server returned HTTP error 401 exception occurs
        // If the client sets a shorter connection time than the server, error=com.launchdarkly.eventsource.StreamIOException: java.net.SocketTimeoutException: timeout exception occurs
        // If the server terminates the connection due to exceeding the connection time, error=com.launchdarkly.eventsource.StreamClosedByServerException: Stream closed by server exception occurs
    }
}
```

  * Finally, implement the code to create the **EventSource** object as follows.

```kotlin
// Creating the EventSource object
val eventSource: BackgroundEventSource = BackgroundEventSource
    .Builder(
        SseEventHandler(),
        EventSource.Builder(
            ConnectStrategy
                .http(URL("{server-url}"))
                // Specifying custom request headers
                .header(
                    "Authorization",
                    "Bearer {token}"
                )
                .connectTimeout(3, TimeUnit.SECONDS)
                // Setting the maximum connection time, longer than the maximum connection time set by the server
                .readTimeout(600, TimeUnit.SECONDS)
        )
    )
    .threadPriority(Thread.MAX_PRIORITY)
    .build()

// Starting the EventSource connection
eventSource.start()
```

### SSE Connection and Event Reception in iOS Swift
  * In the iOS Swift environment, implementation can be done using third-party libraries as follows.
  * First, add the library dependencies to the project environment as follows. (The same **LaunchDarkly**-produced **EventSource** library as in the Android example is used.)

```swift
// In CocoaPods environment, add the following to Podfile
pod 'LDSwiftEventSource', '~> 3.1'

// In Carthage environment, add the following to Cartfile
github "LaunchDarkly/swift-eventsource" ~> 3.1

// In Swift Package Manager environment, add the following to Package.swift
dependencies: [
    .package(url: "https://github.com/LaunchDarkly/swift-eventsource.git", .upToNextMajor(from: "3.1.1"))
]
```

  * Write a listener to handle **Message** arrival events for a specific Channel.

```swift
class SseEventHandler: EventHandler {

    func onOpened() {
        // Write logic for handling successful SSE connection
    }

    func onClosed() {
        // Write logic for handling SSE connection closure
    }

    func onMessage(eventType: String, messageEvent: MessageEvent) {
        // Write logic for handling arrival of SSE events
        
        // eventType: String = Name of the channel or topic the event belongs to
        // messageEvent.lastEventId: String = ID of the arrived event
        // messageEvent.data: String = Data of the arrived event
    }

    func onComment(comment: String) {
    }

    func onError(error: Error) {
        // Write logic for handling errors before or after SSE connection
        
        // error.responseCode: Int = Error response code
    }
}
```

  * Finally, implement the code to create the EventSource object as follows.

```swift
// Creating the EventSource object
var config = EventSource.Config(handler: SseEventHandler(), url: URL(string: "{server-url}")!)
// Specifying custom request headers
config.headers = ["Authorization": "Bearer {token}"]
// Setting the maximum connection time, longer than the maximum connection time set by the server
config.idelTimeout = 600.0
let eventSource = EventSource(config: config)

// Starting the EventSource connection
eventSource.start()
```

### Reference Links
  * [GitHub - EventSource Polyfill](https://github.com/Yaffle/EventSource)
  * [GitHub - okhttp-eventsource](https://github.com/launchdarkly/okhttp-eventsource)
  * [GitHub - swift-eventsource](https://github.com/launchdarkly/swift-eventsource)
