REST API

iOS Client

Updated: October 16, 2020

The goal of the iOS SDK is to integrate correctly with REST tools of the Nuxeo Platform, and provide a hierarchical synchronization service, so as to be able to access content offline.

Adding Nuxeo SDK to Your Project

To include Nuxeo SDK as a library to your project, we recommend you use CocoaPods , a dependencies manager for Objective C projects written in Ruby and available as a RubyGems.

A Nuxeo dedicated specs repository is available in our GitHub account: nuxeo/cocoapods-specs.

Installing CocoaPods and Setting up Nuxeo's Pods Repository

  1. Install Cocoapods using RubyGems.

     $ gem install cocoapods
    
  2. Set up our pods specs repository.

     $ pod repo add nuxeo https://github.com/nuxeo/cocoapods-specs
    

Adding Pods to a XCode Project

  1. Create a text file named PodFile in your project root folder. Its goal is to declare your dependencies. It should looks like this:

    $ cat Podfile
     platform :ios, '7.0'
    pod 'NuxeoSDK', :head
    $ pod
    

    In this sample, we reference our project head. That means you will use our snapshot version. You might need to specify a version number.

  2. Execute the pod command to download or update each dependencies defined in your PodFile. CocoaPods will create a dedicated XCode workspace containing everything you need.

Accessing REST / Automation API

To access a remote server, at a first level we expose some basic objects.

  • A session object, which handles connectivity, can execute a request upon a server and makes it possible to serialize JSON results as entities.
  • A request object, which enables you to parametrize your needs.

Those two objects expose HTTP concepts: you can add parameters, headers, authentication. They can be enhanced with Nuxeo concepts like schema, adaptor, category, etc.

For instance, from scratch a hand made request to fetch the default-domain document with dublincore metadata gets this result as a document entity:

NSURL *url = [[NSURL alloc] initWithString:@"http://localhost:8080/nuxeo"];
 NUXSession *session = [[NUXSession alloc] initWithServerURL:url username:@"Administrator" password:@"Administrator"];
 [session addDefaultSchemas:@[@"dublincore"]];
NUXRequest *request = [[NUXRequest alloc] initWithSession:session];
 [[request addURLSegment:@"path"] addURLSegment:@"default-domain"];
 [request setCompletionBlock:^(NUXRequest *request)
{ NSError *error; NUXDocument *doc = [request responseEntityWithError:&error]; [self doSomethingGreatWith:doc]; }];
 [request start];

There are several ways to execute a request. You can start it asynchrounously, synchronously (beware not to block UI thread), from the session itself, or from convenience method from the request.

NUXSession Class

#import <NuxeoSDK/NUXSession.h>

The NUXSession object's goal is to handle remote server connectivity, authentication and default behaviors.

Do not hesitate to take a look at the NUXSession tests hosted in GitHub.

Shared Session

In case your application uses a single dedicated account to connect to Nuxeo, you are able to configure it using a resource file strictly named NUXSession-info.plist. Then, you'll access the singleton using the NUXSession shared message.

 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
 <key>Username</key>
 <string>sharedUsername</string>
 <key>Password</key>
 <string>sharedPassword</string>
 <key>Repository</key>
 <string>default</string>
 <key>ApiPrefix</key>
 <string>api/v1</string>
 <key>URL</key>
 <string>http://localhost:8080/nuxeo</string>
 </dict>
 </plist>

User's Level Session

Otherwise, if you want to use user's level sessions, you should instantiate it like other objects with the init message.

[[NUXSession alloc] initWithServerURL:url username:@"Administrator" password:@"Administrator"];

Change Requests Default Behaviors

You can also change some default behavior. For instance, you can add some request's default schema or pluggable context. And each request executed with this session will have those settings.

[session addDefaultSchema: @[@"dublincore", @"file"]];
[session addDefaultCategory: @[@"video"]];

NUXRequest Class

#import <NuxeoSDK/NUXRequest.h>

The NUXRequest object exposes Nuxeo concepts like schema, adaptors, categories at a document level on top of HTTP stuff like methods, headers, data. NUXRequest is built using a NUXSession.

