how can I make this Swift event handler boilerplate more concise? - swift

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})
}
}
If folks see an alternative implementation that has the same usability benefits for the event consumers, i'd love to see those too. Here are some of the other options I've thought of or tried:
implement a protocol defining the callbacks. Have event consumers implement the protocol and register that for callbacks. Negative: requires all methods to be implemented - forces too many no-op callbacks.
as above, but extend the protocol with default blank implementations. Negative: still requires the event consumer to implement an extra class for the callbacks. Also, lack of dynamic dispatch for protocol extensions means registering the protocol will force generated events to go to the no-op implementation instead of the real event handler.
set the consuming class as a delegate on the event producer. Negative: still have to implement no-op callbacks. I've only ever seen one delegate per event producer, and I need several. #Darko comments below are prompting me to consider this option further.

NSNotificationCenter
There are multiple solutions for your problem. One could be to simply use NSNotificationCenter instead of implementing your own event mechanism.
With NSNotificationCenter you can register for events:
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: #selector(MyClass.myMethod),
name: "com.mycompany.myEvent1",
object: userData)
And send events from anywhere:
let userData: [NSObject: AnyObject] = [anyObject: anyDataToSend]
NSNotificationCenter.defaultCenter.postNotificationName("com.mycompany.myEvent1", object: userData)
You will find plenty of tutorials on SO and Google regarding NSNotificationCenter.
Delegate with optional protocol
Another method would be to use the delegate pattern. Create a protocol and mark it as #objc, in this way protocol methods can be marked as "optional".
#objc protocol MyEventProtocol: class {
optional func foo() -> Bool
func bar()
}
Every object which conforms to this protocol now must implement bar() but can implement optionally foo().
class MyEventReceiver: MyEventProtocol {
func bar() { // do something }
}
Then your event sender class can do something like this:
class MyEventSender {
weak var delegate: MyEventProtocol? // the name "delegate" is just convention, you can use any other name
init(receiver: MyEventReceiver) {
self.delegate = receiver
}
func sendEventToReceiver() {
if let delegate = self.delegate {
delegate.func() // guaranteed by the protocol
delegate.foo?() // foo is marked as optional, so you have to check it with ?. If foo is implemented on the receiver it will be called, otherwise not.
}
}
}
That's the basic principle. In this way you can define one protocol for all possible events but the implementer of the protocol only have to implement the methods which are not marked as optional. They are "required".
Protocol extensions with default methods
A third method would be to create a protocol extension with default methods.
protocol MyEventProtocol: {
func bar()
}
extension MyEventProtocol {
func foo() -> Bool {
return true
}
}
Then the implementor of MyEventProtocol does not have to implement foo() because there is already an implementation. (it's a kind of a "faked" #objc protocol with optional methods) If you add some generic mechanism to this solution you could also prevent the hefty code duplication. (generics in Protocols are done with associatedtype in Swift 2.2, see other tutorials)

Related

What is a good design pattern approach for a somewhat dynamic dependency injection in Swift

Let's say there are three components and their respective dynamic dependencies:
struct Component1 {
let dependency1: Dependency1
func convertOwnDependenciesToDependency2() -> Dependency2
}
struct Component2 {
let dependency2: Dependency2
let dependency3: Dependency3
func convertOwnDependenciesToDependency4() -> Dependency4
}
struct Component3 {
let dependency2: Dependency2
let dependency4: Dependency4
func convertOwnDependenciesToDependency5() -> Dependency5
}
Each of those components can generate results which can then be used as dependencies of other components. I want to type-safely inject the generated dependencies into the components which rely on them.
I have several approaches which I already worked out but I feel like I am missing something obvious which would make this whole task way easier.
The naive approach:
let component1 = Component1(dependency1: Dependency1())
let dependency2 = component1.convertOwnDependenciesToDependency2()
let component2 = Component2(dependency2: dependency2, dependency3: Dependency3())
let dependency4 = component2.convertOwnDependenciesToDependency4()
let component3 = Component3(dependency2: dependency2, dependency4: dependency4)
let result = component3.convertOwnDependenciesToDependency5()
Now I know that you could just imperatively call each of the functions and simply use the constructor of each component to inject their dependencies. However this approach does not scale very well. In a real scenario there would be up to ten of those components and a lot of call sites where this approach would be used. Therefore it would be very cumbersome to update each of the call sites if for instance Component3 would require another dependency.
The "SwiftUI" approach:
protocol EnvironmentKey {
associatedtype Value
}
struct EnvironmentValues {
private var storage: [ObjectIdentifier: Any] = [:]
subscript<Key>(_ type: Key.Type) -> Key.Value where Key: EnvironmentKey {
get { return storage[ObjectIdentifier(type)] as! Key.Value }
set { storage[ObjectIdentifier(type)] = newValue as Any }
}
}
struct Component1 {
func convertOwnDependenciesToDependency2(values: EnvironmentValues) {
let dependency1 = values[Dependency1Key.self]
// do some stuff with dependency1
values[Dependency2Key.self] = newDependency
}
}
struct Component2 {
func convertOwnDependenciesToDependency4(values: EnvironmentValues) {
let dependency2 = values[Dependency2Key.self]
let dependency3 = values[Dependency3Key.self]
// do some stuff with dependency2 and dependency3
values[Dependency4Key.self] = newDependency
}
}
struct Component3 {
func convertOwnDependenciesToDependency5(values: EnvironmentValues) {
let dependency2 = values[Dependency2Key.self]
let dependency4 = values[Dependency4Key.self]
// do some stuff with dependency2 and dependency4
values[Dependency5Key.self] = newDependency
}
}
But what I dislike with this approach is that you first of all have no type-safety and have to either optionally unwrap the dependency and give back an optional dependency which feels odd since what should a component do if the dependency is nil? Or force unwrap the dependencies like I did. But then the next point would be that there is no guarantee whatsoever that Dependency3 is already in the environment at the call site of convertOwnDependenciesToDependency4. Therefore this approach somehow weakens the contract between the components and could make up for unnecessary bugs.
Now I know SwiftUI has a defaultValue in its EnvironmentKey protocol but in my scenario this does not make sense since for instance Dependency4 has no way to instantiate itself without data required from Dependency2 or Depedency3 and therefore no default value.
The event bus approach
enum Event {
case dependency1(Dependency1)
case dependency2(Dependency2)
case dependency3(Dependency3)
case dependency4(Dependency4)
case dependency5(Dependency5)
}
protocol EventHandler {
func handleEvent(event: Event)
}
struct EventBus {
func subscribe(handler: EventHandler)
func send(event: Event)
}
struct Component1: EventHandler {
let bus: EventBus
let dependency1: Dependency1?
init(bus: EventBus) {
self.bus = bus
self.bus.subscribe(handler: self)
}
func handleEvent(event: Event) {
switch event {
case let .dependency1(dependency1): self.dependency1 = dependency1
}
if hasAllReceivedAllDependencies { generateDependency2() }
}
func generateDependency2() {
bus.send(newDependency)
}
}
struct Component2: EventHandler {
let dependency2: Dependency2?
let dependency3: Dependency3?
init(bus: EventBus) {
self.bus = bus
self.bus.subscribe(handler: self)
}
func handleEvent(event: Event) {
switch event {
case let .dependency2(dependency2): self.dependency2 = dependency2
case let .dependency3(dependency3): self.dependency3 = dependency3
}
if hasAllReceivedAllDependencies { generateDependency4() }
}
func generateDependency4() {
bus.send(newDependency)
}
}
struct Component3: EventHandler {
let dependency2: Dependency2?
let dependency4: Dependency4?
init(bus: EventBus) {
self.bus = bus
self.bus.subscribe(handler: self)
}
func handleEvent(event: Event) {
switch event {
case let .dependency2(dependency2): self.dependency2 = dependency2
case let .dependency4(dependency4): self.dependency4 = dependency4
}
if hasAllReceivedAllDependencies { generateDependency5() }
}
func generateDependency5() {
bus.send(newDependency)
}
}
I think in terms of type-safety and "dynamism" this approach would be a good fit. However to check if all dependencies were received already to start up the internal processes feels like a hack somehow. It feels like I am misusing this pattern in some form. Furthermore I think this approach may be able to "deadlock" if some dependency event was not sent and is therefore hard to debug where it got stuck. And again I would have to force unwrap the optionals in generateDependencyX but since this function would only get called if all optionals have a real value it seems safe to me.
I also took a look at some other design patterns (like chain-of-responsibility) but I couldn't really figure out how to model this pattern to my use-case.
My dream would be to somehow model a given design pattern as a result builder in the end so it would look something like:
FinalComponent {
Component1()
Component2()
Component3()
}
And in my opinion result builders would be possible with the "SwiftUI" and the event bus approach but they have the already described drawbacks. Again maybe I am missing an obvious design pattern which is already tailored to this situation or I am just modeling the problem in a wrong way. Maybe someone has a suggestion.

Subclass Type as closure parameter

Usecase
I have a superclass (FirebaseObject) with subclasses for most data items in my Firebase (ex: RecipeItem, User). I made a function in the superclass that automatically updates the data that is in the subclass, now I am trying to make a function with closures that get called when the object is updated.
Code
class FirebaseObject {
private var closures: [((FirebaseObject) -> Void)] = []
public func didChange(completion: #escaping (((FirebaseObject) -> Void))) {
// Save closures for future updates to object
closures.append(completion)
// Activate closure with the current object
completion(self)
}
//...
}
This calls the closure with the initial object and saves it for later updates. In my Firebase observer I can now activate all the closures after the data is updated by calling:
self.closures.forEach { $0(self) }
To add these closures that listen for object changes I need to do:
let recipeObject = RecipeItem(data)
recipeObject.didChange { newFirebaseObject in
// Need to set Type even though recipeObject was already RecipeItem
// this will never fail
if let newRecipeObject = newFirebaseObject as? RecipeItem {
// Do something with newRecipeObject
}
}
Question
Is there a way to have the completion handler return the type of the subclass so I don't have to do as? Subclass even though it won't ever fail? I tried to do this with generic type but I can't figure it out and I am not sure if this is the correct solution.
I would like to keep most code in the FirebaseObject class so I don't need to add a lot of code when creating a new subclass.
Edit
Based on this article I tried to add the type when creating a subclass:
class RecipeItem: FirebaseObject<RecipeItem> {
//...
}
class FirebaseObject<ItemType> {
private var handlers: [((ItemType) -> Void)] = []
public func didChange(completion: #escaping (((ItemType) -> Void))) {
//...
This compiles but it crashes as soon as RecipeItem is initialised. I also tried
class RecipeItem: FirebaseObject<RecipeItem.Type> {
//...
}
But this gives an interesting compiler error when I try to access RecipeItem data in didChange closure:
Instance member 'title' cannot be used on type 'RecipeItem'
Ok, so I've been working on this for a day and I have found a way to do it using the method in this answer for the didChange and initObserver functions and taking inspiration from this way of saving data in extensions.
First off, all the functions that need to use the type of the subclass are moved to a protocol.
protocol FirebaseObjectType {}
extension FirebaseObjectType where Self: FirebaseObject {
private func initObserver(at ref: DatabaseReference) {
//...
}
mutating func didChange(completion: #escaping (((Self) -> Void))) {
if observer == nil {
// init Firebase observer here so there will be no Firebase
// observer running when you don't check for changes of the
// object, and so the Firebase call uses the type of whatever
// FirebaseObject this function is called on eg:
// RecipeItem.didChange returns RecipeItem
// and NOT:
// RecipeItem.didChange returns FirebaseObject
initObserver(at: ref)
}
if closureWrapper == nil {
// init closureWrapper here instead of in init() so it uses
// the class this function is called on instead of FirebaseObject
closureWrapper = ClosureWrapper<Self>()
}
// Save closure for future updates to object
closures.append(completion)
// Activate closure with current object
completion(self)
}
}
To save the closures I now use a wrapper class so I can do type checking on that. In FirebaseObject:
class ClosureWrapper<T> {
var array: [((T) -> Void)]
init() {
array = []
}
}
fileprivate var closureWrapper: AnyObject?
Now I can get the closures with the right type in FirebaseObjectType protocol:
private var closures: [((Self) -> Void)] {
get {
let closureWrapper = self.closureWrapper as? ClosureWrapper<Self>
return closureWrapper?.array ?? []
}
set {
if let closureWrapper = closureWrapper as? ClosureWrapper<Self> {
closureWrapper.array = newValue
}
}
}
I can now use didChange on a FirebaseObject subclass without checking its type every time.
var recipe = RecipeItem(data)
recipe.didChange { newRecipe in
// Do something with newRecipe
}

Swift protocol for shared functionality

I have a number of objects, and each need to be locked until purchased.
Each of these objects (NSManaged) have a productName String and isPurchased Bool.
I wrote a function isLocked() which uses there properties as well an external check in a singleton.
How can this be combined into a protocol so that the protocol contains the isLocked function implementation and the objects can just adhere to the protocol and then call isLocked when needed?
If im not mistaken this can be achieved with Default implementations in swift.
protocol Locakable {
var productName: String { get }
var isPurchased: Bool { get }
func lock()
func unlock()
}
extension Locakable {
func isLocked() {
if isPurchased {
//do something
//lock it
unlock()
} else {
lock()
}
}
}
For more info on what mixin or default implementation is then chek out this wiki page https://en.wikipedia.org/wiki/Mixin
However, note that isLocked() is dispatched statically. Comment if something is unclear.
You can also abstract the idea of locking and unlocking and write a default implementation on the protocol extension if possible. Or provide a customisation point like so.
class Item: Locakable {
var productName = "ItemName"
var isPurchased = false
init () {
isLocked()
}
func lock() {
}
func unlock() {
}
}

Why can't closures close over instance members

I really liked Sulthan's answer (in Anonymous class in swift)
which describes building an object which conforms to a protocol but whose class is hidden inside a closure. This would be nice for building singletons and not polluting the name-space with classes like Recorder1, Recorder2...
However, when I try to do anything useful with it, I fail because the closure will not close over the outer class' instance members inside the inner class.
protocol EventListener {
func handleEvent(event: Int) -> ()
}
class Recorder {
static var events = [Int]() // static is forced
var listener: EventListener = {
class R : EventListener {
func handleEvent(event: Int) {
events.append(event)
print("Recorded: \(event)")
}
}//
return R()
}()
}// Recorder
class Distributor {
var listeners = [EventListener]()
func register(listener: EventListener){
listeners.append(listener)
}
func distribute(event: Int){
for listener in listeners {
listener.handleEvent(event)
}
}
}
var d = Distributor()
var r1 = Recorder()
var r2 = Recorder()
d.register(r1.listener)
d.register(r2.listener)
d.distribute(10)
print(Recorder.events) // [10, 10] Same event recorded twice.
The above compiles and runs. But I want events in Recorder to be an instance member so that each Recorder has its own record. Removing static throws the compiler error: instance member 'events' cannot be used.
I have tried defining an instance func record(event) in Recorder for handleEvent(event) to call, but I get the same error.
Marius's answer (in Instance member cannot be used on type | Closures) suggests you can't access instance members while properties are being defined so I also tried to compute listener later like this.
class Recorder {
var events = [Int]()
var listener: EventListener {
class R : EventListener {
func handleEvent(event: Int) {
events.append(event) // error: can't access events
print("Recorded: \(event)")
}
}
return R()
}
}// Recorder
But the compiler says it can't access the the outer self.
Closures seem pretty impotent if they can't access outer selfs. In Java, you can get to outer selfs with something like Recorder.self.events. And Recorder.self. might only be necessary if there are name clashes(?)
Is Swift designed to be this way or what am I missing?
How would you write it so Recorder gives Distributor an object that can do nothing but receive handleEvent messages?
Thanks so much.
I don't believe that there is a way to close over events in a partially init'ed Recorder. However you can create a two stage initialization process for Recorder. Stage one creates the Recorder object with a default, empty listener. Stage two creates a proper closure over events and assigns it to listener.
This implementation does not have an EventListener protocol. I am sure there is a simple way to do that but for nested classes it seems overkill. So instead of Distributor containing an array of objects, containing closures. We are letting Distributor contain the array of closures directly.
Except for the additional call to initialize, your API does not change.
typealias EventListener = (Int) -> ()
class Distributor {
var listeners = [EventListener]()
func register(listener: EventListener){
listeners.append(listener)
}
func distribute(event: Int){
for listener in listeners {
listener(event)
}
}
}
class Recorder {
var events = [Int]()
var listener: EventListener = {
(event: Int) in
fatalError("Listener not initialized")
}
func initialize() {
listener = {
(event: Int) in
self.events.append(event)
print("Recorded: \(event)")
}
}
}
var d = Distributor()
// 1st stage of Recorder instantiation
var r1 = Recorder()
var r2 = Recorder()
// 2nd stage
// r1.listener can now have a closure that includes an init'ed events
r1.initialize()
r2.initialize()
d.register(r1.listener)
d.register(r2.listener)
d.distribute(10)
print(r1.events)
print(r2.events)
Using Price Ringo's 2 stage initialisation, and having Recorder pass self into the inner class, I have gotten the class hiding I was looking for. It is a little more verbose than I was hoping but has the advantage of making the life-cycle of Recorder's instance members clear. For example it avoids copy-vs-reference confusion surrounding events. self also makes it easy to pass data the other way: adjustment.
Price Ringo's solution is more elegant and appropriate to this specific problem. This solution is more general and will work with any existing protocol. In general, I am trying to incorporate protocol-oriented patterns into my code. This solution creates objects which have no type other than the protocol they conform to.
protocol EventListener {
func handleEvent(event: Int) -> ()
}
class Recorder {
var events = [Int]() // example of data out
let adjustment: Int // example of data in
var listener: EventListener = {
class __: EventListener {
func handleEvent(event: Int) { }
}
return __()
}()
init(adjustment: Int){
self.adjustment = adjustment
initializeListener()
}
private func initializeListener() {
listener = {
class __: EventListener {
unowned var recorder: Recorder
init(recorder: Recorder){
self.recorder = recorder
}
func handleEvent(event: Int) {
let adjustedEvent =
recorder.adjustment * event
recorder.events.append(adjustedEvent)
// print("Recorded: \(adjustedEvent)")
}
}
return __(recorder: self)
}()
}
}// Recorder
class Distributor {
var listeners = [EventListener]()
func addListener(listener: EventListener){
listeners.append(listener)
}
func distributeEvent(event: Int){
for listener in listeners {
listener.handleEvent(event)
}
}
}
var d = Distributor()
var r1 = Recorder(adjustment: 2)
var r2 = Recorder(adjustment: 3)
d.addListener(r1.listener)
d.addListener(r2.listener)
d.distributeEvent(10)
print(r1.events) // [20]
print(r2.events) // [30]
If you are still reading, I have a question. I have made instance member __>recorder unowned to avoid retention. Do I also need to make Recorder>listener unowned?

Swift: how to support "template method" design pattern (since Swift doesn't have protected)?

Traditionally in the "template method" pattern, a base class implements some algorithms and defers to derived classes for specific behavior. This works well in languages like C++/C#/Java because you can use "protected" on those methods to hide them from the callers but keep it visible for derived classes. For example, in the GoF book, you have something like:
class Application
{
void CreateDocument() { ..., this->DoCreateDocument() }
protected void DoCreateDocument() { } // override for custom behavior
}
This keeps the public interface for Application clean. In Swift, because you cannot use protected, the public interface is not clean. I do not want users of Application to see DoCreateDocument.
So I'm trying another method, which instead of using methods for DoCreateDocument, I'm trying to define a closure and use "functor" patterns.
class Application
{
typealias ActionFunc = () -> ()
private let doCreateDocument : ActionFunc
init(a : ActionFunc) { self.doCreateDocument = a }
func CreateDocument() {
self.doCreateDocument()
}
}
So this class looks good - the public interface is clean. However, it's impossible to actually use this.
The obvious approach is using a derived class:
class DoApplication : Application
{
init() {
super.init(
a : {
// This would work, but you cannot use self here!
self. // anything with self. is an error
})
}
}
The problem with this approach is that in the initializer, you cannot pass a closure to super.init that uses self. I get the error self used before super.init.
That basically makes it useless because you cannot access any state variables.
However, if you don't initialize doCreateDocument in the init, you need to expose a setter of some sorts - again, the lack of protected means that the setter is on the public API. Yuck.
So is there any way to cleanly implement the template pattern that keeps the interface clean?
I know this is an ugly hack, but it works.
class DoApplication: Application {
init() {
var doCreateDocument: (Application.ActionFunc)!
super.init(a: { doCreateDocument() })
doCreateDocument = { [unowned self] in
self.foo()
}
}
func foo() {
println("DoApplication.foo");
}
}
Alternatively, you can pass self to doCreateDocument:
class Application {
typealias ActionFunc = (Application) -> ()
private let doCreateDocument : ActionFunc
init(a : ActionFunc) { self.doCreateDocument = a }
func CreateDocument() {
self.doCreateDocument(self)
}
}
class DoApplication : Application {
init() {
super.init(a : { _self in
let _self = _self as! DoApplication
_self.foo()
})
}
func foo() {
println("DoApplication.foo");
}
}