Skip to content

Instantly share code, notes, and snippets.

@drosenstark
Last active May 15, 2023 19:06
Show Gist options
  • Save drosenstark/1ec16d58bb403b68fccce94edf29c704 to your computer and use it in GitHub Desktop.
Save drosenstark/1ec16d58bb403b68fccce94edf29c704 to your computer and use it in GitHub Desktop.
Prompt: I want a Swift object that will sit on an object (let's call that object A) which will allow other objects (B, C, D) to subscribe to changes in A on a certain variable.
import Foundation
/*
* Prompt:
* I want a Swift object that will sit on an object (let's call that object A) which will allow other objects (B, C, D) to subscribe to changes in A on a certain variable.
*
* So the Swift class/object that you're making allows subscribers to provide a block to be executed based on the changes in that variable. The variable could be a Bool, but if you have to box it as an NSNumber, that's fine.
*
* Response:
* You can achieve this behavior in Swift using a closure (or block in Objective-C terminology) for notifying changes. Here's a simple example with a Publisher class that wraps an object and allows other classes to subscribe for changes:
* In this example, A has a Bool property isReady that B subscribes to. When isReady changes, B gets notified. This pattern allows multiple classes to subscribe to changes in A's properties. If you need to box the Bool as NSNumber, you can modify the generic type T to NSNumber.
*/
/// Usage: put one of these on your class. For instance,
/// to publish the status of a `Bool` called `isReady` use
/// `let isReadyPublisher: Publisher<Bool>`
/// Or more simply (for String) use:
/// `let stringPublisher = Publisher("")`
class Publisher<T> {
typealias Subscriber = (T) -> Void
private var subscribers: [Subscriber] = []
var value: T {
didSet {
subscribers.forEach { $0(value) }
}
}
init(_ value: T) {
self.value = value
}
func subscribe(_ subscriber: @escaping Subscriber) {
subscribers.append(subscriber)
}
}
// MARK: - Example below here for Playground
class A {
let isReadyPublisher: Publisher<Bool>
var isReady: Bool {
didSet {
isReadyPublisher.value = isReady
}
}
init(isReady: Bool) {
self.isReady = isReady
self.isReadyPublisher = Publisher(isReady)
}
}
class B {
let id: String
init(id: String, subscribingTo a: A) {
self.id = id
a.isReadyPublisher.subscribe() { [weak self] isReady in
self?.doSomething(isReady: isReady)
}
}
private func doSomething(isReady: Bool) {
print("yeah \(id) is doing something \(isReady)")
}
}
class AString {
let stringPublisher = Publisher("")
var text: String = "" {
didSet {
stringPublisher.value = text
}
}
}
class BString {
let id: String
init(id: String, aString: AString) {
self.id = id
aString.stringPublisher.subscribe() { [weak self] text in
self?.doSomething(text: text)
}
}
private func doSomething(text: String) {
print("yeah \(id) got the text \(text)")
}
}
let a = A(isReady: false)
let b = B(id: "one", subscribingTo: a)
let c = B(id: "two", subscribingTo: a)
a.isReady = true
a.isReady = false
let aString = AString()
let bString = BString(id: "b-string1", aString: aString)
let bString2 = BString(id: "b-string2", aString: aString)
aString.text = "yes!"
aString.text = "no man!"
@drosenstark
Copy link
Author

It's 99% ChatGPT but I'm modifying to make more sense for a human (me)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment