taylormade201
taylormade201

Reputation: 696

Programming Asynchronous Delegate methods in Swift

I have an application which talks to a device over a BLE UART connection. When data is received, a didReceiveData() delegate is called. Here I need determine who called the data and trigger the corresponding method (of another delegate). I was planning on creating a connections dictionary keyed by a connectionID string created when the connection is established, along with a selector to the callback (may not always be supplied).

class Connection: NSObject {
    var selectr: Selector
    var dataString: String?
    init(selectR:Selector,dString:String) {
        selectr = selectR
        dataString = dString
    }
}
connections[String:Connection]()
func sendData(dataString:String,callbackSelector:Selector){
  con = Connection(selectR: callbackSelector, dString:"")
  connections[cid] = con
}

... When calling:

    let sel = Selector(anotherDelegate?.didReceiveLocation(""))
    self.sendData("sendMe",Selector(anotherDelegate?.didReceiveLocation))

I get a few errors doing this, first a Type NSData does not conform to protocol StringLiteralConvertible. NSData referring to the argument of didReceiveLocation. The second is on the self.sendData line: Cannot invoke 'init' with an argument list of type (StringLiteralConvertible,Selector).

Does this approach make sense? How can I store the callback method of another delegate in a dictionary or other struct to make it accessible from the didReceiveData delegate method?

Upvotes: 1

Views: 1795

Answers (1)

David Berry
David Berry

Reputation: 41236

Selectors are so Objective-C... why not just use a closure?

To declare a dictionary with String as keys and functions that take a String as a parameter and return nothing:

connections = [String:String->()]()

To declare a function that takes a closure with a single String argument and no return value, use something like:

func sendData(dataString:String, callback:String->()) {
    ...
    connections[cid] = callback
    ...
}

To invoke that closure later, you can call it as a subroutine (in this case after performing a dictionary lookup, using optional chaining in case it hasn't been assigned yet):

func didReceiveData(...) {
    ...
    connections[cid]?(response)
}

Then to call the sendData routine declared above and pass in a closure using one of the several abbreviated syntaxes:

    self.sendData("sendMe") { response in
        ...
    }

Note that this is actually short-hand for:

    self.sendData("sendMe", callback:{ response:String in
        ...
    })

Note that this mechanism can also be used by an Objective-C caller, since closures map more or less directly to Objective-C blocks.

For more information on closures, their definition and invocation, I'd strongly recommend downloading the free Swift book from the iTunes book store.

Upvotes: 1

Related Questions