Why can't closures close over instance members - swift

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?

Related

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 override all setters and getters of a subclass

I would like to override a setter/getter one time for but for all the properties for a class in swift
This my class. I want to call Realm each time I am adding a new value
class House : Object
{
var a:String
{
set {
do {
let realm = try Realm()
try realm.write {
a = newValue
}
}
catch {
}
}
}
var b:String
{
set {
do {
let realm = try Realm()
try realm.write {
b = newValue
}
}
catch {
}
}
}
}
There is no way in Swift how you can overwrite setters for all properties at once.
What you could generally do though is use:
overwritten setters per property
abstract computed properties wrapping low-level properties
intercept getters and setters by KVC accessor methods (e.g. is<Key>, get<Key>, …) and rely only on untyped dynamic KVC-based access via valueForKey(Path):, if you want to apply the decorated behavior (which you might want to avoid for this reason)
But Realm is using custom getters and setters under the hood, which are dynamically overwritten in an dynamically inserted intermediate class at runtime and relies on the presence of those. So the only approach, which is really feasible is having dynamic stored properties declared and adding for each of those an extra property, based on those.
var storedPropertyA: String = ""
var computedPropertyA: String {
get {
// add your extra behavior here
return storedPropertyA
}
set {
// add your extra behavior here
self.storedPropertyA = newValue
}
}
Beside that there is an alternative way of using the decorator pattern and decorate your whole object with extra behavior. In Swift, you could have your object and your decorator implement a common protocol, which defines your properties.
protocol HousingProperties {
var a: String { get set }
}
class House: HousingProperties {
var a: String = ""
}
class HouseDecorator: HousingProperties {
internal var house: House
init(house: House) { self.house = house }
var a: String {
// add your extra behavior here
self.house.a = a
}
}
Still I would NOT recommend to intercept property setters and getters for the purpose you intend here. Instead I'd advise to structure your application's architecture in a way, that allows you to be aware whether there is a write transaction or not and let the responsibility of making a write transaction in the hands of the code, which tries to modify objects.
Let me explain why:
Realm is using a multiversion concurrency control algorithm to manage persisted data and achieve thread-safety. This makes sure that different threads can read data at any point in time without having to read-lock and trying to synchronize these. Instead when a write is happening, all accessors are notified that there is new data and try to move on to the newest transaction. Until that has happened, all versions between the oldest data version, which is still used by a thread and the one written have to be retained. They can be first released when all threads advanced their commit pointers. If you do a lot of small transactions, you risk that your file size will blew up to unnecessary high values. For that reason, we recommend to batch write transactions to large changesets.
There is one hack to kind of attain what the poster is looking for, however possibly not advisable... Anyway; you can can create your own assignment operators that does whatever you want to do in realm prior to assigning the values
class MyType {
var myInt : Int = 0
var myString : String = ""
init(int: Int, string: String) {
myInt = int
myString = string
}
}
infix operator === {}
func ===<T>(lhs: T, rhs: T) -> T? {
Realm() // replace with whatever Realm()-specific stuff you want to do
return rhs
}
protocol MyAddableTypes {
func + (lhs: Self, rhs: Self) -> Self
}
extension String : MyAddableTypes {}
extension Int : MyAddableTypes {}
infix operator +== {} // ... -== similarily
func +==<T: MyAddableTypes>(lhs: T, rhs: T) -> T? {
Realm() // replace with whatever Realm()-specific stuff you want to do
return lhs+rhs
}
func Realm() {
// ...
print("Called realm")
}
var a = MyType(int: 1, string: "foo")
a.myString === "bar" // calls Realm(). After operation: a.myString = "bar"
a.myInt +== 1 // calls Realm(). After operation: a.myInt = 2
I thought I'd also mention that if you only want to do "Realm stuff" when a value is set (from your example: prior to setting a value, specifically), then the willSet method, used with stored properties, doesn't need to look so messy (nested closures), and personally, I would prefer this method
func Realm() {
print("Called realm")
}
class MyType {
// This isn't so messy, is it?
var myInt : Int = 0 { willSet { priorToSetValue(newValue) } }
var myString : String = "" { willSet { priorToSetValue(newValue) } }
var myDouble : Double = 0.0 { willSet { priorToSetValue(newValue) } }
private func priorToSetValue<T> (myVar: T) {
// replace with whatever Realm()-specific stuff you want to do,
// possibly including doing something with your new value
Realm()
}
init(int: Int, double: Double, string: String) {
myInt = int
myDouble = double
myString = string
}
}
var a = MyType(int: 1, double: 1.0, string: "foo")
a.myString = "bar"
print(a.myString) // calls Realm(). After operation: a.myString = "bar"
a.myInt += 1 // calls Realm(). After operation: a.myInt = 2

