devxlogo

Bonjour Programming on the iPhone, Part I

Bonjour Programming on the iPhone, Part I

Bonjour is Apple’s implementation of the Zeroconf protocol, which enables the automatic discovery of computers, devices and services on an IP network. In this article, you will learn how to implement Bonjour on the iPhone by using the NSNetService class to publish a service. You will also use the NSNetServiceBrowser class to discover services that have been published. In the next article, you will learn how to communicate with another device that you have discovered using TCP/IP.

Creating the Project

Using Xcode, create a View-based Application project and name it as Bonjour.

Double-click on the BonjourViewController.xib file and populate the View window with the following views (see Figure 1):

    * Text Field
    * Round Rect Button
    * Label
    * Table View
    * Text View

Figure 1: Populating the View window with the various views.

In the BonjourViewController.h file, add the following statements in bold:

#import <UIKit/UIKit.h>@interface BonjourViewController : UIViewController {    //---outlets---    IBOutlet UITableView *tbView;    IBOutlet UITextField *message;    IBOutlet UITextView *debug;}//---expose the outlets as properties---@property (nonatomic, retain) UITableView *tbView;@property (nonatomic, retain) UITextField *message;@property (nonatomic, retain) UITextView *debug;//---actions----(IBAction) btnConnect:(id)sender;-(IBAction) btnSend:(id)sender;-(IBAction) doneEditing;@end

In the BonjourViewController.xib window, perform the following connections:

    * Control-click the File’s Owner item and drag and drop it over the Text Field view. Select message.

    * Control-click the File’s Owner item and drag and drop it over the Table View. Select tbView.

    * Control-click the File’s Owner item and drag and drop it over the Text View. Select debug.

    * Control-click the Send button and drag and drop it over the File’s Owner item. Select btnSend:.

    * Control-click the Connect button and drag and drop it over the File’s Owner item. Select btnConnect:.

    * Right-click on the Text Field view and connect the Did End on Exit event to the File’s Owner item. Select doneEditing:.

    * Right-click on the Table View and connect the dataSource outlet to the File’s Owner item.

    * Right-click on the Table View and connect the delegate outlet to the File’s Owner item.

To verify that all the connections are made correctly, right-click on the File’s Owner item and view its connections (see Figure 2).

Figure 2: Verifying that all the connections are made correctly.

Publishing a Service

With all the views and actions wired up, let’s start by seeing how you can publish a service.

In the BonjourAppDelegate.h file, add the following statements in bold:

#import @class BonjourViewController;@interface BonjourAppDelegate : NSObject  {    UIWindow *window;    BonjourViewController *viewController;    //---use this to publish a service---        NSNetService *netService;}@property (nonatomic, retain) IBOutlet UIWindow *window;@property (nonatomic, retain) IBOutlet     BonjourViewController *viewController;@end

Basically, you will use the NSNetService class to publish your presence on the network.

In the BonjourAppDelegate.m file, add the following statements in bold:

#import "BonjourAppDelegate.h"#import "BonjourViewController.h"@implementation BonjourAppDelegate@synthesize window;@synthesize viewController;- (void)applicationDidFinishLaunching:(UIApplication *)application {        [window addSubview:viewController.view];    [window makeKeyAndVisible];            //---publish the service---    netService = [[NSNetService alloc]                      initWithDomain:@""                                type:@"_MyService._tcp."                                name:@""                                port:9876];    netService.delegate = self;    [netService publish];    }-(void)netService:(NSNetService *)aNetService     didNotPublish:(NSDictionary *)dict {    NSLog(@"Service did not publish: %@", dict);}- (void)applicationWillTerminate:(UIApplication *)application {    //---stop the service when the application is terminated---    [netService stop];}- (void)dealloc {    [netService release];     [viewController release];    [window release];    [super dealloc];}@end

Here, you advertise your presence on the network by publishing a network service when your application has finished launching (applicationDidFinishLaunching:). You publish a network service by passing several parameters to the NSNetService class:

    netService = [[NSNetService alloc]                      initWithDomain:@""                                type:@"_MyService._tcp."                                name:@""                                port:9876];

The first argument specifies the domain for the service. You use @”” to denote the default domain. The second argument indicates the service type and transport layer. In this example, I named the service as MyService and it uses TCP. Note that you need to prefix the service name and protocol with a “_” and end the protocol with a “.”. The third argument specifies the name of the service, which must be a unique name (I have used an empty string in this case). Finally, specify the port number on which the service is published via the fourth argument.

You also implemented the netService:didNotPublish: method so that in the event the service is not published successfully, you will write a message to the debugger console.

When the application exits (applicationWillTerminate:) you will stop publishing the service.

Browsing for Services

Now that you have seen how to publish a service, let’s see how you can browse for services that have been published on the network. In the BonjourViewController.h file, add the following statements in bold:

