Skip to content

Instantly share code, notes, and snippets.

@DelphiWorlds
Created September 10, 2021 23:00
Show Gist options
  • Save DelphiWorlds/ed4a938ad49072467c3f882af3ff338c to your computer and use it in GitHub Desktop.
Save DelphiWorlds/ed4a938ad49072467c3f882af3ff338c to your computer and use it in GitHub Desktop.
Example of how a Bonjour service *might* be set up on macOS. Has not been fully tested
unit Unit1;
// With help from this SO post:
// https://stackoverflow.com/a/16678161/3164070
// ******* NOTE *********
// As per the comment in the Acivate method:
// This assumes you have actually created a socket that is listening on the selected port
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Controls.Presentation, FMX.StdCtrls,
Macapi.Foundation, Macapi.ObjectiveC, Macapi.CocoaTypes;
type
NSRunLoopMode = NSString;
PNSInputStream = ^NSInputStream;
PNSOutputStream = ^NSOutputStream;
NSNetServiceClass = interface(NSObjectClass)
['{3E78C456-0906-4BDB-98A3-946ACF2A36B7}']
{class} function dataFromTXTRecordDictionary(txtDictionary: NSDictionary): NSData; cdecl;
{class} function dictionaryFromTXTRecordData(txtData: NSData): NSDictionary; cdecl;
end;
NSNetService = interface(NSObject)
['{64E3A9B9-03F2-4E69-8586-FF6AAB14E94E}']
function &type: NSString; cdecl;
function addresses: NSArray; cdecl;
function delegate: Pointer; cdecl;
function domain: NSString; cdecl;
function getInputStream(inputStream: PNSInputStream; outputStream: PNSOutputStream): Boolean; cdecl;
function hostName: NSString; cdecl;
function includesPeerToPeer: Boolean; cdecl;
function initWithDomain(domain: NSString; &type: NSString; name: NSString): Pointer; overload; cdecl;
function initWithDomain(domain: NSString; &type: NSString; name: NSString; port: Integer): Pointer; overload; cdecl;
function name: NSString; cdecl;
function port: NSInteger; cdecl;
procedure publish; cdecl;
procedure publishWithOptions(options: NSNetServiceOptions); cdecl;
procedure removeFromRunLoop(aRunLoop: NSRunLoop; forMode: NSRunLoopMode); cdecl;
procedure resolveWithTimeout(timeout: NSTimeInterval); cdecl;
procedure scheduleInRunLoop(aRunLoop: NSRunLoop; forMode: NSRunLoopMode); cdecl;
procedure setDelegate(delegate: Pointer); cdecl;
procedure setIncludesPeerToPeer(includesPeerToPeer: Boolean); cdecl;
function setTXTRecordData(recordData: NSData): Boolean; cdecl;
procedure startMonitoring; cdecl;
procedure stop; cdecl;
procedure stopMonitoring; cdecl;
function TXTRecordData: NSData; cdecl;
end;
TNSNetService = class(TOCGenericImport<NSNetServiceClass, NSNetService>) end;
NSNetServiceDelegate = interface(IObjectiveC)
['{00728682-DC04-47C4-9497-43D2BBA88F2F}']
procedure netServiceDidAcceptConnectionWithInputStream(sender: NSNetService; didAcceptConnectionWithInputStream: NSInputStream;
outputStream: NSOutputStream); cdecl;
procedure netServiceDidNotPublish(sender: NSNetService; didNotPublish: NSDictionary); cdecl;
procedure netServiceDidNotResolve(sender: NSNetService; didNotResolve: NSDictionary); cdecl;
procedure netServiceDidPublish(sender: NSNetService); cdecl;
procedure netServiceDidResolveAddress(sender: NSNetService); cdecl;
procedure netServiceDidStop(sender: NSNetService); cdecl;
procedure netServiceDidUpdateTXTRecordData(sender: NSNetService; didUpdateTXTRecordData: NSData); cdecl;
procedure netServiceWillPublish(sender: NSNetService); cdecl;
procedure netServiceWillResolve(sender: NSNetService); cdecl;
end;
TBonjour = class;
TNSNetServiceDelegate = class(TOCLocal, NSNetServiceDelegate)
private
FBonjour: TBonjour;
public
{ NSNetServiceDelegate }
procedure netServiceDidAcceptConnectionWithInputStream(sender: NSNetService; didAcceptConnectionWithInputStream: NSInputStream;
outputStream: NSOutputStream); cdecl;
procedure netServiceDidNotPublish(sender: NSNetService; didNotPublish: NSDictionary); cdecl;
procedure netServiceDidNotResolve(sender: NSNetService; didNotResolve: NSDictionary); cdecl;
procedure netServiceDidPublish(sender: NSNetService); cdecl;
procedure netServiceDidResolveAddress(sender: NSNetService); cdecl;
procedure netServiceDidStop(sender: NSNetService); cdecl;
procedure netServiceDidUpdateTXTRecordData(sender: NSNetService; didUpdateTXTRecordData: NSData); cdecl;
procedure netServiceWillPublish(sender: NSNetService); cdecl;
procedure netServiceWillResolve(sender: NSNetService); cdecl;
public
constructor Create(const ABonjour: TBonjour);
end;
TBonjour = class(TObject)
private
FPort: Integer;
FService: NSNetService;
FServiceDelegate: TNSNetServiceDelegate;
FServiceName: string;
public
constructor Create(const AServiceName: string);
destructor Destroy; override;
procedure Activate;
property Port: Integer read FPort write FPort;
end;
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
FBonjour: TBonjour;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
uses
Macapi.Helpers;
{ TNSNetServiceDelegate }
constructor TNSNetServiceDelegate.Create(const ABonjour: TBonjour);
begin
inherited Create;
FBonjour := ABonjour;
end;
procedure TNSNetServiceDelegate.netServiceDidAcceptConnectionWithInputStream(sender: NSNetService; didAcceptConnectionWithInputStream: NSInputStream;
outputStream: NSOutputStream);
begin
Log.d('TNSNetServiceDelegate.netServiceDidAcceptConnectionWithInputStream');
end;
procedure TNSNetServiceDelegate.netServiceDidNotPublish(sender: NSNetService; didNotPublish: NSDictionary);
begin
Log.d('TNSNetServiceDelegate.netServiceDidNotPublish');
end;
procedure TNSNetServiceDelegate.netServiceDidNotResolve(sender: NSNetService; didNotResolve: NSDictionary);
begin
Log.d('TNSNetServiceDelegate.netServiceDidNotResolve');
end;
procedure TNSNetServiceDelegate.netServiceDidPublish(sender: NSNetService);
begin
Log.d('TNSNetServiceDelegate.netServiceDidPublish');
end;
procedure TNSNetServiceDelegate.netServiceDidResolveAddress(sender: NSNetService);
begin
Log.d('TNSNetServiceDelegate.netServiceDidResolveAddress');
end;
procedure TNSNetServiceDelegate.netServiceDidStop(sender: NSNetService);
begin
Log.d('TNSNetServiceDelegate.netServiceDidStop');
end;
procedure TNSNetServiceDelegate.netServiceDidUpdateTXTRecordData(sender: NSNetService; didUpdateTXTRecordData: NSData);
begin
Log.d('TNSNetServiceDelegate.netServiceDidUpdateTXTRecordData');
end;
procedure TNSNetServiceDelegate.netServiceWillPublish(sender: NSNetService);
begin
Log.d('TNSNetServiceDelegate.netServiceWillPublish');
end;
procedure TNSNetServiceDelegate.netServiceWillResolve(sender: NSNetService);
begin
Log.d('TNSNetServiceDelegate.netServiceWillResolve');
end;
{ TBonjour }
constructor TBonjour.Create(const AServiceName: string);
begin
inherited Create;
FServiceName := AServiceName;
FServiceDelegate := TNSNetServiceDelegate.Create(Self);
end;
destructor TBonjour.Destroy;
begin
FServiceDelegate.Free;
inherited;
end;
procedure TBonjour.Activate;
begin
// This assumes you have actually created a socket that is listening on the selected port
FService := nil;
FService := TNSNetService.Create;
FService := TNSNetService.Wrap(FService.initWithDomain(StrToNSStr('local.'), StrToNSStr('_' + FServiceName + '._tcp.'), StrToNSStr(''), FPort));
FService.setDelegate(FServiceDelegate.GetObjectID);
FService.publish;
end;
{ TForm1 }
constructor TForm1.Create(AOwner: TComponent);
begin
inherited;
FBonjour := TBonjour.Create('YourServiceName');
FBonjour.Port := 8080;
end;
destructor TForm1.Destroy;
begin
FBonjour.Free;
inherited;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
FBonjour.Activate;
end;
end.
@Knut-Knoblauch
Copy link

Knut-Knoblauch commented Sep 15, 2021

Activate needs to change to this for it to work

procedure TBonjour.Activate;
begin
  // This assumes you have actually created a socket that is listening on the selected port
  FService := nil;
  FService := TNSNetService.Wrap(TNSNetService.Alloc.initWithDomain(StrToNSStr('local.'), StrToNSStr('_' + FServiceName + '._tcp.'), StrToNSStr(''), FPort));
  FService.setDelegate(NSNetServiceDelegate((FServiceDelegate as ILocalObject).GetObjectID));
  FService.publish;
end;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment