Skip navigation

We've started to document the Jive SDK API level documentation here:

 

 

Jive SDK API Docs

 

This link serves the documentation straight of Git. Its not complete, but its getting there! Please leave feedback here, or in the github issues page (Issues · jivesoftware/jive-sdk · GitHub) so we can continue to tighten up this documentation.

 

Some cool facts about this documentation:

 

 

Enjoy!

In my last post I talked about the new direction for the SDK. While I am still trying to get that done, BUGS have been filed and changes have been made. So today I am going to talk about one of the more involved changes that will affect how the SDK is used.

 

For most Jive instances everything works as before. But there are 2 situations where things need to change.

 

The first situation is url redirection. There are cases where you need to manually update the URL used to connect to the instance. This is most commonly found in internal networks where IT has provided a short name that resolves to the full URL. Both are accessible but only the full URL should be used in @mentions and other links.

As part of the instance validation the server sends the URL that should be used. If this URL is different than the entered URL you need to test to see if it can be reached.

 

Which leads to the second situation. In this case the server wants you to use a different URL but it is not reachable. In this case the URLs returned by the server should be updated to match the URL entered. This scenario is handled by the SDK once you determine that the redirect cannot be done. This most commonly happens with large instances that need a load balancer. The servers can be configured to include an instance identifier. For the average user this identifier needs to be removed, and the SDK handles this.

 

When I created the Example project I skipped the version check because I hard coded the instance URL. To demonstrate what is needed now I have added a view for the user to enter the instance URL. I then validate it and transition to the original login view.

Here is the validation code:

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    // Assume the default instance.
    NSString *instanceString = @"https://community.jivesoftware.com/";
    // Check to see if the user entered a different url.
    if (self.communityURL.text.length > 0) {
        instanceString = self.communityURL.text;
        if (![instanceString hasPrefix:@"http"]) {
            instanceString = [@"http://" stringByAppendingString:instanceString];
            // But what if it should be https:// ?
        }
        // The SDK assumes the URL has a / at the end. So make sure it does.
        if (![instanceString hasSuffix:@"/"]) {
            instanceString = [instanceString stringByAppendingString:@"/"];
        }
    }

Here I do simple formatting of the entered value. Make sure it has the correct prefix and suffix. You should validate both http and https prefixes, preferring the https version if they both work.

    NSURL *instanceURL = [NSURL URLWithString:instanceString];
    __block JVJiveFactory *factory = nil;
    [self.activityIndicator startAnimating];
    [self.communityURL resignFirstResponder];
    self.communityURL.enabled = NO;
    // Is it a valid instance?
    factory = [[JVJiveFactory alloc] initWithInstanceURL:instanceURL
                                                complete:^(JivePlatformVersion *version) {
                                                    [self checkForRedirect:version
                                                                   fromURL:instanceURL
                                                                   factory:factory];
                                                } error:^(NSError *error) {
                                                    [self.activityIndicator stopAnimating];
                                                    self.communityURL.enabled = YES;
                                                    [self.communityURL becomeFirstResponder];
                                                }];  
    return NO;
}

Then I create the jive factory class with blocks to handle the version check results. The simple interface elements are to disable user interaction while performing the check. If the check fails, by returning an error, I reenable interaction. You may want to do more to better inform the user of what happened. Alternatively you could dynamically update the GO button on the keyboard as the user types. If the check succeeds we proceed to the first redirect check.

- (id)initWithInstanceURL:(NSURL *)instanceURL
                 complete:(JivePlatformVersionBlock)completeBlock
                    error:(JiveErrorBlock)errorBlock {
    self = [super init];
    if (self) {
        self.jive = [[Jive alloc] initWithJiveInstance:instanceURL
                                 authorizationDelegate:self];
        [self.jive versionForInstance:instanceURL
                           onComplete:completeBlock
                              onError:errorBlock];
    }

    return self;
}

As part of creating the jive factory class I create a Jive object and start the version check.

 

- (void)checkForRedirect:(JivePlatformVersion *)version
                 fromURL:(NSURL *)targetURL
                 factory:(JVJiveFactory *)initialFactory {
    // Not all instances report their url in the version.
    if (!version.instanceURL || [version.instanceURL isEqual:targetURL]) {
        [self advanceToLogin:initialFactory];
    } else {
        // Attempt to redirect to the server's instance url.
        __block JVJiveFactory *factory = nil;
        factory = [[JVJiveFactory alloc] initWithInstanceURL:version.instanceURL
                                                    complete:^(JivePlatformVersion *redirectVersion) {
                                                        // Direct access granted.
                                                        self.communityURL.text = redirectVersion.instanceURL.absoluteString;
                                                        [self advanceToLogin:factory];
                                                    }
                                                       error:^(NSError *error) {
                                                           // The server lied, bad server. Use the original url.
                                                           [self advanceToLogin:initialFactory];
                                                       }];
    }
}

There are 4 possibilities here: the server reported the same url that the user entered, the server didn't report a url, the server reported a different url that should be used instead of the user entered url, and finally the server requires the SDK to rewrite the urls.

In the first 2 cases we just go to the login screen normally. For the last 2 cases we need to check the server reported URL to distinguish them, by creating a second JVJiveFactory with the server reported URL. If this second check succeeds we use the server reported URL as if the user had entered it instead. Otherwise we continue with the user entered URL.

 

- (void)advanceToLogin:(JVJiveFactory *)factory {
    [self.activityIndicator stopAnimating];
    [JVJiveFactory setInstance:factory];
    [self performSegueWithIdentifier:@"Community" sender:self];
}

Here we update the UI and set the jive factory instance to the validated factory object.

 

The second redirect check happens after the user enters his credentials and tries to login. You need to change the completeBlock passed to the Jive me:onError: method.

In the example this is in the JVJiveFactory loginWithName:password:complete:error: method.

    [self.jive me:^(JivePerson *me) {
        JivePlatformVersion *platformVersion = self.jive.platformVersion;
    
        // url check.
        if (platformVersion.instanceURL) {
            // It's all good.
            if (completeBlock) {
                completeBlock(me);
            }
        } else {
            // NO!!! We have to make sure we have the right URL.
            [self doubleCheckInstanceURLForMe:me onComplete:completeBlock];
        }
    }

The new completeBlock checks to see if the version already includes the instance URL. If not it does another call to the server to get the instance URL.

 

- (void)doubleCheckInstanceURLForMe:(JivePerson *)me
                         onComplete:(JivePersonCompleteBlock)completeBlock {
    [self.jive propertyWithName:JivePropertyNames.instanceURL
                     onComplete:^(JiveProperty *property) {
                         NSString *instanceString = property.valueAsString;
                     
                         // The SDK assumes the URL has a / at the end. So make sure it does.
                         if (![instanceString hasSuffix:@"/"]) {
                             instanceString = [instanceString stringByAppendingString:@"/"];
                         }
                     
                         NSURL *instanceURL = [NSURL URLWithString:instanceString];
                     
                         // Yes! We have a server url.
                         if ([instanceString isEqualToString:self.jive.jiveInstanceURL.absoluteString]) {
                             // Everything matches up.
                             if (completeBlock) {
                                 completeBlock(me);
                             }
                         } else {
                             [self loginWithNewInstanceURL:instanceURL me:me completeBlock:completeBlock];
                         }
                     } onError:^(NSError *error) {
                         // No! We are stuck with what works.
                         if (completeBlock) {
                             completeBlock(me);
                         }
                     }];
}

If there is an error or the server URL matches the entered URL we continue accept the current login. If not then we try logging in again with the new URL.

 

- (void)loginWithNewInstanceURL:(NSURL *)instanceURL me:(JivePerson *)me completeBlock:(JivePersonCompleteBlock)completeBlock
{
    // Make sure we have the correct me object.
    Jive *oldJive = self.jive;

    self.jive = [[Jive alloc] initWithJiveInstance:instanceURL
                             authorizationDelegate:self];
    [self.jive me:completeBlock
          onError:^(NSError *error) {
              // Fall back to what works.
              self.jive = oldJive;
              if (completeBlock) {
                  completeBlock(me);
              }
          }];
}

We start by creating a new Jive instance and try to login with it, returning to the previous login attempt if this one fails.

 

That's a lot of code for something the SDK should just handle. Unfortunately because of the number of NSOperations involved and the different places they need to be, they can't simply be put into the SDK. Feel free to prove me wrong on this.

A couple weeks ago we released a new open-source component on our Github account.

 

PDTSimpleCalendar is a very simple calendar/date picker component for your iOS apps based on UICollectionView.

It has a very light and clean design inspired by Apple Calendar app.

 

default.pnghebrew.png


PDTSimpleCalendar supports all calendar supported by NSCalendar and is very easy to use and customize. Colors can be changed using properties or UIAppearance proxy.

Check it out on our Github account: jivesoftware/PDTSimpleCalendar · GitHub

 

 

At Jive, we use it in the Producteev app to set deadline for tasks:

producteev.png

Hope you'll like it!

OUTDATED

If you need to host a jive sdk service (jivesoftware/jive-sdk · GitHub) somewhere on the Internets, Heroku (https://www.heroku.com) is a pretty good choice, along with Nodejitsu. For the purposes of this blogpost, I'll be talking about Heroku.

 

I'm assuming that you've already:

 

  1. Generated a jive-sdk service
  2. Signed up for Heroku
  3. Uploaded your service to Heroku

 

After having done the above, you'll need to add mongodb to your app:

 

heroku addons:add mongolab

 

MongoDB is required on Heroku, because Heroku uses a virtual file system that makes it impossible to use the sdk's default filesystem persistence. Setting up mongoDB persistence for your service is pretty easy.

 

Your package.json should have this dependency:

"jive-persistence-mongo" : "*"

And your jiveclientconfiguration.json should have this:

"persistence" : "jive-persistence-mongo"

Finally, the app.js generated by the SDK won't play nicely with Heroku's requirement that it controls the HTTP port and mongoDB URL. You can get around this by using the following app.js:

 

var express = require('express'),
    http = require('http'),
    jive = require('jive-sdk');
var app = express();

var failServer = function(reason) {
    console.log('FATAL -', reason );
    process.exit(-1);
};

var startServer = function () {
    if ( !jive.service.role || jive.service.role.isHttp() ) {
        var server = http.createServer(app).listen( app.get('port') || process.env.PORT, function () {
            console.log("Express server listening on port " + server.address().port);
        });
    }
};

jive.util.fsreadJson('jiveclientconfiguration.json').then( function(options) {
    options['port'] = process.env.PORT || options['port'];
    options['databaseUrl'] = process.env.MONGOLAB_URI || process.env.MONGOHQ_URL || options['databaseUrl'];
    jive.service.init(app, options)
        .then( function() { return jive.service.autowire() } )
        .then( function() { return jive.service.start() } ).then( startServer, failServer );
});

 

What this is doing is reading in your jiveclientconfiguration.json file and then overriding the port and databaseUrl parameters from environment process variables set by Heroku. This app.js should work running locally, or in Heroku!

Filter Blog

By date: By tag: