I've seen a pattern in Java that lets you implement a subset of a list of callbacks, in a type-safe way, inline with the class that uses the callbacks:
registerHandlers(new ClassWithNoOpMethods() {
@override
public void onFooEvent(FooEvent event) { ... }
@override
public void onBarEvent(BarEvent event) { ... }
}
All nice and type-safe. I'd like to do the same thing in Swift, but some googling didn't turn up any (IMHO) elegant solutions. So I came up with this:
let registrar = EventSource.getEventRegistrar()
registrar.onFooEvent = { event in doSomethingFoo(event) }
registrar.onBarEvent = { event in doSomethingBar(event) }
...
EventSource.removeEventCallbacks(registrar)
This works fine for consuming the events - its just the subset of events I'm interested in, it lets me define the code inline, etc etc.
However, when I actually implemented this, I got a lot of repeated, boilerplate, non-DRY code. It offends me, but I can't figure out a better way to implement the scheme shown above. I'd like to appeal to the Swift gods on StackOverflow to show me a more concise way to implement this.
Here is what it looks like now:
public class Phone {
...phone stuff...
public class PhoneEventRegistrar {
let phone : Phone
init(phone : Phone) {
self.phone = phone
}
public typealias OnErrorCallback = (PhoneErrorType, String) -> Void
private var onErrorValue : OnErrorCallback?
public var onError : OnErrorCallback {
get { return onErrorValue != nil ? onErrorValue! : {_,_ in} }
set {
assert(onErrorValue == nil, "onError cannot be set twice")
onErrorValue = newValue
}
}
func invokeErrorCallback(type : PhoneErrorType, message : String) {
if let onErrorValue = onErrorValue {
onErrorValue(type, message)
}
}
public typealias OnCallStateChangeCallback = (CallState) -> Void
private var onCallStateChangeValue : OnCallStateChangeCallback?
public var onCallStateChange : OnCallStateChangeCallback {
get { return onCallStateChangeValue != nil ? onCallStateChangeValue! : {_ in} }
set {
assert(onCallStateChangeValue == nil, "onCallStateChange cannot be set twice")
onCallStateChangeValue = newValue
}
}
func invokeCallStateChangeCallback(state : CallState) {
if let onCallStateChangeValue = onCallStateChangeValue {
onCallStateChangeValue(state)
}
}
// and the mostly-similar code shown twice above is repeated for
// each possible callback
}
func invokeErrorCallbacks(type : PhoneErrorType, message : String) {
objc_sync_enter(self)
defer { objc_sync_exit(self) }
registrars.forEach({$0.invokeErrorCallback(type, message: message)})
}
func invokeCallStateChangeCallbacks(state : CallState) {
objc_sync_enter(self)
defer { objc_sync_exit(self) }
registrars.forEach({$0.invokeCallStateChangeCallback(state)})
}
// again, the mostly similar block of code shown twice above is
// repeated for each possible callback
private var registrars : [PhoneEventRegistrar] = []
public func getPhoneEventRegistrar() -> PhoneEventRegistrar {
objc_sync_enter(self)
defer { objc_sync_exit(self) }
let registrar = PhoneEventRegistrar(phone: self)
registrars.append(registrar)
return registrar
}
public func removeRegistrarCallbacks(registrar : PhoneEventRegistrar) {
objc_sync_enter(self)
defer { objc_sync_exit(self) }
assert(registrars.contains({$0 === registrar}), "cannot remove callbacks, no matching PhoneEventRegistrar found")
registrars = registrars.filter({$0 !== registrar})
}
}
Aucun commentaire:
Enregistrer un commentaire