Skip to content

Instantly share code, notes, and snippets.

@chrishulbert
Created July 23, 2015 10:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chrishulbert/5046d21000abadad45e6 to your computer and use it in GitHub Desktop.
Save chrishulbert/5046d21000abadad45e6 to your computer and use it in GitHub Desktop.
Example of a simple Swift alternative to KVO. Inspired by reactive cocoa's MutableProperty. Weakly-referenced subscribers and all.
import Foundation
// Simple observable property
// Careful: Not thread safe! You should use this only from the main thread, or modify to
// have locks.
class Property<T> {
private var _value: T
var value: T {
get { return _value }
set {
_value = newValue
tellSubscribers()
}
}
init(_ value: T) {
_value = value
}
var subscriptions = [Subscription<T>]()
func subscribe(subscriber: AnyObject, next: T -> Void) {
subscriptions.append(Subscription(subscriber: subscriber, next: next))
next
}
private func tellSubscribers() {
subscriptions = subscriptions.flatMap(tellAndFilterSubscription)
}
private func tellAndFilterSubscription(subscription: Subscription<T>) -> Subscription<T>? {
if subscription.subscriber != nil { // Subscriber is still around.
subscription.next(_value)
return subscription
} else { // Subscriber has gone, so cull this subscription.
return nil
}
}
}
struct Subscription<T> {
weak var subscriber: AnyObject?
let next: T -> Void
}
// Tests / examples of how to use it.
class Foo { // The one with the property.
let property = Property("Blah") // Here's how you declare a property.
}
class Bar { // The observer.
var string: String?
}
class Test {
var string: String?
func test() {
// Setup bar subscribed to foo's property.
let foo = Foo()
var bar: Bar? = Bar()
foo.property.subscribe(bar!) {
[weak bar] newValue in
bar?.string = newValue
}
// Example of how to subscribe.
foo.property.subscribe(self) {
[weak self] newValue in
self?.string = newValue
}
// Test it calls the subscriber.
foo.property.value = "Test"
print(bar!.string) // Should be "Test"
print(string) // Should also be "Test"
// Test it culls nilled subscribers.
print(foo.property.subscriptions.count) // Should be 1.
bar = nil
foo.property.value = "GiveItAChanceToCull"
print(foo.property.subscriptions.count) // Should be 0.
}
}
Test().test()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment