Console Sample

The Console sample demonstrates using the DABriefcase class to store and then load two DADataTables that have changes that are waiting to be sent to the server.

The Briefcase can be helpful for situations where there isn't a persistent connection to the server and the user needs to be able to quit your app or otherwise work in an offline fashion. Another use of a it might be to aid in the application start-up process, as you can load the data from disk without needing to rely on the server being available.

Getting Started

The Console sample is typically located in /Developer/RemObjects Software/Samples/Data Abstract/Console/Briefcases, though you may have installed the Data Abstract Cocoa SDK and it's samples in another location.

To build it, you will of course need Xcode, and like all the samples provided with Data Abstract for Cocoa you will need to be running the Relativity Server with the DASamples Domain and the Simple Schema available.

Lastly this sample makes use of a convenience method (DALogTable) provided by a class extension to NSArray that we ship with Data Abstract for Cocoa. It is actually a #define method that uses one of three new log methods added to NSArray that simply prints the contents of a DADataTable in a pretty fashion to the console using NSLog. To use this in your own code you must add the following to your import statements #import <DataAbstract/NSArray+DADataTableLog.h>. Please note that this class extension is intended for test purposes only.

Running the Sample

The sample when run sets up a connection to the Relativity Server (Step 1) and then retrieves a limited set of data from two DADataTables using a Dynamic Where clause to do the filtering (Step 2). The first table is the Orders table, where we are only interested in the rows that match particular order ids. The second table is the OrderDetails table, where we are only interested in the rows where the value in the ORDERS column matches the order id.

Having retrieved the tables, we make minor modifications to them so that they are different than the server versions so we can see see that when we reload the data from the briefcase (Step 3). The newly modified tables are then stored to disk in a briefcase folder with some properties set that will be printed when the briefcase is later reloaded (Step 4).

The briefcase is then reloaded from disk and the contents printed to the console (Step 5), before the changes are then applied back to the server and stored to disk once again (Step 6).

Once the sample is finished if you navigate to the /Users/<user>/Library/Application Support/RemObjects/Briefcase folder in Finder, you will see the briefcase file (Briefcase.briefcase) that was written to disk at the end of Step 6. You can right-click on this package and choose Show package contents, to explore the files inside. There will be three files, two with the extension .daBriefcase which are the tables themselves, and a plist file which has the data stored in the briefcase properties in Step 4.

Examining the Code

The code for creating the connection to the server, retrieving and modifying the data are covered by other samples. In this section we shall focus solely on Steps 4 and 5 that created the briefcase, stores the tables to it and then reload the tables from disk.

Creating the path to store the Briefcase

In the first two lines, an NSString, appHomeFolder, is created that points to the location that the briefcase will be stored in. That string is then passed as a location to the createDirectoryAtPath:withIntermediateDirectories:attributes:error: method of the NSFileManager class to create the location if it isn't already there. Finally we create a new string fullBriefcaseName which is the full path to the briefcase we will be using. You can find the BRIEFCASE_NAME define at the top of main.m.

NSArray *appSupportPath = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
NSString *appHomeFolder = [appSupportPath[0] stringByAppendingString:@"/RemObjects/Briefcase"] ;
[[NSFileManager defaultManager] createDirectoryAtPath:appHomeFolder withIntermediateDirectories:YES attributes:nil error:nil];

NSString *fullBriefcaseName = [appHomeFolder stringByAppendingFormat:@"/%@", BRIEFCASE_NAME];

Store the tables in a new briefcase

As noted in the Briefcase Overview, briefcases can be stored in one of two formats; either as a single .daBriefcase which will contain all of the tables and properties that you set or as a package (folder) where each table is stored in its own .daBriefcase file and the properties are stored in a .plist file. By default this sample uses the folder format, but if you want to use the other then simply change the USE_FOLDER_BRIEFCASE hash define from YES to NO.

The briefcaseWithFolder: method of DABriefcase will attempt to load into memory the briefcase folder if it exists, otherwise it will create it in memory till you write it to disk.

The addTables: method adds names of the tables to the list of tables that briefcase contains. If a table with the same name is already there, it will be replaced with this new table.

As the briefcase folder might be around from running the sample previously we remove any properties that are currently set with [briefcase.properties removeAllObjects];. Properties are stored in a dictionary, so we can set and retrieve a property by passing its name to the properties object. Here we set two properties, the first is a version number which could be used to prevent loading a briefcase from an old version of the application, and the second is the data at which the briefcase contents were updated.

Finally to actually write the briefcase to the filesystem we need to use the writeBriefcase: method. When using a folder based briefcase you could save each briefcase as needed by using the writeTable: and writeTables: of DAFolderBriefcase.

To finish this step off the briefcase is nil'ed as that variable will be used again when we load the the briefcase back again.

DABriefcase *briefcase = USE_FOLDER_BRIEFCASE ?
            [DABriefcase briefcaseWithFolder:fullBriefcaseName] :
            [DABriefcase briefcaseWithFile:fullBriefcaseName forReading:NO];

NSLog(@"Briefcase: %@", fullBriefcaseName);

[briefcase addTables:@[ordersTable, detailsTable]];
NSLog(@"Added tables: %@", [briefcase.tableNames componentsJoinedByString:@", "]);
[briefcase.properties removeAllObjects];
briefcase.properties[@"VERSION"] = @"v.0.1";
briefcase.properties[@"DATE"] = [NSDate date];
NSLog(@"Writing briefcase to file system...");
[briefcase writeBriefcase];
briefcase = nil;

Loading the briefcase from disk and retrieving the tables

As before we pass the fullBriefcaseName to the appropriate briefcaseWithXXX method (note that this time the briefcaseWithFile:forReading: passes in a YES value because we are opening a briefcase file we know exists for reading).

The briefcase is loaded back into memory from disk, and we can immediately start retrieving the properties and tables that were stored in it. Properties are retrieved by passing the name of the property to briefcase.properties, and a reference to the DADataTable is retrieved using the tableNamed: method.

briefcase = USE_FOLDER_BRIEFCASE ?
[DABriefcase briefcaseWithFolder:fullBriefcaseName] :
[DABriefcase briefcaseWithFile:fullBriefcaseName forReading:YES];

NSLog(@"Briefcase version is: %@", briefcase.properties[@"VERSION"]);
NSLog(@"Briefcase update date is: %@", briefcase.properties[@"DATE"]);
ordersTable = [briefcase tableNamed:@"Orders"];
detailsTable = [briefcase tableNamed:@"OrderDetails"];
DALogTable(ordersTable);
DALogTable(detailsTable);

Typical Output

This is a typical output of the sample when it is run. As you can see it clearly calls out each step as it is processed. You can see that in Step 2 the dates in the Orders table are 2015-12-02 and that they are modified in Step 3 to be 2016-01-11. In Step 5 you can see that the dates of data newly loaded from the briefcase match those of Step 3 and if you where to run the sample again, then the date in Step 2 would also now be 2016-01-11.

Likewise you can clearly see that the values in the Amounts column are increased by 1 between when they are first retrieved from the server in Step 2 and when they are loaded back from the briefcase in Step 5.

Briefcases sample has been started.
Target URL is http://localhost:7099/bin.
RO SDK layer is configured.
RO DataAbstract layer is configured.
Trying to login with login string User=simple;Password=simple;Domain=DASamples;Schema=Simple...
Login successful

STEP 2. Select some data (certain orders and their details) to store as briefcase...
TABLE: Orders (3 rows from 3)
----------------------------------------------------------------------------
| Id      | Date       | Status        | Type          | Contract| Manager |
----------------------------------------------------------------------------
| 20      | 2015-12-02 | 0             | 2             | 21      | 8       |
| 22      | 2015-12-02 | 0             | 2             | 22      | 8       |
| 24      | 2015-12-02 | 0             | 2             | 22      | 8       |
----------------------------------------------------------------------------
TABLE: OrderDetails (5 rows from 5)
---------------------------------------------------------
| Id      | Order   | Product | Amount  | Price         |
---------------------------------------------------------
| 129     | 20      | 79      | 3       | 357           |
| 132     | 22      | 16      | 4       | 360           |
| 135     | 24      | 37      | 4       | 23            |
| 136     | 24      | 24      | 4       | 347           |
| 137     | 24      | 86      | 3       | 17            |
---------------------------------------------------------

STEP 3. Make some changes but does not apply them ...
TABLE: Orders (3 rows from 3)
----------------------------------------------------------------------------
| Id      | Date       | Status        | Type          | Contract| Manager |
----------------------------------------------------------------------------
| 20      | 2016-01-11 | 0             | 2             | 21      | 8       |
| 22      | 2016-01-11 | 0             | 2             | 22      | 8       |
| 24      | 2016-01-11 | 0             | 2             | 22      | 8       |
----------------------------------------------------------------------------
TABLE: OrderDetails (5 rows from 5)
---------------------------------------------------------
| Id      | Order   | Product | Amount  | Price         |
---------------------------------------------------------
| 129     | 20      | 79      | 4       | 357           |
| 132     | 22      | 16      | 5       | 360           |
| 135     | 24      | 37      | 5       | 23            |
| 136     | 24      | 24      | 5       | 347           |
| 137     | 24      | 86      | 4       | 17            |
---------------------------------------------------------

STEP 4. Prepare briefcase and store tables there ...
Briefcase: /Users/rob/Library/Application Support/RemObjects/Briefcase/Briefcase
Added tables: OrderDetails, Orders
Writing briefcase to file system...

STEP 5. Read briefcase from file system with its pending changes ...
Briefcase version is: v.0.1
Briefcase update date is: 2016-01-11 23:15:11 +0000
TABLE: Orders (3 rows from 3)
----------------------------------------------------------------------------
| Id      | Date       | Status        | Type          | Contract| Manager |
----------------------------------------------------------------------------
| 20      | 2016-01-11 | 0             | 2             | 21      | 8       |
| 22      | 2016-01-11 | 0             | 2             | 22      | 8       |
| 24      | 2016-01-11 | 0             | 2             | 22      | 8       |
----------------------------------------------------------------------------
TABLE: OrderDetails (5 rows from 5)
---------------------------------------------------------
| Id      | Order   | Product | Amount  | Price         |
---------------------------------------------------------
| 129     | 20      | 79      | 4       | 357           |
| 132     | 22      | 16      | 5       | 360           |
| 135     | 24      | 37      | 5       | 23            |
| 136     | 24      | 24      | 5       | 347           |
| 137     | 24      | 86      | 4       | 17            |
---------------------------------------------------------

STEP 6. Apply pending changes to the server and update briefcase....
Done!
Program ended with exit code: 0

NOTE: For clarity the date and process name and ID have been removed from the sample text above