#import @interface BonjourViewController : UIViewController {    IBOutlet UITableView *tbView;    IBOutlet UITextField *message;    IBOutlet UITextView *debug;    //---use for browsing services---    NSNetServiceBrowser *browser;    NSMutableArray *services;}@property (nonatomic, retain) UITableView *tbView;@property (nonatomic, retain) UITextField *message;@property (nonatomic, retain) UITextView *debug;@property (readwrite, retain) NSNetServiceBrowser *browser;@property (readwrite, retain) NSMutableArray *services;-(IBAction) btnConnect:(id)sender;-(IBAction) btnSend:(id)sender;-(IBAction) doneEditing;@end

In the BonjourViewController.m file, add the following statements in bold:

#import "BonjourViewController.h"#import @implementation BonjourViewController@synthesize tbView;@synthesize message;@synthesize debug;@synthesize browser;@synthesize services;//---set the number of rows in the TableView---- (NSInteger)tableView:(UITableView *)tableView  numberOfRowsInSection:(NSInteger)section {     return [services count];    }//---display the individual rows in the TableView---- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {    static NSString *CellIdentifier = @"Cell";        UITableViewCell *cell = [tableView         dequeueReusableCellWithIdentifier:CellIdentifier];    if (cell == nil) {        cell = [[[UITableViewCell alloc]             initWithStyle:UITableViewCellStyleDefault             reuseIdentifier:CellIdentifier] autorelease];    }    //---display the hostname of each service---    cell.textLabel.text = [[services objectAtIndex:indexPath.row] hostName];    return cell;}//---browse for services----(void) browseServices {    services = [NSMutableArray new];    self.browser = [[NSNetServiceBrowser new] autorelease];    self.browser.delegate = self;    [self.browser searchForServicesOfType:@"_MyService._tcp." inDomain:@""];}-(void)viewDidLoad {    [self browseServices];    [super viewDidLoad];}//---hide the keyboard----(IBAction) doneEditing {    [message resignFirstResponder];    }//---services found----(void)netServiceBrowser:(NSNetServiceBrowser *)aBrowser           didFindService:(NSNetService *)aService moreComing:(BOOL)more {    [services addObject:aService];    debug.text = [debug.text stringByAppendingString:                     @"Found service. Resolving address...
"];    [self resolveIPAddress:aService];}//---services removed from the network----(void)netServiceBrowser:(NSNetServiceBrowser *)aBrowser         didRemoveService:(NSNetService *)aService moreComing:(BOOL)more {    [services removeObject:aService];    debug.text = [debug.text stringByAppendingFormat:@"Removed: %@
",                      [aService hostName]];    [self.tbView reloadData];}//---resolve the IP address of a service----(void) resolveIPAddress:(NSNetService *)service {        NSNetService *remoteService = service;    remoteService.delegate = self;    [remoteService resolveWithTimeout:0];}//---managed to resolve----(void)netServiceDidResolveAddress:(NSNetService *)service {    NSString           *name = nil;    NSData             *address = nil;    struct sockaddr_in *socketAddress = nil;    NSString           *ipString = nil;    int                port;    for(int i=0;i < [[service addresses] count]; i++ )    {        name = [service name];        address = [[service addresses] objectAtIndex: i];        socketAddress = (struct sockaddr_in *) [address bytes];        ipString = [NSString stringWithFormat: @"%s",             inet_ntoa(socketAddress->sin_addr)];        port = socketAddress->sin_port;        debug.text = [debug.text stringByAppendingFormat:            @"Resolved: %@-->%@:%hu
", [service hostName], ipString, port];    }    [self.tbView reloadData];}//---did not managed to resolve----(void)netService:(NSNetService *)service     didNotResolve:(NSDictionary *)errorDict {    debug.text = [debug.text stringByAppendingFormat:                     @"Could not resolve: %@
", errorDict];}- (void)dealloc {    [tbView release];    [message release];    [debug release];        [browser release];    [services release];        [super dealloc];}@end

There is quite a bit of coding involved here. So, let’s take a more detailed look.

First, you defined the browseServices method, which uses the NSNetServiceBrowser class to search for the service named “_MyService._tcp.” in the default domain.

As services are discovered, the netServiceBrowser: didFindService:moreComing: method will be called. In this method, you will add all the discovered services into the services mutable array. You will also try to resolve the IP address of the discovered service by calling the resolveIPAddress: method, which you will define.

The resolveIPAddress: method uses the resolveWithTimeout: method of the NSNetService instance (representing the service that was discovered) to obtain its IP address. If it managed to resolve the IP address, the netServiceDidResolveAddress: method will be called. If it did not managed to resolve the IP address, the netService:didNotResolve: method will be called.

In the netServiceDidResolveAddress: method, you extract the IP addresses of the service. You then try to reload the TableView.

When services are removed from the network, the netServiceBrowser:didRemoveService: method will be called, and hence in this method you will remove the service from the services mutable array.

The rest of the code involves load the TableView with the host name of the services that have been discovered.

Testing the Application

Figure 3: Discovering services published on the same network.

That’s it! Try deploying the application onto two devices (one on the iPhone Simulator and one on a real device). When the application is running, it will search for all services published on the same network. As services are discovered, you will be able to see the their names appear in the TableView (see Figure 3).

Summary

In this article, you have seen how to publish a service on the network and how to discover them on the local network. In the next article, I will show you how you can communicate with another device on the network using TCP/IP.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist