Revisiting persisting app settings in the cloud

Bookmarks: 

Tuesday, May 03, 2016

Back in 2013, I wrote a blog on persisting application settings in the cloud like Google Chrome for example does. The solution presented at that time was using the DropBox cloud service. The technique was quite simple. Persist settings in an INI file and automatically store and retrieve this INI file from a DropBox account.

Now, 3 years later, I wanted to revisit this technique and present a new solution that is more flexible and fine-grained and can be used from apps on Windows, Mac OSX, iOS, Android and Linux. The new technique will use the TMS structured data cloud storage service myCloudData.net.

As a brief introduction to myCloudData, think of it as configurable tables in the cloud, accessible with a REST API and using OAUTH 2 for authentication & authorization. If you want to learn more about myCloudData.net, check its API doc or another blog article written about it.

Time to get down to the nitty gritties! As an example, a VCL app was created with a left and right aligned panel and inbetween a memo. The user can customize the left panel and right panel width with a splitter control and by right-clicking the panel, the panel color can be changed. Further, there is a checkbox for both panels to toggle the visibility of the panel. This is a typical and basic skeleton of what many user configurable apps could be.



We'll now use the myCloudData.net service to store not only how the user configures this left and right panel, but also the last form's size and position on the screen. Whenever the user restarts the app, it will bring back the app as he originally left it on whatever machine the user starts this app. He will always find it back as he left it.
We'll drop a TAdvmyCloudData component on the form and in the form's constructor, we'll initialize it with the application key & secret obtained to use the myCloudData.net service. Further, this component is initialized to persist its application token & secret and finally, the call to AdvmyCloudData.Connect will test validity of possible existing tokens or will perform a new authentication & authorization when no valid tokens are found.
procedure TmCDDemo.FormCreate(Sender: TObject);
begin
  FConnected := false;
  AdvMyCloudData1.App.Key := MYCLOUDDATAKEY;
  AdvMyCloudData1.App.Secret := MYCLOUDDATASECRET;
  AdvMyCloudData1.App.CallBackPort := 8888;
  AdvMyCloudData1.App.CallBackURL := 'http://127.0.0.1:8888';

  AdvMyCloudData1.PersistTokens.Location := plIniFile;
  AdvMyCloudData1.PersistTokens.Key := '.myCloudData.ini';
  AdvMyCloudData1.PersistTokens.Section := 'tokens';
  AdvMyCloudData1.Connect;
end;
Once this step is performed, the AdvMyCloudData component triggers the OnConnected event from where we call the form's ReadSettings method.

In the ReadSettings method, it is checked whether the table to hold the settings exists for the user account on the service or not. When found, the first record from the table is read to retrieve the settings. When not found, the metadata for the table is created. Of course when the new table is created with its metadata, it's not yet possible to retrieve stored settings. This table will only be used the first time then when the user closes the app.
procedure TmCDDemo.ReadSettings;
var
  table: TMyCloudDataTable;

begin
  table := AdvMyCloudData1.TableByName('SETTINGS');

  if not Assigned(table) then
  begin
    table := AdvMyCloudData1.CreateTable('SETTINGS');
    table.MetaData.Add('PNLVISL', ftBoolean);
    table.MetaData.Add('PNLVISR', ftBoolean);
    table.MetaData.Add('PNLWL', ftInteger);
    table.MetaData.Add('PNLWR', ftInteger);
    table.MetaData.Add('PNLCLRL', ftInteger);
    table.MetaData.Add('PNLCLRR', ftInteger);
    table.MetaData.Add('FORMW', ftInteger);
    table.MetaData.Add('FORMH', ftInteger);
    table.MetaData.Add('FORML', ftInteger);
    table.MetaData.Add('FORMT', ftInteger);
    table.SetMetaData;
  end
  else
  begin
    table.Query;
    if table.Entities.Count > 0 then
    begin
      Self.Left := table.Entities[0].Value['FORML'];
      Self.Top := table.Entities[0].Value['FORMT'];
      Self.Width := table.Entities[0].Value['FORMW'];
      Self.Height := table.Entities[0].Value['FORMH'];
      LeftPanel.Visible := table.Entities[0].Value['PNLVISL'];
      RightPanel.Visible := table.Entities[0].Value['PNLVISR'];
      LeftPanel.Width := table.Entities[0].Value['PNLWL'];
      RightPanel.Width := table.Entities[0].Value['PNLWR'];
      LeftPanel.Color := TColor(table.Entities[0].Value['PNLCLRL']);
      RightPanel.Color := TColor(table.Entities[0].Value['PNLCLRR']);

      LeftCheckBox.Checked := LeftPanel.Visible;
      RightCheckBox.Checked := RightPanel.Visible;
    end;
  end;
end;
It was a choice to create this metadata programmatically. It could as well have been created in advance online via the metadata editor. When checking the myCloudData.net account, after this programmatic creation of metadata, it is shown as:

To store the settings, all we need to do is call WriteSettings from the form's OnClose event. This of course is only done when TAdvmyCloudData was able to succesfully connect to an account on the service:
procedure TmCDDemo.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if FConnected then
    WriteSettings;
end;
Persisting the settings in the WriteSettings procedure becomes:
procedure TmCDDemo.WriteSettings;
var
  table: TMyCloudDataTable;
  entity: TMyCloudDataEntity;
  ins: boolean;
begin
  table := AdvMyCloudData1.TableByName('SETTINGS');

  table.Query;

  ins := table.Entities.Count = 0;

  if not ins then
    entity := table.Entities[0]
  else
    entity := table.Entities.Add;

  entity.Value['FORML'] := Self.Left;
  entity.Value['FORMT'] := Self.Top;
  entity.Value['FORMW'] := Self.Width;
  entity.Value['FORMH'] := Self.Height;
  entity.Value['PNLVISL'] := LeftPanel.Visible;
  entity.Value['PNLVISR'] := RightPanel.Visible;
  entity.Value['PNLWL'] := LeftPanel.Width;
  entity.Value['PNLCLRL'] := integer(LeftPanel.Color);
  entity.Value['PNLWR'] := RightPanel.Width;
  entity.Value['PNLCLRR'] := integer(RightPanel.Color);

  if not ins then
    entity.Update
  else
    entity.Insert;
end;
So, with this small amount of code and a one-time authentication and authorization of the myCloudData account, the user will always have the last settings of the app he used, on any machine from where he is using the application.
I hope this serves as a small example of uses for the myCloudData.net service and actually only scratches the surface of possibilities. The use of myCloudData.net for the functionality demonstrated here is completely free, as in free beer. Only when using large amounts of data or blob fields, a subscription to myCloudData.net is required.
Our TMS Cloud Pack component TAdvmyCloudData makes it completely abstract and dead-simple to use the underlying REST API from the service for VCL applications and this component is also available with exactly the same interface for FireMonkey applications and Lazarus applications.

We are eager to learn about your creative and innovative applications using these components and service and in what further directions this can be developed to satisfy your needs!

The full source code of this sample application can be downloaded here.

Bruno Fierens


Bookmarks: 

This blog post has not received any comments yet. Add a comment.




Previous  |  Next  |  Index