Overview Bonjour is an Apple service discovery protocol, which locates devices such as printers, other computers, and the services that those devices offer on a local network using multicast Domain Name System (mDNS) service records. You can set unique names for how your Macs computer name appears locally, from file sharing and networking, and even Bonjour services, with the help of the scutil command. This allows you to have a custom hostname for Terminal and SSH, another friendlier name for what's visible to others on local networks, and yet another name only visible to.
This article provides easy step-by-step instructions allowing to create your first app in Swift. You'll create an application for searching the network for computers with enabled file sharing. With the help of this app, users will be able to search the network for computers which use AFP or SMB protocols for files and folders sharing, and connect to any of them.
Note: You'll need the latest version of Xcode 6.1.
To create a Swift project in Xcode:
- Start Xcode.
- Choose File > New > Project > OS X > Application > Cocoa Application.
- Enter SharingBrowser into Product Name field and choose Swift from the Language drop-down list. Make sure Use Core Data checkbox is disabled, and click Next.
- Select the folder to save the project to, and click Create.
A Swift project's structure is nearly identical to an Objective-C project, with one important distinction: Swift has no header files. There is no explicit delineation between the implementation and the interface, so all the information about a particular class resides in a single .swift file.
Adding Classes to the Project
SharedPoint class
Create a new Swift class named SharedPoint. To do this, choose from the menu: File > New > File > OS X > Source > Swift. This class is designed to describe the compliance between file sharing service and its IP address.
SharedPoint.swift file contains a string with import statement:
This string is intended to import Objective-C Foundation system framework into the Swift project. Importing makes all API of the framework available in Swift.
Bonjour File Windows 10
Add the SharedPoint class definition to the file. Class definition starts with the key word class and is enclosed in curly brackets:
Add three properties to the class:
- netService – variable of NSNetService type – file sharing service on computer.
- type – variable of String type – file sharing protocol (SMB or AFP).
- ipAddress – variable of String type – computer's IP address in the network.
If app is launched right away, the following compilation error is invoked:
Class ‘SharedPoint' has no initializers.
This happens because Swift requires all class properties, the types of which are optional, to be initialized at class instantiation.
Add initializer with three named parameters to the class:
With this initializer you can create an instance of the SharedPoint class, passing named values to each of its parameters. Note that one cannot call the initializer without stating external parameters names as it will lead to error.
Add the Equatable protocol conformance to the SharedPoint class. It allows to define whether two values of one and the same type are identical. To do this, add the name of Equatable protocol to the SharedPoint class definition after the class name separated by a colon:
Then add the following function at global scope:
SharedBrowser class
Create a new Swift class named SharedBrowser. The class is intended to search computers with enabled file sharing in the network. To implement the search option, we'll use NSNetServiceBrowser class, which defines an interface for finding published services on a network using multicast DNS. Due to the possible delays associated with network traffic, NSNetServiceBrowser objects perform browsing asynchronously by registering with the default run loop. Browsing results are returned to your app through delegate methods. To handle results from an NSNetServiceBrowser object, you must assign it a delegate.
Thus, to perform search, do the following:
- Initialize an NSNetServiceBrowser instance and assign a delegate to the object.
- Start the search.
- Handle search results and other messages sent to delegate.
Each browser service performs one search operation at a time, so to perform multiple operations simultaneously, use several browser services. We'll need two browser services: the first one – to search the network for computers which use SMB protocol for files sharing, the second one – to search the network for computers which use AFP protocol.
From the NSNetServiceBrowserDelegate protocol declaration, we see that it requires a conforming type also to conform to the NSObjectProtocol protocol:
Since NSObject class conforms to the NSObjectProtocol protocol:
we'll make it superclass for the SharedBrowser class:
Add the following properties to SharedBrowser class:
- afpType – constant of String type – registration name of AFP service, which works over TCP protocol (see Bonjour Names for Existing Service Types);
- smbType – constant of String type – registration name of SMB service;
- smbBrowser – constant of NSNetServiceBrowser type – browser for computers in the network that provide access to files and folders over SMB protocol;
- afpBrowser – variable of NSNetServiceBrowser type – browser for computers in the network that provide access to files and folders over AFP protocol;
- serviceList – the list of detected services (array of objects of NSNetService type);
- sharedPointsList – array of objects of SharedPoint type, each of which can be used for connecting to a certain computer in the network with enabled sharing via the IP address over the supported sharing protocol.
To add initializer init() to SharedBrowser class, it's necessary to override the designated NSObject initializer of its superclass via the key word override.
Initializer first assigns values to class properties, then it delegates up to init() initializer of the NSObject class. Then the fully initialized object assigns itself as delegate for objects afpBrowser and smbBrowser.
Please pay attention that calling the superclass's initializer init() precedes the setting of delegates. Otherwise we'll get the compilation error:
‘self' used before super.init call.
Delegate of the NSNetServiceBrowser object must conform to the NSNetServiceBrowserDelegate protocol. To avoid the following compilation error:
Type ‘SharedBrowser' does not conform to protocol 'NSNetServiceBrowserDelegate',
Download office mac free trial. add the name of the NSNetServiceBrowserDelegate protocol to the SharedBrowser class header after the name of its superclass, separated by comma:
The NSNetServiceBrowserDelegate protocol defines the optional methods implemented by delegates of NSNetServiceBrowser objects.
Let's study some of them:
netServiceBrowser(_,didFindService:,moreComing:) tells the delegate the browser found a service.
Method declaration:
netService parameter of NSNetService type is the detected service.
moreServicesComing parameter of Bool type shows whether the browser expects any other additional services.
Add implementation of this method to SharedBrowser class. When calling this method, the detected service is to be added to serviceList array, and when the service search is over, update() method, which description is provided below, is to be called:
netServiceBrowser(_,didRemoveService:,moreComing:) method tells the delegate a service has disappeared or has become unavailable.
Method declaration:
When calling this method, the service which became unavailable is to be deleted from serviceList array, and its corresponding elements should be deleted from sharedPointsList array. When the service search is over, update() method should be called.
find method returns the first index where `value` appears in `domain` or `nil` if `value` is not found. Elements must conform to the Equatable protocol.
filter method returns an array containing the elements of the receiver for which a provided closure indicates a match. In our case it's an array of elements of [SharedPoint] type, in which netService variable refers to the service which became unavailable.
netServiceBrowser(_,didNotSearch:) method tells the delegate that the search failed.
Method declaration:
When calling this method, the message containing the error code will be displayed in console. One can get the error code via the NSNetServiceErrorCode key in errorDict dictionary.
In update() method, for each service of serviceList array, we'll set object of SharedBrowser class as delegate, and start the process of service address resolution.
NSNetService object delegate must conform to the NSNetServiceDelegate protocol. The name of the NSNetServiceDelegate protocol should be added to the SharedBrowser class header.
The NSNetServiceDelegate protocol defines the optional methods implemented by delegates of the NSNetService objects.
We'll add this protocol conformance to the SharedBrowser class.
netServiceDidResolveAddress(_) method informs the delegate that the address for a given service was resolved.
Let's study in detail the method implementation:
addresses property of NSNetService class returns an array of objects of [AnyObject] type, each of which contains the sockaddr structure, which can be used to connect to socket:
When working with Cocoa APIs, it is common to receive an array with a type of [AnyObject], or 'an array of values of any object type'. This is because Objective-C does not have explicitly typed arrays. Though, in the most of cases you can be sure of the type of objects contained in such an array, basing on the information about the API provided by the array.
For example, in our case it is known that the type of objects in array returned by addresses property of NSNetService class is NSData.
Enumerate all elements of sender.addresses array (sender constant of NSNetService type is a service for which addresses are resolved) and perform the following:
1. Declare two variables: inetAddress of sockaddr_in! type and inetAddress6 of sockaddr_in6! type for IPv4 and IPv6 addresses correspondingly. Both variables are implicitly unwrapped optionals.
sockaddr_in structure describes the socket for work with TCP/IP protocols and has the following form:
The structure properties:
- sin_len – structure length;
- sin_family – address family (must be equal to AF_INET);
- sin_port – port of IP addresses;
- sin_addr – IP address;
- sin_zero – padding.
For IPv6 address representation, sockaddr_in6 structure is used. sin6_family property stores address family (must be equal to AF_INET6), and sin6_addr property stores IPv6 address.
2. Declare inetAddressPointer variable and assign constant pointer to sockaddr_in structure to it.
bytes property of NSData class returns pointer of UnsafePointer<()> type (analog in Objective-C is const void *) to data of object of this class:
As it is known that the object contains sockaddr structure, we can cast returned value to UnsafePointer type.
3. Assign to inetAddress variable the value which is stored in inetAddressPointer pointer.
To get the object stored in UnsafePointer structure, we'll use its memory property:
4. Check address family. Note that during address family check implicit type casting is carried out (AF_INET – Int32 type, while the type of sin_family property of sockaddr_in structure is sa_family_t (alias for __uint8_t type). Omitting type casting leads to compilation error.
In case the address family is AF_INET (IPv4 address), no additional actions are required. Otherwise, check whether AF_INET6 (IPv6) is the address family. If it really is, then assign constant pointer to sockaddr_in6 structure to inetAddressPointer6 variable, the structure stored in this address to inetAddress6 variable, and nil to inetAddress variable.If not, assign nil to inetAddress variable.
5. Declare ipString variable of UnsafePointer? optional type. Question mark means that the variable value is optional, that is, the variable can contain constant pointer to Int8 type or can contain no values at all.
6. Declare ipStringBuffer variable of UnsafeMutablePointer type (pointer to Int8 type) and allocate memory via alloc method.
7. As there is a possibility that inetAddress variable became equal to nil, process it as optional variable to check whether it contains any value. If it does, declare addr variable and assign to it the address stored in sin_addr field of inetAddress structure. Then assign constant pointer to the string returned by inet_ntop function to ipString variable.
inet_ntop function converts a packed Internet address into the readable format. The function gets binary IP address and returns the pointer to the string which contains decimal format with dots and colons.
inet_ntop function declaration in Swift:
Function parameters:
- variable of Int32 type – address family (sin_family variable of inetAddress structure should be casted to Int32 type, that is Int32(inetAddress.sin_family));
- constant pointer of UnsafePointer type. Here's an extract from Apple's doc 'Using Swift with Cocoa and Objective-C. Interacting with C APIs':
When a function is declared as taking a UnsafePointer argument, it can accept the same operands as UnsafePointer for any type Type.
That's why, we can pass the value of UnsafePointer type to the function as the second argument, that is, constant pointer to in_addr structure which stores IP address.
- variable of UnsafeMutablePointer type – pointer to buffer;
- variable of socklen_t type – buffer size (cast INET6_ADDRSTRLEN constant of Int32 type to __uint32_t type (socklen_t type is alias for __uint32_t)).
Underscore (_) means that you don't have to indicate outer names for the parameters at function call.
In case inetAddress variable does not contain value, check whether inetAddress6 variable contains any value. If it does, declare addr variable and assign to it the address which is stored in sin6_addr field of inetAddress6 structure. Then assign constant pointer to the string returned by inet_ntop function to ipString variable.
8. Then check whether variable ipString contains value. If it does, declare ip variable and assign it the value returned by fromCString static method of String structure.
Since ipString variable is of UnsafePointer? optional type, one needs to force unpack it (exclamation mark after the variable name) before passing it to fromCString method. Casting UnsafePointer type to UnsafePointer type is not necessary, as CChar is alias for Int8.
If ip variable contains the value, declare ext variable of String type and assign to it the value depending on the service type. Note that in switch-case construction there is no break operator in case-blocks, as, unlike switches in C and Objective-C, switches in Swift do not pass to the next block by default after executing code inside the corresponding case-block.
Add to sharedPointsList array the object of SharedPoint class, which is created via initializer with three named parameters. As ip variable is of String? type, that is, optional String, we'll force unpack it.
After that, send to the notification center of the process the notification that a new point with file sharing was found.
9. Since UnsafePointer structure does not provide automatic memory control, free the memory allocated to ipStringBuffer variable.
Add implementation of one more method of the NSNetServiceDelegate protocol to the SharedBrowser class.
netServiceDidResolveAddress(_,didNotResolve:) method informs the delegate that an error occurred during resolution of a given service.
Method declaration:
When calling this method, a message with code error will be shown in console. One can get an error code via NSNetServiceErrorCode key from errorDict dictionary.
All is left is to add to the class SharedBrowser method which is intended to launch network search for computers with enabled file sharing. In our case, there are three search variants available:
- search for computers with file sharing via AFP protocol;
- search for computers with file sharing via SMB protocol;
- search for computers with file sharing via AFP and SMB protocols.
To differentiate the three search variants, we'll create ServiceType enumeration with three members, each of which corresponds to the definite search variant:
Note that, unlike in C and Objective-C, integer values cannot be assigned by default to enumeration members at their creation in Swift. Afp, Smb and AfpAndSmb members in ServiceType enumeration are not equal to 0, 1 and 2 correspondingly, but they are actual values.
Add the following method to SharedBrowser class:
The method accepts the only parameter of ServiceType type, describing the search variant. In switch-case construction, the short form of access to ServiceType enumeration members is used, like .Afp, not ServiceType.Afp, since it is known that the variable type is ServiceType.
searchForServicesOfType(_,inDomain:) method of NSNetServiceBrowser class launches the search for services of the definite type in the specified domain.
Method declaration:
Passing an empty string to the method as the second parameter lets the browser search for services among registration domains used by default.
Add to SharedBrowser class the last method which is intended to stop the current search and clear its results:
Now let's start the creation of the user interface.
User Interface Creation
1. Choose MainMenu.xib file in project browser to display SharingBrowser window.
2. Display the Utilities in the right corner of the window. To do this, click the rightmost button of View switch on the toolbar. Omniplan pro 3 3 9 3.
3. Display the main window. To do this, click Window icon in Editor.
4. Open the Object Library. The Object Library can be found in the lower part of the Utilities.
5. Drag Push Button, Table View, two Check Boxes and Box to the window from the Object Library.
6. Place the objects as shown on the picture and name them appropriately.
7. In Attributes inspector tab, choose Content Mode in Cell Based, and set the number of Columns to 4.
8. For each column of the table set the name in Attributes inspector tab. In Identity inspector tab, set the Restoration ID: Name and name correspondingly for the 1rst column, Domain and domain – for the 2nd column, Type and type – for the 3rd column, IP and ip – for the 4th column.
9. Create outlet for each element in AppDelegate class. To do this, display Assistant editor panel by clicking Editor switch button on the toolbar. Make sure that Assistant editor displays AppDelegate.swift file. If not, choose AppDelegate.swift file from Top Level Objects menu in the upper part of the panel.
For each element, do the following:
- Click Control key and hold it, while dragging element to AppDelegate.swift file;
- Release Control key and Assistant editor will display connection setup window;
- Choose Outlet from Connection menu;
- Input the name and click Connect button.
Interface Builder will add the outlet declaration to the class. Outlets in Swift are declared via the @IBOutlet key word.
The following strings should appear in AppDelegate class:
10. Set AppDelegate object as the data source for the table. To do this, click Control key and hold it, while dragging the pointer from the table towards AppDelegate object in the objects hierarchy.
Choose dataSource property.
11. Add action for Search button. Click Control key and hold it, while dragging Search button into AppDelegate.swift file. Release Control key, choose Action from Connection menu, input searchBtnAction as the action name. Interface Builder will add the stub for the method to AppDelegate class:
Code Adding
Go to AppDelegate.swift file.
Add sharedBrowser property and initialize it by the instance of the SharedBrowser class:
Add the following code to the button click handler of Search button:
First, declare searchType variable of ServiceType? type, that is, optional ServiceType type. Make sure that afpCheckBox checkbox is ticked. If it is, make sure that smbCheckBox checkbox is ticked as well. Chrome os download. If yes, assign .AfpAndSmb value to searchType, otherwise – .Afp.
If afpCheckBox checkbox is not ticked, make sure that smbCheckBox checkbox is ticked. If yes, assign .Smb value to searchType. Check whether searchType variable contains value. If it does, stop the previous search, clear its results and srart the new search. If not, display the warning window.
To populate the table with content programmatically, NSTableViewDataSource protocol should be realized. Since AppDelegate class was set in Interface Builder as the data source for the table, add the NSTableViewDataSource protocol conformance to it:
numberOfRowsInTableView(_:) method of NSTableViewDataSource protocol returns the number of records provided to NSTableView object by the data source.
Method declaration:
Add this method implementation to the AppDelegate class:
tableView(_:objectValueForTableColumn:row:) method of the NSTableViewDataSource protocol is called by the table to return the data of the object, related to the indicated row and column.
Method declaration:
Add this method implementation to the AppDelegate class:
In applicationDidFinishLaunching(_:) method set the AppDelegate class itself as the target for the sharedPointsTable, and set the selector for its doubleAction property. At double-click on the table's row, mountToShare(sender:) method will be called, which is indented to create the connection to file sharing service, corresponding to the row. In this very method subscribe to NewSharedDetected notification, upon receipt of which reloadSharedPointsTable(), method will be called, which is indented to refresh sharedPointsTable table data.
Add reloadSharedPointsTable() method implementation:
mountToShare(sender:) function:
Connection to file sharing service is processed via script on AppleScript. There's NSAppleScript class to create and perform scripts in Cocoa applications.
NSAppleScript object creation is performed via init?(source: String) initializer, to which a string with script code is passed.
Application Launch
1. Launch the app by clicking Run button.
2. Click Search button.
3. In the table, you will see computers' names with enabled file sharing, the protocol type used for sharing and their IP addresses.
4. To connect to a definite computer with enabled file sharing, double-click the row corresponding to it. You'll see the window:
Bonjour File Sharing Windows 10
Congrats! That's your first Swift app!
Download the archive with sources of the above described sample here: SharingBrowser
If you just want to see Wide-Area Bonjour browsing in action without having to install the Preference Pane / Control Panel, you can do that by entering an appropriate DNS search domain. To advertise your own services and have more control over the settings, follow the instructions here to install the Preference Pane (Mac) or Control Panel (Windows).
The Bonjour Preference Pane provides a user interface for using the wide-area aspects of Bonjour. The customer releases of Mac OS X Tiger and Bonjour for Windows include full wide-area Bonjour functionality for developers to utilize in their applications, but no user interface. The Bonjour Preference Pane allows you to set system-wide defaults that will cause standard unmodified Bonjour applications to browse for and/or register network services in wide-area Bonjour domains, rather than only on the local link.
The Bonjour Preference Pane is not a supported Apple product, and is not guaranteed to work in future releases of Mac OS X. It is a tool to allow users and developers to experiment with the capabilities of wide-area Bonjour. As a tool for experimenting and exploring all the possibilities, it has more options than a real shipping product would have, because it sacrifices some simplicity in order to provide maximum flexibility.
Installation
Mac OS X v10.5 Leopard and later
Download the Bonjour Preference Pane and drag it into/Library/PreferencePanes/
. Mac OS X v10.4 Tiger
Download the Bonjour Preference Pane for Tiger and drag it into/Library/PreferencePanes/
. Windows XP and 2000/2003
Download and install the Bonjour SDK for Windows, which includes the Control Panel.Source Code
The Bonjour Preference Pane and Control Panel source code is available from Apple's Darwin CVS repository.Using the Bonjour Preference Pane
The Bonjour Preference Pane has three tabs: Hostname, Registration, and Browsing.
Hostname
If you have a Dynamic DNS hostname [RFC 2136] assigned to you by your Bonjour server admin, who ensures that everyone's hostname is unique, (or if you run your own DNS server with Dynamic Update), you can enter it here and click Apply. The hostname must be fully qualified, so don't enter a hostname like 'steve', enter a hostname like 'steve.bonjour.example.com'. The yellow dot will turn green to confirm a successful registration with the DNS server, or red if a permanent error occurs, such as trying to update a name that you're not authorized to update. If the dot remains yellow, that indicates lack of network connectivity, for example, your Ethernet cable may not be plugged in. Connect the cable or otherwise establish connectivity and the dot should turn green or red as appropriate. Note that currently, hostname registrations will not work if your computer is behind a NAT, unless the NAT gateway is one that supports NAT Port Mapping Protocol [NAT-PMP], such as AirPort Extreme or AirPort Express. Some NAT gateways that support the UPnP Home Gateway Protocol may also work.
If the DNS server requires credentials to authenticate secure updates [RFC 3007], click 'Password.' and enter the key name and key data given to you by the DNS operator. The key name is most often the name of your Bonjour domain, for example, 'bonjour.example.com' (i.e. your fully qualified host name with the first label removed). The key data or 'password' is most often a random-looking string of characters, for example, 'CnMMp/xdDomQZ4TelKIHeQ'.
Registration
Call of duty black ops 1st mission. If you'd like to advertise services on your machine that are discoverable anywhere on the Internet (or at least anywhere within your company network, depending on firewall policies and the like), click the checkbox and enter a Bonjour domain in the Registration panel. Bonjour services such as Personal File Sharing, Personal Web Sharing, Remote Login, FTP Sharing, SubEthaEdit shared documents, iPhoto sharing, etc, will be visible from anywhere in the world (sorry, iTunes and iChat currently only support link-local Bonjour). As with Dynamic DNS hostnames, if your computer is behind a NAT gateway, wide-area service registrations will only work if the gateway supports either NAT Port Mapping Protocol [NAT-PMP] or the UPnP Home Gateway Protocol.
Just like your hostname registration, your Bonjour name server may require you to enter a key name and password before it will accept service registrations. Simply click 'Password.' and enter the key name and key data given to you by the Bonjour name server operator. The key name is most often just your Bonjour domain, for example, 'bonjour.example.com'. The key data or 'password' is most often a random-looking string of characters, for example, 'CnMMp/xdDomQZ4TelKIHeQ'.
Browsing
If you don't want to advertise services on your machine, but do want to discover services advertised by others, enter a Bonjour browse domain in the Browsing panel.
For fun, try this on Mac OS X Tiger or later (or the equivalent steps on Windows using the Bonjour plugin for Internet Explorer):
- Open a Safari window.
- Press Cmd-Opt-B to bring up the bookmarks list.
- Click on the Bonjour icon.
- Now, with the Safari window still visible, go back to System Preferences.
- Click + and enter dns-sd.org as a browse domain.
- Click Apply and watch a bunch of new stuff instantly appear in the Safari Bonjour bookmarks list.
- Uncheck dns-sd.org and click Apply again, the new stuff instantly vanishes.