Developer Tutorial - iOS

Getting Started

Add RSOfflinePlayer-combined.a, the RSOfflinePlayer header files and the RSOfflinePlayerResources.bundle file to your Xcode project and make sure they are linked and exported properly in your project target.

Authentication

Authentication for the SDK is handled by the host application and not the Rustici Engine. This is the most flexible solution for adding the SDK to existing applications as well as new projects. The host application will handle the user login and will provide any necessary authorization details to the SDK when required.

Offline Catalog

The Rustici Engine provides a simple offline catalog that will by default include any package that has been marked as available for offline use. Any courses that you wish to be used within the application will first need to be prepared using the Offline Exporter service. See the Preparing Content for Offline Playback document for more information on this process.

Using this offline catalog feature, however, will require some custom integration layer overrides. If you are already using an older custom integration, this may be the path you choose to take. If you are a newer integration only using the Rustici Engine's REST API, then you should ignore the Rustici Engine's catalog feature and create your own JSON for your mobile app to use for the offline catalog. The important part is to provide a way to download the exported packages and to have a proper ExternalRegistrationId that maps back to an existing registration on the Rustici Engine side.

In the method mentioned below for retrieving this default catalog, you will be required to pass along an authString value. This authString can be anything the host application needs it to be, from an encoded credentials string to a special token. This authString is one of the parameters available in the your integration layer’s GetOfflineCatalog or OfflineGetExternalRegistrationId overrides mentioned above. You will need to adjust the OfflineCatalog data based upon this authString. This will allow you to only display courses to a user which they should be allowed to access.

RSOfflinePlayer SDK provides a getOfflineCatalogWithConfiguration method that takes the authString and a few optional sorting and filtering options and returns a basic JSON object containing the data the host application needs to build the user’s catalog.

[_catalog getOfflineCatalogWithConfiguration:kRSConfiguration withAuthString:_userName withLimit:[NSNumber numberWithInt:10] withOffest:[NSNumber numberWithInt:0] withSearch:@"" withCompletionBlock:^(NSString *catalogString){

   NSMutableArray *catalogArray = [[NSMutableArray alloc] init];

   NSError* error;

   NSDictionary *dictFromJson = [NSJSONSerialization JSONObjectWithData:[catalogString dataUsingEncoding:NSStringEncodingConversionAllowLossy] options:kNilOptions error:&error];
   NSArray *catalogFromServer = [dictFromJson objectForKey:@"offlineCatalog"];

   //get the local packages and add the data to the catalogDict
   NSArray *localPackages = [_catalog getPackageList];

   [catalogFromServer enumerateObjectsUsingBlock:^(id catObj, NSUInteger catIdx, BOOL *catStop){
       __block BOOL _userHasPackage = NO;
       [localPackages enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
           OfflinePackage *currentPkg = (OfflinePackage *)obj;
           if([currentPkg.packageId isEqualToString:[catObj valueForKey:@"packageId"]])
           {
               //user has this package
               NSMutableDictionary *catalogItem = [(NSDictionary *)catObj mutableCopy];
               [catalogItem setValue:currentPkg.packageSize forKey:@"packageSize"];
               [catalogItem setValue:NO forKey:@"isDownloading"];
               // get the current status for each local package to show the user
               [catalogItem setValue:[RSOfflinePlayer getCurrentStatusForUser:_userName withRegistrationId:[catObj valueForKey:@"registrationId"] withConfiguration:[catObj valueForKey:@"configuration"]] forKey:@"attemptDetail"];
               // add it to the catalogArray
               [catalogArray addObject:[catalogItem copy]];
               _userHasPackage = YES;
               stop = YES;
           }
       }];
       //add the ones the user does not already have
       if (!_userHasPackage) {
           NSMutableDictionary *catalogItem = [(NSDictionary *)catObj mutableCopy];
           [catalogItem setValue:NO forKey:@"isDownloading"];
           [catalogArray addObject:[catalogItem copy]];
       }
   }];

   _offlineCatalog = catalogArray;
   [self.tableView reloadData];

 } withErrorBlock:^(NSError *error){
   NSLog(@"%@", [error userInfo]);
}];

