You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- Added `KeyedObservable` Protocol
- Added `KeyedObservableClass` Base Type
- Added `KeyedObservableThread` Base Type
- Added `KeyedObservableThreadSafeClass` Base Type
- Added Overloads for `addObserver`
- Added Overloads for `removeObserver`
- Added Macro Extension for `Observable` Protocol implementations to iterate Arrays of Multiples and, for each, invoke their Singular method counterparts
- Updated README.MD to reflect all above changes
- Added Discord link and button to README.MD
Collection of carefully-prepared Classes and Protocols designed to imbue your inheriting Object Types with efficient, protocol-driven Observer Pattern Behaviour.
20
+
As of version 1.1.0, this includes support for *Keyed Observers* (see usage examples below for details)
@@ -190,6 +194,181 @@ That's it! The Playground program will now simply print out the new Random Numbe
190
194
191
195
You can adopt this approach forany Observation-Based Thread Behaviour you require, because `ObservableThread` will always invoke the Observer callback methods in the execution context their own threads! This means that, for example, you can safely instantiate an Observer class on the UI Thread, while the code execution being observed resides in its own threads (one or many, per your requirements).
192
196
197
+
### Keyed Observation Pattern
198
+
As of version 1.1.0, you can now register and notify *Keyed Observers*.
199
+
200
+
This functionality is an extension of the standard Observer Pattern, and is implemented in the following classes from which you can extend:
201
+
- `KeyedObservableClass<TKey:Hashable>` instead of `ObservableClass`
202
+
- `KeyedObservableThread<TKey:Hashable>` instead of `ObservableThread`
203
+
- `KeyedObservableThreadSafeClass<TKey:Hashable>` instead of `ObservableThreadSafeClass`
204
+
205
+
*Remember, Keyed Observation is an **extension** of the basic Observation Pattern, so any Keyed Observable is also inherently able to register and notify non-Keyed Observers*
206
+
207
+
You would use Keyed Observation whenever your Observers care about a specific context of change. A good example would be for a Model Repository, where an Observer may only care about changes to a specific Model contained in the Repository. In this scenario, you would used Keyed Observation to ensure the Observer is only being notified about changes corresponding to the given Key.
208
+
209
+
Key Types must always conform to the `Hashable` protocol, just as must anyKey Type used for a `Dictionary` collection.
210
+
211
+
Let's take a look at a basic usage example.
212
+
213
+
We shall provide a basic usage example to synchronize an Observer's internalDictionaryfor specific keys only with the values from the Observable's internalDictionary.
214
+
215
+
First, we would begin with an Observation Protocol:
The above Observation Protocol provides the method `onValueChanged` which takes the `key` (in this case a `String` value) and provides the corresponding `oldValue` and `newValue` values for that `key`.
222
+
Our *Observer* will implement `TestKeyedObservable` to provide an implementation for this function.
223
+
224
+
Now, let's define a simple *Keyed Observable* to house the master Dictionary we will be selectively-synchronizing with one or more *Observers*.
225
+
```swift
226
+
class TestKeyedObservableClass: KeyedObservableClass<String> {
The above classinherits from `KeyedObservableClass` and specializes the `TKey` generic to be a `String`. In other words, the Keys for this Observable must always be `String` values.
238
+
It includes a simple `String:String`dictionary (`String` key with a `String` value)
239
+
240
+
The `setValue` method will simply notify all observers using `withKeyedObservers` any time a specific `key` the Obsever(s) is(are) observing is updated, passing along the `oldValue` and `newValue` values. It will then update its internal Dictionary (`keyValues`) so that it always contains the latest value.
241
+
242
+
Note the use of `withKeyedObservers` instead of `withObservers`. You will use this syntax in your own Keyed Observables, changing only the declared Observer Protocol (`TestKeyedObservable` in this example) with the Observer Protocol representing your own observation methods.
243
+
244
+
Now that we have a Keyed Observable that will notify Observers each time the value of a key changes, let's define an Observer.
245
+
```swift
246
+
class TestKeyedObserverClass: TestKeyedObservable {
So, `TestKeyedObserverClass` is a simple class, implementing our `TestKeyedObservable` Observer Protocol.
255
+
For this example, we are going to presume that there are 2 pre-defined Keys with known initial values (there do not have to be... you can have as many keys as you wish)
256
+
257
+
You will notice that we initialized both the Observable and Observer classes to have identical `keyValues` dictionaries. This is solely for the sake of simplifying this example by ensuring there is always an `oldValue`. You don't need to do this in your own implementations.
258
+
259
+
So, now that we have the *Observable* and the *Observer* types, let's produce a simple bit of Playground code to tie it together.
260
+
```swift
261
+
let observable =TestKeyedObservableClass() // Creates our Observable
262
+
let observer = TestKeyedObserverClass // Creates a single Observer instance
263
+
```
264
+
At this point, we need to consider what Key or Keys our `observer` is going to Observe.
265
+
266
+
For example, we can Observe just one key:
267
+
```swift
268
+
observable.addKeyedObserver(for: "A", observer)
269
+
```
270
+
The above means that `observer` would only have its `onValueChanged` method invoked when the value of key *A*is modified in `observable`.
271
+
272
+
Likewise, if we only care about key *B*, we can do:
273
+
```swift
274
+
observable.addKeyedObserver(for: "B", observer)
275
+
```
276
+
277
+
If we care about *both* known keys, we can simply register them both:
The above would register `observer` with `observable` for every *key* contained in `observer`'s `keyValues` dictionary.
287
+
288
+
Ultimately, you can register the `observer` with the `observable` forany keys you want:
289
+
```swift
290
+
observable.addKeyedObserver(for: "Foo", observer)
291
+
```
292
+
293
+
Let's output the initial values of all of our keys before we invoke any code that would modify their values:
294
+
```swift
295
+
for (key, value) in observer.keyValues {
296
+
print("Key: '\(key)' has a value of '\(value)'")
297
+
}
298
+
```
299
+
This would output:
300
+
>Key: 'A' has a value of 'Hello'
301
+
>Key: 'B' has a value of 'Foo'
302
+
303
+
So, now that we can register the Keyed Observer with the Observer for whatever key or keys we wish, let's trigger the Observer Pattern in the `observer`:
304
+
```swift
305
+
observable.setValue(key: "A", "World")
306
+
```
307
+
The above will then update the value if*A* from "Hello" to "World".
308
+
309
+
If we repeat the following code:
310
+
```swift
311
+
for (key, value) in observer.keyValues {
312
+
print("Key: '\(key)' has a value of '\(value)'")
313
+
}
314
+
```
315
+
This would output:
316
+
>Key: 'A' has a value of 'World'
317
+
>Key: 'B' has a value of 'Foo'
318
+
319
+
Okay, so what if we change the value for key "C"? What will happen?
320
+
```swift
321
+
observable.setValue(key: "C", "Pong")
322
+
```
323
+
Now, if we repeat the following code:
324
+
```swift
325
+
for (key, value) in observer.keyValues {
326
+
print("Key: '\(key)' has a value of '\(value)'")
327
+
}
328
+
```
329
+
This would output:
330
+
>Key: 'A' has a value of 'World'
331
+
>Key: 'B' has a value of 'Foo'
332
+
333
+
Note that the `observer` was not notified about the change to the value of key *C*. This is because `observer` is not observing `observable` for changes to key *C*.
334
+
335
+
This is the value of Keyed Observation Pattern. Put simply: not all Observations are meaningful to all Observers. So, as you have now seen, Keyed Observeration enables our *Observers* to be notified specifically of changes relevant to that *Observer*.
336
+
337
+
## Overloaded `addObserver`, `removeObserver`, `addKeyedObserver`, and `removeKeyedObserver` methods
338
+
As of version 1.1.0, all useful combination overloads for the above-specified methods of `ObservableClass`, `ObservableThread`, `ObservableThreadSafeClass`, `KeyedObservableClass`, `KeyedObservableThread`, and `KeyedObservableThreadSafeClass` have been provided to streamline the adding and removal of *Observers* with/from an *Observable*.
339
+
340
+
### Adding a single *Observer* to an *Observable*
341
+
```swift
342
+
observable.addObserver(myObserver)
343
+
```
344
+
345
+
### Adding multiple *Observers* to an *Observable*
`removeObserver` and `removeKeyedObserver` also provide the same overloads as shown above.
371
+
193
372
## Additional Useful Hints
194
373
There are a few additional useful things you should know about this Package.
195
374
### A single *Observable* can invoke `withObservers` forany number of *Observer Protocols*
@@ -223,3 +402,7 @@ Any number of *Observer Protocols* can be marshalled by any of our *Observable*
223
402
## License
224
403
225
404
`Observable` is available under the MIT license. See the [LICENSE file](./LICENSE) for more info.
405
+
406
+
## Join us on Discord
407
+
408
+
If you require additional support, or would like to discuss `Observable`, Swift, or any other topics related to Flowduino, you can [join us on Discord](https://discord.com/invite/GdZZKFTQ2A).
0 commit comments