Using Bonjour to find iOS companion app
Let's say you have a Xojo desktop app and you like to have an iOS app, which can connect to the desktop application. We like to avoid the user entering some kind of connection information and just automatically connect. Then once connected, the iOS app can do some things and talk to the desktop app for more. Let's say the desktop app manages the database and the iOS app can show data on the go, e.g. after scanning a barcode.
For this project we use Xojo with two projects, one for desktop and one for iOS.
Desktop Project
We use a ServerSocket to wait for incoming connections. We let the ServerSocket pick a random port instead of relaying on a fixed port number.
Public Sub StartServer() ServerSocket = New MyServerSocket ServerSocket.MinimumSocketsAvailable = 3 ServerSocket.Listen System.DebugLog "Waiting on port "+ServerSocket.Port.ToString End Sub
We need a subclass of NSNetServiceMBS to advertise the service in the local network. We pick a name for our service, which may be the name of the user or his machine. For now it is "MyTestService". Next we decide how we name the service type. This needs to be unique, but e.g. "_appname._tcp." may do it with the app name inserted without space and in lowercase letters.
Public Sub AdvertiseService() Const domain = "" // default Const type = "_test._tcp." Const name = "MyTestService" Dim port As Integer = ServerSocket.port Service = New MyNetService(domain, type, name, port) Service.publish End Sub
We need a subclass of TCP Socket to process incoming requests. Since a socket provides a stream of bytes which arrive in chunks, we have to decide how we send pieces of information. It is a big data stream and you need a delimited to see where data packets end. For our example we decide to use JSON with a newline character. All our data packets end with "}"+EndOfLine.Unix, so we can cut them there.
Here is the code for sending:
Public Sub Send(j as JSONItem) Me.Write j.ToString Me.Write EndOfLine.UNIX // we send EndOfLine, so we can watch on other side for } + endofline as delimiter End Sub
And in DataAvailable event, we read data into a buffer and split as data arrives and call the Process method to work on the packets:
Sub DataAvailable() System.DebugLog CurrentMethodName Dim buf As String = Me.ReadAll buffer = buffer + buf Dim Delimiter As String = "}"+EndOfLine.UNIX Dim pos As Integer = buffer.IndexOf(Delimiter) While pos >= 0 Dim packet As String = buffer.Left(pos+1) Process packet buffer = buffer.Middle(pos+2) pos = buffer.IndexOf(Delimiter) Wend End Sub
Don't worry with the delimiter. If your text contains a } and a new line character, the JSON will have it encoded as \r or \n in the JSON. But please let us know if you find an edge case.
iOS Project
Our subclass of NSNetServiceBrowserMBS class to look for service. We browse for "_test._tcp." in the example, but please change it to match whatever you use in the desktop application.
Public Sub StartBrowser() browser = New ServiceBrowser Const domain = "" // default Const type = "_test._tcp." browser.MainScreen = Self browser.searchForServicesOfType type, domain End Sub
Once you find a service via the browser, you need to resolve it with the NSNetServiceMBS class to know the IP and port.
Sub DidFindService(service as NSNetServiceMBS, moreComing as Boolean) Handles DidFindService System.DebugLog CurrentMethodName // make a new object so we have our events installed dim m as new NetworkService(Service) // need to resolve to know IP and Port m.resolve m.MainScreen = Me.MainScreen services.Add m End Sub
We need a subclass of NSNetServiceMBS to resolve the services. When the browser finds a service, we resolve it to see the port number and connect via socket:
Public Sub FoundService(m as NetworkService) If socket = Nil Then // for now ignore second one found FoundService = m socket = New MySocket socket.Address = m.hostName Socket.Port = m.port Socket.MainScreen = Me System.DebugLog "Connect to "+m.hostName+":"+m.port.ToString StatusLabel.Text = "Connecting..." Socket.Connect End If End Sub
And our Socket subclass can send/receive messages. We use JSON and the code in desktop and iOS is the same.
The example will be included in next pre-release. Let us know if you have questions.