Playing Content

To play the offline package, a RSOfflinePlayer object needs to be created by supplying a valid Rustici Engine ExternalPackageId, ExternalRegistrationId and ExternalConfiguration to the service along with the remote Rustici Engine URL and paths.

rsop = [[RSOfflinePlayer alloc] initWithPackageId:_packageId withRegistrationId:_registrationId withConfiguration:_configuration withUserId:@"" withRemoteServer:[RSUtil getValueFromConfig:@"remote-server"] withRemotePaths:remotePaths withRemoteCredentials:remoteCredentials  withCompletionBlock:^{

   [rsop setRemoteServerUrl:[NSURL URLWithString:[RSUtil getValueFromConfig:@"remote-server"]]];
   //Create a URL object.
   NSURL *url = [NSURL fileURLWithPath:rsop.packageLaunchUrl];
   //URL Requst Object
   NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];

   _wv.delegate = self;

   [[NSURLCache sharedURLCache] removeAllCachedResponses];
   _wv.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
   //Load the request in the UIWebView.
  [_wv loadRequest:requestObj];
} withErrorBlock:^(NSError *error){
   UIAlertView *errorAlert = [[UIAlertView alloc] initWithTitle:@"Error!"
              message:[NSString stringWithFormat:@"There was an error loading that package. \n %@", [error userInfo]]
              delegate:nil
    cancelButtonTitle:@"OK"
    otherButtonTitles:nil];
   [errorAlert show];
   } isOnline:[_isOnline boolValue]];

Syncing Results

The SDK relies on the host application to create a UIWebView and navigate to the rsop.packageLaunchUrl as the start page. When the host application wants to sync the course data, it grabs the current values from the UIWebView’s content and sends it to the SDK for syncing. The host application supplies the isOnline value to allow various definitions of ‘online’ to be determined by the host application.

- (IBAction)updateData {
    //get the current runtimeXML from the player

    NSString *runtimeXml = [_wv stringByEvaluatingJavaScriptFromString:@"Control.GetXmlForDirtyData();"];
    //get the current Javascript object from the player
    NSString *attemptInfo = [_wv stringByEvaluatingJavaScriptFromString:@"GetCurrentRegistration();"];
    //send both bits of data to the SDK
    [rsop syncCurrentSessionWithAttemptInfo:(NSString*)attemptInfo WithRuntimeXml:runtimeXml withOptions:nil withCompletionBlock:^{
      } withErrorBlock:^(NSError *error){} isOnline:[_isOnline boolValue]];  
    }

Getting Current Status on Device

To get the current SCORM status for a registration, you can use the RSOfflinePlayer’s getCurrentStatus service. If you are offline, the most recent results from the local datastore are returned. If you are online, the data will be pulled from the Rustici Engine server and then checked against the latest local results to ensure the most recent or completed attempt is used.

[RSOfflinePlayer getCurrentStatusForUser:_userName withRegistrationId:[catObj valueForKey:@"registrationId"] withConfiguration:[catObj valueForKey:@"configuration"]] forKey:@"attemptDetail"];

Deleting Local Content

The host application can use the SDK to delete content from the device when the user is finished with it and synced the results back to the Rustici Engine. Using RSOfflinePlayer’s deletePackage service will delete the package folder from the local filesystem as well as all of the session information in the datastore.

Special measures should be taken by the host application to not allow this for multi-user setups where once user could delete the package while another is still using it.

[_catalog deletePackageById:_packageId withCompletionBlock:^{
       NSLog(@"package deleted");
   } withErrorBlock:^(NSString *errorCode){
       NSLog(@"package delete error %@", errorCode);
   }];

results matching ""

    No results matching ""