Sample code for 30+ languages & platforms
Objective-C

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 Objective-C Downloads

Objective-C
#import <CkoFileAccess.h>
#import <NSString.h>
#import <CkoRest.h>
#import <CkoAuthGoogle.h>
#import <CkoUrl.h>
#import <CkoJsonObject.h>
#import <CkoStream.h>
#import <CkoServerSentEvent.h>
#import <CkoTask.h>

BOOL success = NO;

// 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.
CkoFileAccess *fac = [[CkoFileAccess alloc] init];
NSString *accessToken = [fac ReadEntireTextFile: @"qa_data/tokens/firebaseToken.txt" charset: @"utf-8"];
if (fac.LastMethodSuccess == NO) {
    NSLog(@"%@",fac.LastErrorText);
    return;
}

CkoRest *rest = [[CkoRest alloc] init];

// 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: @"chilkat.firebaseio.com" port: [NSNumber numberWithInt: 443] tls: YES autoReconnect: YES];
if (success == NO) {
    NSLog(@"%@",rest.LastErrorText);
    return;
}

CkoAuthGoogle *authGoogle = [[CkoAuthGoogle alloc] init];
authGoogle.AccessToken = accessToken;
[rest SetAuthGoogle: authGoogle];

[rest AddHeader: @"Accept" value: @"text/event-stream"];
[rest AddHeader: @"Cache-Control" value: @"no-cache"];

NSString *responseBody = [rest FullRequestNoBody: @"GET" uriPath: @"/.json"];

// A 307 redirect response is expected.
if ([rest.ResponseStatusCode intValue] != 307) {
    NSLog(@"%@%d",@"Unexpected response code: ",[rest.ResponseStatusCode intValue]);
    NSLog(@"%@",responseBody);
    NSLog(@"%@",@"Failed.");
    return;
}

// Get the redirect URL
NSString *urlStr = rest.LastRedirectUrl;
CkoUrl *url = [[CkoUrl alloc] init];
[url ParseUrl: urlStr];

NSLog(@"%@%@",@"redirect URL domain: ",url.Host);
NSLog(@"%@%@",@"redirect URL path: ",url.Path);
NSLog(@"%@%@",@"redirect URL query params: ",url.Query);
NSLog(@"%@%@",@"redirect URL path with query params: ",url.PathWithQueryParams);

// Our text/event-stream will be obtained from the redirect URL...
CkoRest *rest2 = [[CkoRest alloc] init];

success = [rest2 Connect: url.Host port: [NSNumber numberWithInt: 443] tls: YES autoReconnect: YES];
if (success != YES) {
    NSLog(@"%@",rest2.LastErrorText);
    return;
}

[rest2 AddHeader: @"Accept" value: @"text/event-stream"];
[rest2 AddHeader: @"Cache-Control" value: @"no-cache"];

// Add the redirect query params to the request
[rest2 AddQueryParams: url.Query];

// In our case, we don't actually need the auth query param,
// so remove it.
[rest2 RemoveQueryParam: @"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: @"GET" uriPath: url.Path];
if (success != YES) {
    NSLog(@"%@",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.
int responseStatusCode = [[rest2 ReadResponseHeader] intValue];
if (responseStatusCode < 0) {
    NSLog(@"%@",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) {
    NSLog(@"%@%d",@"Response Code: ",responseStatusCode);
    NSLog(@"%@%@",@"Response Status Text: ",rest2.ResponseStatusText);
    NSLog(@"%@%@",@"Response Header: ",rest2.ResponseHeader);
    responseBody = [rest2 ReadRespBodyString];
    if (rest2.LastMethodSuccess == YES) {
        NSLog(@"%@%@",@"Error Response Body: ",responseBody);
    }

    NSLog(@"%@",@"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.
CkoJsonObject *jsonDb = [[CkoJsonObject alloc] init];

// 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..
CkoStream *eventStream = [[CkoStream alloc] init];

// This sse object will be used as a helper to parse the server-sent event stream.
CkoServerSentEvent *sse = [[CkoServerSentEvent alloc] init];

CkoTask *task = [rest2 ReadRespBodyStreamAsync: eventStream autoSetStreamCharset: YES];
[task Run];

// For this example, we'll just read a few events, and then cancel the
// async task.
int count = 0;
while ((count < 3) && (task.Finished == NO)) {

    // 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.
    NSString *eventStr = [eventStream ReadUntilMatch: @"\r\n\r\n"];
    if (eventStream.LastMethodSuccess != YES) {
        NSLog(@"%@",eventStream.LastErrorText);
        // Force the loop to exit by setting the count to a high number.
        count = 99999;
    }
    else {
        NSLog(@"%@%@%@",@"Event: [",eventStr,@"]");

        // We have an event. Let's update our local copy of the JSON database.
        success = [sse LoadEvent: eventStr];
        if (success != YES) {
            NSLog(@"%@%@",@"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: sse.EventName data: sse.Data];
            if (success != YES) {
                NSLog(@"%@%@%@%@",@"Failed to apply event: ",sse.EventName,@": ",sse.Data);
            }
            else {
                NSLog(@"%@%@%@%@",@"Successfully applied event: ",sse.EventName,@": ",sse.Data);
            }

        }

    }

    count = count + 1;
}

// Make sure the background task is cancelled if still running.
[task Cancel];

// Examine the JSON database after applying events..
jsonDb.EmitCompact = NO;
NSLog(@"%@",@"----");
NSLog(@"%@",[jsonDb Emit]);