Anonymous class in swift

Is there an equivalent syntax or technique for Anonymous class in Swift?
Just for clarification Anonymous class in Java example here - http://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html
Thanks!
There is no equivalent syntax, as far as I know.
Regarding equivalent techniques, theoretically you could use closures and define structs and classes inside them. Sadly, I can't get this to work in a playground or project without making it crash. Most likely this isn't ready to be used in the current beta.
Something like...
protocol SomeProtocol {
func hello()
}
let closure : () -> () = {
class NotSoAnonymousClass : SomeProtocol {
func hello() {
println("Hello")
}
}
let object = NotSoAnonymousClass()
object.hello()
}
...currently outputs this error:
invalid linkage type for global declaration
%swift.full_heapmetadata* #_TMdCFIv4Test7closureFT_T_iU_FT_T_L_19NotSoAnonymousClass
LLVM ERROR: Broken module found, compilation aborted!
Command /Applications/Xcode6-Beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift failed with exit code 1
You can also create a basic empty class that acts like a bare protocol, and pass a closure to the init function that overrides anything you want, like this:
class EmptyClass {
var someFunc: () -> () = { }
init(overrides: EmptyClass -> EmptyClass) {
overrides(self)
}
}
// Now you initialize 'EmptyClass' with a closure that sets
// whatever variable properties you want to override:
let workingClass = EmptyClass { ec in
ec.someFunc = { println("It worked!") }
return ec
}
workingClass.someFunc() // Outputs: "It worked!"
It is not technically 'anonymous' but it works the same way. You are given an empty shell of a class, and then you fill it in or override whatever parameters you want when you initialize it with a closure.
It's basically the same, except instead of fulfilling the expectations of a protocol, it is overriding the properties of a class.
For example, Java listener/adapter pattern would be translated to Swift like this:
protocol EventListener {
func handleEvent(event: Int) -> ()
}
class Adapter : EventListener {
func handleEvent(event: Int) -> () {
}
}
var instance: EventListener = {
class NotSoAnonymous : Adapter {
override func handleEvent(event: Int) {
println("Event: \(event)")
}
}
return NotSoAnonymous()
}()
instance.handleEvent(10)
(Crashing the compiler on Beta 2)
The problem is, you always have to specify a name. I don't think Apple will ever introduce anonymous classes (and structs etc.) because it would be pretty difficult to come with a syntax that doesn't collide with the trailing closures.
Also in programming anonymous things are bad. Naming things help readers to understand the code.
No anonymous class syntax in Swift. But, you can create a class inside a class and class methods:
class ViewController: UIViewController {
class anonymousSwiftClass {
func add(number1:Int, number2:Int) -> Int {
return number1+number2;
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
class innerSwiftClass {
func sub(number1:Int, number2:Int) -> Int {
return number1-number2;
}
}
var inner = innerSwiftClass();
println(inner.sub(2, number2: 3));
var anonymous = anonymousSwiftClass();
println(anonymous.add(2, number2: 3));
}
}
This is what I ended up doing (Observer pattern). You can use closures in a similar way you would use anonymous classes in Java. With obvious limitations of course.
class Subject {
// array of closures
var observers: [() -> Void] = []
// #escaping says the closure will be called after the method returns
func register(observer: #escaping () -> Void) {
observers.append(observer)
}
func triggerEvent() {
observers.forEach { observer in
observer()
}
}
}
var subj = Subject()
// you can use a trailing closure
subj.register() {
print("observerd")
}
// or you can assign a closure to a variable so you can maybe use the reference to removeObserver() if you choose to implement that method
var namedObserver: () -> Void = {
print("named observer")
}
subj.register(observer: namedObserver)
subj.triggerEvent()
// output:
// observerd
// named observer
If you want to inline a click handler in Java style fashion, first define your closure as a variable in your button class:
var onButtonClick: () -> Void = {}
Then add a method to accept the closure as parameter and store it in the variable for later use:
func onClick(label: String, buttonClickHandler: #escaping () -> Void) {
button.label = label
onButtonClick = buttonClickHandler
}
Whenever the closure should be executed, call it in your button class:
onButtonClick()
And this is how to set the action that should occur on click:
newButton.onClick(label: "My button") { () in
print("button clicked")
}
You can also accept multiple parameters. For example, a toggle button may be handled like this:
var buttonClicked: (_ isOn: Bool) -> Void { set get }
Simply use a struct for defining the interface via function values and then anonymously implement it from a function, as is a very common way to write objects in JavaScript.
The function is only required for creating a private scope for the object returned.
import Foundation
struct Logger {
let info: (String) -> ()
let error: (String) -> ()
}
func createSimpleLogger() -> Logger {
var count = 0
func number() -> Int {
count += 1
return count
}
return Logger(
info: { message in
print("INFO - #\(number()) - \(message)")
},
error: { message in
print("ERROR - #\(number()) - \(message)")
}
)
}
let logger = createSimpleLogger()
logger.info("Example info log message")
logger.error("Example error log message")
Output:
INFO - #1 - Example info log message
ERROR - #2 - Example error log message

Add "for in" support to iterate over Swift custom classes

As we know, we can use the for..in loop to iterate across Arrays or Dictionaries. However, I would like to iterate over my own CustomClass like this:
for i in CustomClass {
someFunction(i)
}
What operations/protocols does CustomClass have to support for this to be possible?
Say you have a class "Cars" that you want to be able to iterate over using a for..in loop:
let cars = Cars()
for car in cars {
println(car.name)
}
The simplest way is to use AnyGenerator with the classes like this:
class Car {
var name : String
init(name : String) {
self.name = name
}
}
class Cars : SequenceType {
var carList : [Car] = []
func generate() -> AnyGenerator<Car> {
// keep the index of the next car in the iteration
var nextIndex = carList.count-1
// Construct a AnyGenerator<Car> instance, passing a closure that returns the next car in the iteration
return anyGenerator {
if (nextIndex < 0) {
return nil
}
return self.carList[nextIndex--]
}
}
}
To try a complete working sample add the two classes above and then try to use them like this, adding a couple of test items:
let cars = Cars()
cars.carList.append(Car(name: "Honda"))
cars.carList.append(Car(name: "Toyota"))
for car in cars {
println(car.name)
}
That's it, simple.
More info: http://lillylabs.no/2014/09/30/make-iterable-swift-collection-type-sequencetype
All of the above answers can be a little tricky. If you have an array in your class, which you want to iterate over (like in #Lee Whitney's answer), there's a much simpler way to implement it. You have the following class, CustomClass:
class CustomClass: SequenceType {
let array: [String]
init(array: [String]) {
self.array = array
}
func generate() -> IndexingGenerator<[String]> {
return array.generate()
}
}
Simple as that. Tested to work in the latest Xcode version (6.1 at the time of writing), and iOS 8.1.2. This code should be stable through future versions, though.
P.S. With generics, you can easily do your own Array replica by following this pattern, and only implement the methods which you want.
#Matt Gibson is correct. However, I would like to add more information for future reference.
From Advanced Swift:
This code:
for x in someSequence {
...
}
Is converted into this:
var __g = someSequence.generate()
while let x = __g.next() {
...
}
Therefore, one must adopt Sequence, which gives the class generate() and next(). Here are these protocols:
protocol Generator {
typealias Element
mutating func next() -> Element?
}
protocol Sequence {
typealias GeneratorType : Generator
func generate() -> GeneratorType
}
That would be the SequenceType protocol, and its related Generator protocol.
The SequenceType protocol says that the class must implement generate(), which returns something that conforms to the Generator protocol, which is the bit that does the actual work; the Generator protocol is the one with the all-important next() method.
There's an example of implementing it to allow for..in in the WWDC 2014 video "Advanced Swift" (in the generics example "A Simple Generic Stack", starting around slide 183.)
The basic info on which protocol to implement for for..in is in the Statements section of the documentation, which gives a brief overview of SequenceType and Generator
NOTE AnyGenerator and SequenceType are old types that do not exist in recent versions. You need to implement Sequence protocol now.
There are two ways to implement Sequence.
Conform to Sequence, IteratorProtocol at the same time by just implementing next() method. This approach is the simplest and there is an example in the headers. See Sequence.swift. Implementing both protocols at the same time could be non-realistic or not fulfill your needs. (It might prevent two different instances to be iterated at the same time, etc)
Conform to Sequence and return an object that implements IteratorProtocol. I think this is the most common case in real world classes (when things become a bit complicated, not Hello Worlds). There is also an example in the Sequence.swift
Below is an example of approach 2. An custom class (Linked List) which is iterable:
/// Linked List Node:
public class LinkedListNode <T> {
public internal(set) var value: T
public internal(set) var next: LinkedListNode<T>?
internal init(_ value: T) {
self.value = value
self.next = nil
}
}
/// Linked List with append method only.
public class LinkedList<T> {
public internal(set) var first: LinkedListNode<T>? = nil
public internal(set) var last: LinkedListNode<T>? = nil
/// Appends a new node.
public func append(_ value: T) {
if first == nil {
first = LinkedListNode(value)
last = first
} else {
last.next = LinkedListNode(value)
last = last.next
}
}
}
Finally the Sequence implementation
/// Sequence protocol adoption. It allows `for ... in` and a bunch of other methods too.
extension LinkedList: Sequence {
/// Iterator implementation
public class Iterator<T>: IteratorProtocol {
/// Maintain a ref to current element so next element can be reached
var cur: LinkedListNode<T>?
/// IteratorProtocol protocol requirement
public func next() -> T? {
let res = cur?.value
cur = cur?.next
return res
}
}
/// Sequence protocol requirement
public func makeIterator() -> Iterator<T> {
let g = LinkedList.Iterator()
g.cur = first
return g
}
}
Usage:
let linkedList = LinkedList<Int>()
linkedList.append(3)
linkedList.append(6)
linkedList.append(9)
linkedList.append(12)
for element in linkedList {
print(element)
}
let odds = linkedList.filter { return $0 % 2 == 0 }
print(odds)
The accepted answer is correct, and up until recently was the accepted way to address this. However, given the introduction of Protocol Extensions in Swift 2.0, rather than conformance to SequenceTypeand implementing func generate() -> GeneratorOf<Car> there is now an abstract base class that handles the implementation of this functionality for you called AnyGenerator<T> (see Apple docs) since GeneratorOf<T> no longer exists.
What this means is that you can simply subclass this abstract base class, and by doing do, inherit all of the functionality of the aforementioned protocol conformance:
class Cars: AnyGenerator<Car> {
private var carList = [Car]()
private var currentIndex:Int
...
}
One then need only override the next() method declared by the GeneratorType protocol (which AnyGenerator<T> also conforms to) in order to define the desired iteration behavior:
class Cars: AnyGenerator<Car> {
private var carList = [Car]()
private var currentIndex:Int
override func next() -> Car? {
if (currentIndex < self.carList.count) {
currentIndex++
return self.carList[currentIndex-1]
} else {
currentIndex = 0;
return nil
}
}
}

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

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)