NUXRequest *request = [[NUXRequest alloc] initWithSession:aSession];
// Nuxeo related stuff
 [request addURLSegment: @""];
 [request addAdaptor: @"children"];
 [request addAdaptor: @"blob" withValue: @"file:content"];
 [request addCategory: @"video"];
 [request addCategories: @[@"video", ...]];
 [request addSchema: @"dublincore"];
 [request addSchemas: @[@"file", ...]];
// HTTP stuff
 [request addHeaderValue:@"value" forKey:@"key"];
 [request setMethod: @"GET"];
 [request setContentType: @"application/json"];
 [request setPostData: nil];

NXRequest can be executed synchronously or asynchronously.

[request setCompletionBlock:^(NUXRequest *request)
{ // Do some stuff }];
 [request setFailureBlock:^(NUXRequest *request) { // Do some stuff executed on failure }];

 [request start]; //Start request asynchrounously
 [request startSynchronous]; //Start request synchronously

 // Convenience method to do everything in one line
 [request startWithCompletionBlock:^(NUXRequest *request) { // Do some stuff }FailureBlock:^(NUXRequest *request)
{ // Do some stuff executed on failure }];

Do not hesitate to take a look at the NUXRequest tests hosted in GitHub.

Response Handling

There are several ways to get response data:

  • NSData: passing the HTTP result as it comes, as an object-oriented wrapper for byte buffer.
  • NSString: building a string with the NSData object, expected response string is UTF8 encoded.
  • JSON as NSDictionnary: serializing response in a JSON object.
  • NUXEntity: using our custom serialization object, to fill a custom object depending of the entity-type JSON field.

Code sample:

[request startSynchronous];
// Reponse
 request.responseStatusCode;
 request.responseMessage;
NSData *data = [request responseData];
 NSString *string = [request responseString];
NSError *error;
 NSDictionnary *json = [request responseJSONWithError:&error];
 NUXEntity *entity = [request responseEntityWithError:&error];

Downloading Blobs

When you know your response will be a file, instead of mounting the complete file in memory in the data field of your request object, you must set the downloadDestinationPath property to stream the response data into it.

NSString *tempFile = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"tempfile%d.tmp", rand()]];
 request = [session requestDownloadBlobFrom:uid inMetadata:@"file:content"];
 request.downloadDestinationPath = tempFile;

To make sure downloading a file is not blocking other requests, and to allow you to cancel downloads and let other requests finish their work, we use two different queues.

Object Mapping

To manipulate a JSON response as an object instead of a NSDictionnary class, we build an automatic introspection mapping based on the entity-type value: each existing property is filled in the object mapping.

Each entity class must be registered as such and extend at least NUXEntity. We provide entity classes for base entity-type (document and documents), but you could register any entity for your custom business objects.

To register a new entity class:

[[NUXJSONMapper sharedMapper] registerEntityClass:[NUXDocument class]];

To convert NSData -> NUXEntity, or NUXEntity -> NSData:

NSError *error;
NUXDocument *entity = [NUXJSONSerializer entityWithData:someData error:&error];
NSData *data = [NUXJSONSerializer dataWithEntity:entity error:&error];

To allow your class to be persisted, don't forget to implement NUXEntityPersistable protocol.

Do not hesitate to take a look at the NUXJSONSerializer tests hosted in GitHub.

Convenience Category to Generate Common Requests

import <NuxeoSDK/NUXSession+requests.h>

To ease the request generation on some common requests like fetchDocument, get document children, query, etc., we added a category which adds these methods to a NUXSession object.

Here is a sample of convenience method, but do not hesitate to look at the NUXSession+requests.h file itself.

// Prefilled request messages
 NUXRequest *request = [session requestDocument: @"uid|path"];
request = [session requestChildren: @"uid|path"];
 request = [session requestQuery: @"SELECT * FROM Folder"];
// You can pass JSON dictionnary or NUXEntity
 [session requestUpdateDocument: myDoc];
 [session requestDeleteDocument: myDoc];
 [session requestCreateDocument: myDoc withParent: @"/default-domain"];

Do not hesitate to take a look at the NUXSession tests hosted in GitHub.

Automation Requests

We define a specific request for calling Automation operations. It exposes more Automation concepts like operation input (with a document or a file), operation context and operation parameters.

NUXAutomationRequest *request = [session requestOperation:@"FileManager.Import"];
 [request addContextValue:@"/default-domain/workspaces" forKey:@"currentDocument"];
 [request setInputFile:aFilePath];
// or more simply
 request = [session requestImportFile:file withParent:@"/default-domain/workspaces"];

Cache

Hierarchical Cache

Hierarchical cache is initialized with one request to define the hierarchy nodes. Then for each node you can define a block to fill its content. Nodes and content are stored in a SQLite database and available offline. Content could be refreshed automatically when online (default behavior).

Each hierarchy is identified with a unique name.

Here is a sample to initialize a hierarchy.

Don't forget to initiate it with blocks at your application startup, even if you don't need to load it at a time and even if loadWithRequest takes into account if the hierarchy is loaded or not.

NUXHierarchy *hierarchy = [NUXHierarchy hierarchyWithName:@"mainHierarchy"];
 NUXRequest *request = [session requestQuery:@"select * from Document where ecm:mixinType = 'Folderish'"];
 [hierarchy loadWithRequest:request]; //loading is asynchronous
[hierarchy waitUntilLoadingIsDone]; //exists, if you really need it.

In this sample, no content block is defined, so you'll have to manage your node content outside of the hierarchy mecanism. After this async initialization, you'll be able to browse nodes, for instance.

hierarchy.childrenOfRoot; // Return all root nodes
 [hierarchy childrenOfDocument:@"/default-domain"]; // Assuming your request has loaded /default-domain
If you want to use the provided node's content management, you just have to define a content block like this:
NUXHierarchy *hierarchy = [NUXHierarchy hierarchyWithName:@"mainHierarchy"];
 hierarchy.nodeBlock = ^NSArray *(NUXEntity *entity, NSUInteger depth)
 {
 NUXDocument *doc = (NUXDocument *)entity;
 if ([self shouldLoadDocumentsForNode:doc withDepth:depth] == YES)
{
 NUXSession *nuxSession = [NUXSession sharedSession]; // retrieve all
documents in this node in synchronize mode NSString *subRequestFormat =
@"SELECT * FROM Document where ecm:parentId = '%@'and ecm:currentLifeCycleState <> 'deleted'"; NSString *subRequestQuery = [NSString stringWithFormat:subRequestFormat, doc.uid]; NUXRequest *nuxSubRequest = [nuxSession requestQuery:subRequestQuery]; [nuxSubRequest startSynchronous]; NUXDocuments *documents = [nuxSubRequest responseEntityWithError:nil]; return documents.entries; }return nil;
 };
NUXRequest *request = [session requestQuery:@"select * from Document where ecm:mixinType = 'Folderish'"];
 [hierarchy loadWithRequest:request]; //loading is asynchronous

The goal of a nodeBlock is to return an array of documents that you want to get as the content of a node. Note that nodeBlock is executed in a separate thread, so if you want to get documents from a request, you have to start it synchronously.

While you are online, the same block is called each time you get content.

Blob LRU cache

We provide a LRU cache to easily store your blob with an API-oriented NUXEntity or digest. You can change the maximum size and maximum items in cache. Size is defined in bytes.

[NUXBlobStore instance].sizeLimit = @(2*1024*1024*1024); // Set cache limit to 2Go
[NUXBlobStore instance].countLimit = @(10); // Set item limit to 10.

NUXBlobStore removes the least recently used items first. Default values are unlimited size and 100 items. It uses blob digest as a key store. You can test if a blob is present, save a new blob, delete a specific blob from cache or reset the whole cache.

// Saving a blob
 NSString *storePath = [[NUXBlobStore instance] saveBlobFromPath:filePath withDocument:doc metadataXPath:@"file:content"];
// Accessing a blob with his digest
 NSString *digest = [[doc.properties valueForKey:@"file:content"] valueForKey:@"digest"];
 NSString *blobPath = [[NUXBlobStore instance] blob:digest];
 // Removing blob
 [[NUXBlobStore instance] removeBlob:digest];

Do not hesitate to take a look at the NUXBlobStore tests hosted in GitHub.