TMB client: iOS and macOS

The principles of the Text Message Bus bus are described in the main TMB article. Do read that article first.

Brief summary of components

common (library) A few common declarations.
drawing (library) Create Bezier curves for circle, square and triangle.
networking (library) Socket-level networking based on CFStream. Reads and writes data.
class Figure NSView that draws a geometric figure.
class MainView NSViewController for the main NSView. Singleton.
class MessageBusTester Application delegate. Processes incoming messages. Singleton.
class MessageBusClient Message bus logic. Sends and receives messages. Singleton.
User interface Created with Interface Builder.

Each of the three singleton classes have a function instance() that returns the instance, and each use a static variable the_instance.

Libraries drawing and network offload some source from Figure and MessageBusClient respectively.

Execution flow


in main()
   calls NSApplicationMain
      creates the complete main view from NIB
      does [MainView awakeFromNib]
         sets the_instance to self
      creates the the application delegate ($MessageBusTester)
      does [MessageBusTester applicationDidFinishLaunching]
         does [self start]
            sets the_instance to self
            create the single instance of $MessageBusClient by calling +instance
               sets the_instance to self
            calls [MessageBusClient start]
               calls network_start()
                  creates client socket
                  connects to server
                  set network_read_callback() as read callback

Incoming message

data becomes available on the socket
the framework calls network_read_callback()
   reads data from the socket
      calls [ MessageBusClient on_incoming_data ]
          build lines from incoming character
          for each complete line
             calls [ MessageBusTester on_incoming_message ]
                decodes message into group, key and value
                calls [ MainView set_property ]
                   calls [ Figure +get_figure group ]
                   calls [ Figure set_property key value ]

Sending a figure property

Example for the upper Figure, property colour and value red.

someone calls [ Figure send :key :value] with "colour" and "red"
   builds string "/upper/colour red"
   calls [ MessageBusClient send_peer_message ]
      calls [ MessageBusClient send_message ]
         prepends a colon
         appends a newline
         calls network_write()
             calls CFWriteStreamWrite()

Colour or shape button clicked

Example for the upper “Red” button.

user clicks the upper "Red" button
   framework calls [ Figure redClicked ]
      calls [ Figure send ] with "colour" and "red"
         ... as Sending a figure property ...

“Send all” button clicked

user clicks on the "Send all" button
   framework calls [ MainView send_all ]
      for each Figure (upper and lower)
         calls [ Figure send_all ]
            for each property (colour and shape)
               calls [ Figure send ] with property and property value
                  ... as Sending a figure property ...

Subscription button clicked

Connections from buttons to methods are set up in Interface Builder.

These buttons send their own labels.

user clicks on a subscribe or unsubscribe button
   framework calls [ MainView send_button_verbatim ]
      extracts the buttons text
      calls [ MessageBusClient send_command_message ]
         calls [ MessageBusClient send_message ]
            append a newline
            calls network_write()
                calls CFWriteStreamWrite()

Guide to the source code

The code is mostly limited to header files. Only the most important parts of the files are included.

Summary of source code

The code is mostly limited to header files. Only the most important parts of the files are included.


# define VECTSIZE(x) ((sizeof (x)) / (sizeof ((x)[0])))
typedef const char cchar;
NSString *a2nss(cchar *);    -- ASCII C string to NSString

Main program

int main(int argc, const char *argv[]) {
    return NSApplicationMain(argc, argv);


NSBezierPath *drawCircle  (void);
NSBezierPath *drawSquare  (void);
NSBezierPath *drawTriangle(void);

These functions return Bezier curves. Implementation of these functions go into a separate modules to make class Figure smaller and easier to understand.


@interface Figure  : NSView
+     (Figure *)get_figure   :(NSString *)id;
-     (void    )send_all;     -- Callback for button "Send all"
-     (void    )set_property :(NSString *)name :(NSString *)value;

Figure keeps track of all instances of Figure. +get_figure() returns a figure based on the buttons string property identifier:

    Figure → NSView → NSUserInterfaceItemIdentification → identifier

There are two figures, “upper” and “lower”.


@interface MainView : NSViewController
+     (MainView *)instance;
-     (void)set_property :(NSString *)group key:(NSString *)key value:(NSString *)value;

Method set_property is called by [MessageBusTester on_incoming_message]. It uses [Figure get_figure] to get the Figure that matches group (group is ether “upper” or “lower”), and calls [Figure set_property] on that Figure.


@interface MessageBusClient : NSObject
@property (strong, nonatomic) NSWindow *window;
+     (MessageBusClient *)instance;
-     (void) start;                              -- Called by applicationDidFinishLaunching
-     (void) stop;                               -- Called by applicationWillTerminate
-     (void) send_peer_message    :(cchar *)msg; -- Called by Figure
-     (void) send_command_message :(cchar *)msg; -- Called by MainView
-     (void) on_incoming_data     :(char  *)msg; -- Called by network_read_callback()

Methods send_peer_message and send_command_message both append a newline and pass the line on to network_write().In addition, send_peer_message prepends a colon.


@interface MessageBusTester : NSObject 
+     (MessageBusTester *)instance;
-     (void) on_incoming_message: (char *)msg; -- Called by MessageBusClient


@class MessageBusClient;

void network_start(NSString *host, int port, MessageBusClient *client);
void network_stop (void);       <em>-- Called by MessageBusClient</em>
long network_write(char *buf);  <em>-- Called by MessageBusClient</em>
static void network_read_callback(...);    <em>-- Called by framework</em>

Implements socket-level networking using CFStream.When data is available, network_read_callback() calls [MessageBusClient on_incoming_data].The instance to call is passed as a parameter to network_start().

You can reach me by email at “lars dash 7 dot sdu dot se” or by telephone +46 705 189090

View source for the content of this page.