Last active
May 15, 2023 19:06
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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!" | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
It's 99% ChatGPT but I'm modifying to make more sense for a human (me)