Using NSNetService and NSNetServiceBrowser on a Swift application

jib picture jib · Jul 7, 2014 · Viewed 9.1k times · Source

I'd like to make an app that is able to discover and connect to peers on the local network, so i've decided to implement it in swift, using the bonjour framework.

However, i can't make Bonjour work using Swift, and I can't figure out why. Here is the code I use to test this service :

import Foundation

let BM_DOMAIN = "local"
let BM_TYPE = "_helloworld._tcp."
let BM_NAME = "hello"
let BM_PORT : CInt = 6543

/// Netservice
let nsns = NSNetService(domain: BM_DOMAIN, 
        type: BM_TYPE, name: BM_NAME, port: BM_PORT)
let nsnsdel = BMNSDelegate() //see bellow
nsns.delegate = nsnsdel
nsns.publish()

/// Net service browser.
let nsb = NSNetServiceBrowser()
let nsbdel = BMBrowserDelegate() //see bellow
nsb.delegate = nsbdel
nsb.searchForServicesOfType(BM_TYPE, inDomain: BM_DOMAIN)

println("press enter")
// this prevents the app from quitting instantly.
NSFileHandle.fileHandleWithStandardInput().availableData 

The delegates are glue code that simply prints every call to the console.

class BMNSDelegate : NSObject, NSNetServiceDelegate {
    func netServiceWillPublish(sender: NSNetService!) {
        println("netServiceWillPublish:sender");
    }
    // .....and so on for the 8 other methods.....
}

class BMBrowserDelegate : NSObject, NSNetServiceBrowserDelegate {
    func netServiceBrowserWillSearch(aNetServiceBrowser: NSNetServiceBrowser!){
        println("netServiceBrowserWillSearch")
    }
    // .....and so on for the 6 other methods.....
}

Here is the output of this sample code :

netServiceWillPublish:sender
netServiceBrowserWillSearch
press enter

If I use Bonjour browser, I can see that the service is correctly published. However the callbacks in both delegates are not called, beside the **WillPublish ones :-(

After intense debugging (and reading on stackoverflow), I can't figure why it does not work. Any ideas ?

(I'm using Mac OS X 10.9.3, and xcode 6.0 beta build 6A215l)

Answer

prh picture prh · Dec 3, 2014

Without your full code, it may be difficult to know, for sure, what your issue is. I suspect that you declared your variables/constants local to a function. When they went out of scope, the references to the service went out of scope. That's why you tried a blocking call requesting input from STDIN (to keep things stuck there). According to Apple documentation, netService and netServiceBrowser both implicitly associate with the default run loop, so you don't need to explicitly do that either. Explicitly associating with a run loop causes the program to get stuck, which is not what you want. This code creates the following output

netServiceWillPublish:<NSNetService 0x14522e00> local _helloworld._tcp. hello
netServiceBrowserWillSearch
netServiceDidPublish:<NSNetService 0x14522e00> local. _helloworld._tcp. hello
netServiceDidFindService

and without being blocked or in a run loop that prevents the program from proceeding normally. In AppDelegate.swift

class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

var nsns:NSNetService?
var nsnsdel:BMNSDelegate?
var nsb:NSNetServiceBrowser?
var nsbdel:BMBrowserDelegate?


func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    // Override point for customization after application launch.


    let BM_DOMAIN = "local"
    let BM_TYPE = "_helloworld._tcp."
    let BM_NAME = "hello"
    let BM_PORT : CInt = 6543

    /// Netservice
    nsns = NSNetService(domain: BM_DOMAIN,
        type: BM_TYPE, name: BM_NAME, port: BM_PORT)
    nsnsdel = BMNSDelegate() //see bellow
    nsns?.delegate = nsnsdel
    nsns?.publish()

    /// Net service browser.
    nsb = NSNetServiceBrowser()
    nsbdel = BMBrowserDelegate() //see bellow
    nsb?.delegate = nsbdel
    nsb?.searchForServicesOfType(BM_TYPE, inDomain: BM_DOMAIN)

    //println("press enter")
    // this prevents the app from quitting instantly.
    // NSRunLoop.currentRunLoop().run()
    // NSFileHandle.fileHandleWithStandardInput().availableData

    return true
}

and the delegate callbacks elsewhere...

class BMNSDelegate : NSObject, NSNetServiceDelegate {
func netServiceWillPublish(sender: NSNetService!) {
    println("netServiceWillPublish:\(sender)");
}

func netService(sender: NSNetService, didNotPublish errorDict: [NSObject : AnyObject]) {
    println("didNotPublish:\(sender)");
}

func netServiceDidPublish(sender: NSNetService) {
    println("netServiceDidPublish:\(sender)");
}

func netServiceWillResolve(sender: NSNetService) {
    println("netServiceWillResolve:\(sender)");
}

func netService(sender: NSNetService, didNotResolve errorDict: [NSObject : AnyObject]) {
    println("netServiceDidNotResolve:\(sender)");
}

func netServiceDidResolveAddress(sender: NSNetService) {
    println("netServiceDidResolve:\(sender)");
}

func netService(sender: NSNetService, didUpdateTXTRecordData data: NSData) {
    println("netServiceDidUpdateTXTRecordData:\(sender)");
}

func netServiceDidStop(sender: NSNetService) {
    println("netServiceDidStopService:\(sender)");
}

func netService(sender: NSNetService,
    didAcceptConnectionWithInputStream inputStream: NSInputStream,
    outputStream stream: NSOutputStream) {
        println("netServiceDidAcceptConnection:\(sender)");
}
}

class BMBrowserDelegate : NSObject, NSNetServiceBrowserDelegate {

func netServiceBrowser(netServiceBrowser: NSNetServiceBrowser,
                            didFindDomain domainName: String,
                            moreComing moreDomainsComing: Bool) {
    println("netServiceDidFindDomain")
}

func netServiceBrowser(netServiceBrowser: NSNetServiceBrowser,
                            didRemoveDomain domainName: String,
                            moreComing moreDomainsComing: Bool) {
    println("netServiceDidRemoveDomain")
}

func netServiceBrowser(netServiceBrowser: NSNetServiceBrowser,
    didFindService netService: NSNetService,
    moreComing moreServicesComing: Bool) {
        println("netServiceDidFindService")
}

func netServiceBrowser(netServiceBrowser: NSNetServiceBrowser,
    didRemoveService netService: NSNetService,
    moreComing moreServicesComing: Bool) {
        println("netServiceDidRemoveService")
}

func netServiceBrowserWillSearch(aNetServiceBrowser: NSNetServiceBrowser!){
    println("netServiceBrowserWillSearch")
}

func netServiceBrowser(netServiceBrowser: NSNetServiceBrowser,
    didNotSearch errorInfo: [NSObject : AnyObject]) {
        println("netServiceDidNotSearch")
}

func netServiceBrowserDidStopSearch(netServiceBrowser: NSNetServiceBrowser) {
    println("netServiceDidStopSearch")
}

}