Smart platform services

November 4, 2015 Leave a comment Go to comments

Something cool is taking place in the Smart Mobile Studio RTL, namely the implementation of platform services.

What is a platform service you say? Put simply a platform service is an API that the RTL exposes for you while abstracting you from the low-level implementation. Where the existing browser-driver system is platform spesific, dealing with how the browser exposes it’s features (which is different from browser to browser), platform services are feature spesific: meaning that they present a set of features un-bound by the environment.

This means that even though browsers implement things differently (even with different names, attributes and method calls), you dont have to worry about it.

Platform services are available (read: resolved) at runtime rather than design-time. So you dont have to pick out the services from a list prior to compilation, instead the RTL will create the correct object factory – which in turn exposes the correct service-item for the browser you use. The number of services available depends on what you have used, which the compiler takes care of.

What features?

Services deal with features that the browsers may or may-not expose, hence the service architecture for querying what you can and cannot do. For instance, access to the camera or image-picker is a typical service API, because it greatly depends on the mobile-device, what JS API’s are available (phonegap or native access) – and last but not least: user interaction.

Other types of services are more low-level in nature: websocket support, rest-client support, JSONP support, data encoding mechanisms, encryption support, access to gps tracking, camera access, disk access, storage functionality, database access.

if PlatformServices.GetCameraAPI(IAccess) then
begin
  if IAccess.ShowCamera(mediastream, [ocTakePhoto,ocPhotoPicker]) then
  begin
    //mediastream now contains picture or movie frames
  end else
  writeln('debug: camera was canceled');
end else
raise exception.create('Camera not supported error');

Now in order for us to shield you from the gory details we have started to implement the most common API’s as run-time services. This means easy to use, easier for us to maintain and setting a standard for future expansion of the Smart Mobile Studio run-time library.

Working with platform services

Coding based on platform services is all about abstraction. You simply ask the system to provide you with access to a service, and if the system supports that service (or allows it) you are given an access interface. The abstraction layer and working with interfaces like this is very effective. You can of course create class instances ad-hoc if you like, but that kinda defeats the portability of your code and binds you to that spesific class (which may be webkit spesific, firefox spesific or something else). The goal of platform services is to make sure your code runs and behaves identical on all supported platforms.

Websocket example

Here is an example of how to access the websocket platform service, using the access interface to create a websocket element, initialize it and then connect to a server:

procedure TForm1.btnConnectClick(Sender: TObject);
var
  IAccess:  IW3WebSocketService;
  ISocket:  IWebSocket;
begin
  if PlatformServices.GetWebSocketAPI(IAccess) then
  begin
    if IAccess.CreateSocket(ISocket) then
    Begin

      //Attach connected event handler
      ISocket.SetOnOpenEvent( procedure (Access:IWebSocket)
        begin
          Writeln('Websocket is now open!');
        end);

      // Attach disconnect event handler
      ISocket.SetOnClosedEvent( procedure (Access:IWebSocket)
        begin
          Writeln('Websocket is now closed');
        end);

      // Attach Message handler
      ISocket.SetOnMessageEvent( procedure (IO:IWebSocketIO;
                                 Message:TWebSocketMessageData)
        begin
          WriteLn(message.mdText);
        end);

      // Connect to our server
      ISocket.Connect('ws://192.168.38.108:8080',['void']);
    end;
  end;
end;

As you can see from the code above you are pretty much shielded from all the technical aspects of the WebSocket standard. You simply ask for the service and then use the service to create a websocket instance – which you access through the socket interface.

As a user you don’t have to care about the gory details here. You don’t have to worry about stuff like instance factories, if your code is running on FireFox, Chrome, MS10 or Safari. And even better is that you no longer have to care if it’s running on desktop, iOS, Android or an embedded system.

Here is the full IWebSocket interface

  IWebSocketIO = interface
    Procedure WriteString(const value:String);
    procedure WriteMemory(const Value:TMemoryHandle);
    Procedure WriteStream(const Value:TStream);
    Procedure WriteBinary(Const Data:TBinaryData);

    function  GetSocketState:TWebSocketState;
    function  GetConnected:Boolean;
    function  GetURL:String;
    function  GetProtocol:String;

    function  GetBinaryMessageDataType:TWebSocketBinaryDataType;
    procedure SetBinaryMessageDataType(const Value:TWebSocketBinaryDataType);

    procedure Disconnect;

    Property  BinaryMessageDataType:TWebSocketBinaryDataType
              read GetBinaryMessageDataType
              write SetBinaryMessageDataType;

    Property  SocketState:TWebSocketState read getSocketState;
    Property  Connected:Boolean read getConnected;
    property  URL:String read getURL;
    Property  Protocol:String read getProtocol;
  end;

  (* Websocket service interface *)
  IWebSocket = interface
    function  GetBinaryMessageDataType:TWebSocketBinaryDataType;
    procedure SetBinaryMessageDataType(const Value:TWebSocketBinaryDataType);

    function  GetSocketState:TWebSocketState;
    function  GetConnected:Boolean;
    function  GetURL:String;
    function  GetProtocol:String;

    procedure Connect(URL:String;Protocols:Array of String);
    procedure Disconnect;

    Procedure WriteString(const value:String);
    procedure WriteMemory(const Value:TMemoryHandle);
    Procedure WriteStream(const Value:TStream);
    Procedure WriteBinary(Const Data:TBinaryData);

    function  GetOnOpenEvent:TWebSocketOpenEvent;
    function  GetOnClosedEvent:TWebSocketCloseEvent;
    function  GetOnMessageEvent:TWebSocketMessageEvent;
    function  GetOnErrorEvent:TWebSocketErrorEvent;

    procedure SetOnOpenEvent(const Handler:TWebSocketOpenEvent);
    procedure SetOnClosedEvent(const Handler:TWebSocketCloseEvent);
    procedure SetOnMessageEvent(const Handler:TWebSocketMessageEvent);
    procedure SetOnErrorEvent(const Handler:TWebSocketErrorEvent);

    Property  SocketState:TWebSocketState read getSocketState;
    Property  Connected:Boolean read getConnected;
    property  URL:String read getURL;
    Property  Protocol:String read getProtocol;

    Function  GetServiceElement:IServiceElement;

    Property  BinaryMessageDataType:TWebSocketBinaryDataType
              read GetBinaryMessageDataType
              write SetBinaryMessageDataType;
    Property  OnOpen:TWebSocketOpenEvent read GetOnOpenEvent write SetOnOpenEvent;
    Property  OnClosed:TWebSocketCloseEvent read GetOnClosedEvent write SetOnClosedEvent;
    Property  OnMessage:TWebSocketMessageEvent read GetOnMessageEvent write SetOnMessageEvent;
    Property  OnError:TWebSocketErrorEvent read GetOnErrorEvent write SetOnErrorEvent;
  end;

