Sample code for 30+ languages & platforms
Swift

Firebase Receive Server-Sent Events (text/event-stream)

See more Firebase Examples

Demonstrates how to start receiving server-sent events and update your JSON database with each event.

Chilkat Swift Downloads

Swift

func chilkatTest() {
    var success: Bool = false

    // Demonstrates how to begin receiving server-sent events, and to update
    // your JSON database for each event.

    // This example requires the Chilkat API to have been previously unlocked.
    // See Global Unlock Sample for sample code.

    // This example assumes a JWT authentication token, if required, has been previously obtained.
    // See Get Firebase Access Token from JSON Service Account Private Key for sample code.

    // Load the previously obtained Firebase access token into a string.
    let fac = CkoFileAccess()!
    var accessToken: String? = fac.readEntireTextFile(path: "qa_data/tokens/firebaseToken.txt", charset: "utf-8")
    if fac.lastMethodSuccess == false {
        print("\(fac.lastErrorText!)")
        return
    }

    let rest = CkoRest()!

    // Make the initial connection (without sending a request yet).
    // Once connected, any number of requests may be sent.  It is not necessary to explicitly
    // call Connect before each request.  
    success = rest.connect(hostname: "chilkat.firebaseio.com", port: 443, tls: true, autoReconnect: true)
    if success == false {
        print("\(rest.lastErrorText!)")
        return
    }

    let authGoogle = CkoAuthGoogle()!
    authGoogle.accessToken = accessToken
    rest.setAuthGoogle(authProvider: authGoogle)

    rest.addHeader(name: "Accept", value: "text/event-stream")
    rest.addHeader(name: "Cache-Control", value: "no-cache")

    var responseBody: String? = rest.fullRequestNoBody(httpVerb: "GET", uriPath: "/.json")

    // A 307 redirect response is expected.
    if rest.responseStatusCode.intValue != 307 {
        print("Unexpected response code: \(rest.responseStatusCode.intValue)")
        print("\(responseBody!)")
        print("Failed.")
        return
    }

    // Get the redirect URL
    var urlStr: String? = rest.lastRedirectUrl
    let url = CkoUrl()!
    url.parse(url: urlStr)

    print("redirect URL domain: \(url.host!)")
    print("redirect URL path: \(url.path!)")
    print("redirect URL query params: \(url.query!)")
    print("redirect URL path with query params: \(url.pathWithQueryParams!)")

    // Our text/event-stream will be obtained from the redirect URL...
    let rest2 = CkoRest()!

    success = rest2.connect(hostname: url.host, port: 443, tls: true, autoReconnect: true)
    if success != true {
        print("\(rest2.lastErrorText!)")
        return
    }

    rest2.addHeader(name: "Accept", value: "text/event-stream")
    rest2.addHeader(name: "Cache-Control", value: "no-cache")

    // Add the redirect query params to the request
    rest2.addQueryParams(queryString: url.query)

    // In our case, we don't actually need the auth query param,
    // so remove it.
    rest2.removeQueryParam(name: "auth")

    // Send the request.  (We are only sending the request here.
    // We are not yet getting the response because the response
    // will be a text/event-stream.)
    success = rest2.sendReqNoBody(httpVerb: "GET", uriPath: url.path)
    if success != true {
        print("\(rest2.lastErrorText!)")
        return
    }

    // Read the response header.  
    // We want to first get the response header to see if it's a successful
    // response status code.  If not, then the response will not be a text/event-stream
    // and we should read the response body normally.
    var responseStatusCode: Int = rest2.readResponseHeader().intValue
    if responseStatusCode < 0 {
        print("\(rest2.lastErrorText!)")
        return
    }

    // If successful, a 200 response code is expected.
    // If the reponse code is not 200, then read the response body and fail..
    if responseStatusCode != 200 {
        print("Response Code: \(responseStatusCode)")
        print("Response Status Text: \(rest2.responseStatusText!)")
        print("Response Header: \(rest2.responseHeader!)")
        responseBody = rest2.readRespBodyString()
        if rest2.lastMethodSuccess == true {
            print("Error Response Body: \(responseBody!)")
        }

        print("Failed.")
        return
    }

    // For this example, our JSON database will be empty at the beginning.
    // The incoming events (put and patch) will be applied to this database.
    let jsonDb = CkoJsonObject()!

    // Make sure to set the JSON path delimiter to "/".  The default is "." and this
    // is not compatible with Firebase paths.
    jsonDb.delimiterChar = "/"

    // At this point, we've received the response header.  Now it's time to begin
    // receiving the event stream.  We'll start a background thread to read the 
    // stream.  (Our main application (foreground) thread can cancel it at any time.)  
    // While receiving in the background thread, our foreground thread can read the stream
    // as it desires..
    let eventStream = CkoStream()!

    // This sse object will be used as a helper to parse the server-sent event stream.
    let sse = CkoServerSentEvent()!

    var task: CkoTask? = rest2.readRespBodyStreamAsync(stream: eventStream, autoSetStreamCharset: true)
    task!.run()

    // For this example, we'll just read a few events, and then cancel the
    // async task.
    var count: Int = 0
    while (count < 3) && (task!.finished == false) {

        // Get the next event, which is a series of text lines ending with
        // a blank line. 
        // Note: This method blocks the calling thread until a message arrives.
        // a program might instead periodically check the availability of
        // data via the stream's DataAvailable property, and then do the read.

        // An alternative to writing a while loop to read the event stream
        // would be to setup some sort of timer event in your program (using whatever timer functionality
        // is provided in a programming language/environment), to periodically check the eventStream's
        // DataAvailable property and consume the incoming event.
        var eventStr: String? = eventStream.read(untilMatch: "\r\n\r\n")
        if eventStream.lastMethodSuccess != true {
            print("\(eventStream.lastErrorText!)")
            // Force the loop to exit by setting the count to a high number.
            count = 99999
        }
        else {
            print("Event: [\(eventStr!)]")

            // We have an event. Let's update our local copy of the JSON database.
            success = sse.loadEvent(eventText: eventStr)
            if success != true {
                print("Failed to load sse event: \(eventStr!)")
            }
            else {
                // Now we can easily access the event name and data, and apply it to our JSON database:
                success = jsonDb.firebaseApplyEvent(name: sse.eventName, data: sse.data)
                if success != true {
                    print("Failed to apply event: \(sse.eventName!): \(sse.data!)")
                }
                else {
                    print("Successfully applied event: \(sse.eventName!): \(sse.data!)")
                }

            }

        }

        count = count + 1
    }

    // Make sure the background task is cancelled if still running.
    task!.cancel()

    task = nil

    // Examine the JSON database after applying events..
    jsonDb.emitCompact = false
    print("----")
    print("\(jsonDb.emit()!)")

}