Keep it simple, keep it safe

The benefit for us in terms of delivering a better Smart Mobile Studio, is that it allows us to compartmentalize our code. We get to create service classes for FireFox, Chrome and so on without having to go through hoops just to make it work identically everywhere. It works almost like a driver or plugin system, where the correct factory and service provider (sigh) is resolved at runtime. Most of the time we get away with a single implementation class, but it’s nice to know that we can isolate code for one browser in its own class, and support for the same features for another browser in another class. This helps keep the RTL sane and organized.

Platform services is pretty cool and saves you a lot of time

Platform services is pretty cool and saves you a lot of time

While no date has been set, I can at least tell you that this system will not be available until we reach at least version 2.3.x; at the very least.

  1. November 4, 2015 at 12:30 pm

    Reading your code, it resembles one that I’ve created once, it’s a smart wrapper jquery class to use with Smart, f.i. when I click in a button it displays a message. So I can call this jQuery-like function

    myObj(‘#btn1’).on(‘click’,function(e: variant): variant
    begin
    WriteLn(‘Button1 was Clicked’);
    end);

    …but I would like to implement the same method using a neater layout of code, something like this:

    procedure btn1Click(Sender: TObject);
    Begin
    WriteLn(‘Button1 was Clicked’);
    end;

    Any idea for insane usage of Delphi;

  2. Jon Lennart Aasenden
    November 4, 2015 at 7:16 pm

    JQuery is really not needed for Smart Pascal. JQuery is great in ordinary JS where elements and their handle’s are unknown. But in a component and class driven architecture like the smart RTL provides, the component or class always know’s it’s own handle (or the handle to the visual element it manages) — so i cant really see why you would want to use jQuery at all.

    But for building syntax like the stuff you describe, you should be able to do something like this:

    TEventReference = Class(TObject)
    private
    FInstance: TW3TagObj;
    FEventName: String;
    public
    procedure Execute(EntryPoint:TProcedureRef);
    constructor Create(Obj:TW3TagObj;EventName:String);
    end;

    TControlReference = class(TObject)
    private
    FParent: TW3Component;
    FInstance: TW3TagObj;
    public
    function Attach(EventName:String):TEventReference;
    constructor Create(AParent:TW3Component;AInstance:TW3TagObj);
    end;

    TSelector = class abstract
    public
    class function &Object(Container:TW3Component;ControlName:String):TControlReference;
    end;

    //#############################################################################
    // TEventReference
    //#############################################################################

    constructor TEventReference.Create(Obj:TW3TagObj;EventName:String);
    begin
    inherited Create;
    FInstance := Obj;
    FEventName := EventName;
    end;

    procedure TEventReference.Execute(EntryPoint:TProcedureRef);
    begin
    w3_AddEvent(FInstance.Handle,FEventName,Entrypoint);
    end;

    //#############################################################################
    // TControlReference
    //#############################################################################

    constructor TControlReference.Create(AParent:TW3Component;AInstance:TW3TagObj);
    begin
    inherited Create;
    FParent := AParent;
    FInstance := AInstance;
    end;

    function TControlReference.Attach(EventName:String):TEventReference;
    begin
    result := TEventReference.Create(Finstance,EventName);
    end;

    //#############################################################################
    // TSelector
    //#############################################################################

    class function TSelector.&Object(Container:TW3Component;ControlName:String):TControlReference;
    var
    x: Integer;
    mObj: TObject;
    begin
    result := NIL;
    if ContainerNIL then
    begin
    controlname:=controlname.trim();
    if controlname.length>0 then
    begin
    mObj := Container.ChildByName(ControlName);
    if mObjNIL then
    result := TControlReference.Create(Container,TW3Component(mObj));
    end;
    end;
    end;

  1. No trackbacks yet.

Leave a reply to warleyalex Cancel reply