Releases: codeface-io/SwiftObserver
Releases · codeface-io/SwiftObserver
Support Linux
Support Other Platforms
Fixes compile issues on iOS, tvOS and watchOS
Stabilize Dependency Version
Fixes the unstable version of the dependence on SwiftyToolz
SwiftObserver 7 – Refocus
This update lets go of some hard-earned features in favour of simplicity and shifts focus to native Swift features that have recently filled some gaps.
7.0.0 also expresses a renewed commitment to semantic versioning, in particular since SwiftObserver has moved to the Codeface GitHub organization.
Removed
Promise
has been removed as any Promise/Future implementation is obsolete with Swift's latest native concurrency features.- "One-shot" observations have been removed, as their primary purpose was to enable
Promise
. FreeObserver
and "global" observations have been removed, since they undermined the memory management concept without much benefit- Any value type-specific extensions of
Var<Value>
have been removed, as the property wrapper now fulfills their purpose. - Cocoapods support has been dropped.
Changed
- Protocol
Observable
has been renamed toObservableObject
so the new property wrapper could be conveniently namedObservable
and because the protocol is actually a class protocol. - Author filters
from
andnotFrom
require non-optionalAnyAuthor
.
Improved
- Variable values don't need to be
Codable
anymore.Var<Value>
remainsCodable
whereValue
is. - Many small refinements, more or less under the hood.
Added
- Property wrapper
Observable
allows to make anyvar: Value
observable by storing it in a wrappedVar<Value>
. - CombineObserver is a new library product of the SwiftObserver package, offering conversion of SwiftObserver's
ObservableObject
s into CombinePublisher
s.
Promise Mappers
New functions on Promise<Value>
that return a mapped new Promise<MappedValue>
:
map(...)
unwrap(default)
new()
Promises, Free Observers, Consistency and Flexibility
All that's new is also compatible with message authors and ad-hoc transform chains:
- Promises:
Promise<Value>
is aMessenger<Value>
with some conveniences for async returns- Promise composition functions
promise
,then
andand
- Free Observers:
- Class for adhoc observers
FreeObserver
- Global function
observe(...)
, andobserved(...)
on observables, both useFreeObserver.shared
- Global function
observeOnce(...)
, andobservedOnce(...)
on observables, both useFreeObserver
- Class for adhoc observers
BufferedObservable
:BufferedObservable
has been renamed toObservableCache
.ObservableCache
can be created via observable transformcache()
, which makesMessage
optional only when necessary.whenCached
retrieves non-optional message from caches that have optionalMessage
.
- Observables can stop their observations via
stopBeingObserved()
andstopBeingObserved(by: observer)
. Weak
is available as observable transformweak()
and is generally a regular transform object.- All transforms have mutable property
origin
which is the underlying observable whose messages get transformed. - It's possible for an observer to do multiple simultaneous observations of the same observable.
SwiftObserver 6
- Memory management is new:
- Before 6.0, memory leaks were technically impossible, because SwiftObserver still had a handle on dead observers, and you could flush them out when you wanted "to be sure". Now, dead observations are actually impossible and you don't need to worry about them.
- Observers now automatically clean up when they die, so a call of
stopObserving()
in deinit can now be omitted. Observers can still callstopObserving(observable)
andstopObserving()
if they want to manually end observations. Observer
now has one protocol requirement, which is typically implemented aslet connections = Connections()
. Theconnections
object keeps theObserver
s observations alive.- A few memory management functions were removed since they were overkill and are definitely unnecessary now.
- The new design scales better and should be more performant with ginormous amounts of observers and observables.
- The
Observable
protocol has become simpler.- The requirement
var latestMessage: Message {get}
is gone. - No more message duplication in messengers since the
latestMessage
requirement is limited toBufferedObservable
s. And so, switching buffering on or off on messengers is also no more concern. - Message buffering now happens exactly whenever it is really possible, that is whenever the observable is backed by an actual value (like variables are) and there is no filter involved in the observable. Filters annihilate random access pulling. The weirdness of a mapping having to ignore its filter in its implementation of
latestMessage
is gone. Observable
just requires oneMessenger
.- All observables are now implemented the same way and are thereby on equal footing. You could now easily reimplement
Var
and benefit from the order maintaining message queue ofMessenger
.
- The requirement
- Custom observables are simpler to implement:
- The protocol is the familiar
Observable
. No more separateCustomObservable
. - The
typealias Message = MyMessageType
can now be omitted. - The need to use optional message types to be able to implement
latestMessage
is gone.
- The protocol is the familiar
- Observers can optionally receive the author of a message via an alternative closure wherever they normally pass in a message handler, even in combined observations. And observables can optionally attach an author other than themselves to a message, if they want to.
- This major addition breaks no existing code and the author argument is only present when declared in the observer's message handler or the observable's
send
function. - This is hugely beneficial when observing shared mutable states like the repository / store pattern, really any storage abstraction, classic messengers (notifiers) and more.
- Most importantly, an observer can now ignore messages that he himself triggered, even when the trigger was indirect. This avoids redundant and unintended reactions.
- This major addition breaks no existing code and the author argument is only present when declared in the observer's message handler or the observable's
- The internals are better implemented and more readable.
- No forced unwraps for the unwrap transforms
- No weird function and filter compositions
- No more unnecessary indirection and redundance in adhoc observation transforms
- Former "Mappings" are now separated into the three simple composable transforms: map, filter and unwrap.
- The number of lines has only slightly increased because lots of complexity could be removed.
- The
ObservableObject
base class is gone.
- Other consistency improvements and features:
- An observer can now check whether it is observing an observable via
observer.isObserving(observable)
. - Stand-alone and ad hoc transforms now also include an
unwrap()
transform that requires no default message. - Message order is maintained for all observables, not just for variables. All observables use a message queue now.
- The source of transforms cannot be reset as it was the case for mappings. As a nice side effect, the question whether a mapping fires when its source is reset is no concern anymore.
Change<Value>
is more appropriately namedUpdate<Value>
since its propertiesold
andnew
can be equal.Update
isEquatable
when itsValue
isEquatable
, so messages of variables can be selected viaselect(Update(specificOldValue, specificNewValue))
or any specific value update you define.- The issue that certain Apple classes (like NSTextView) cannot directly be
Observable
because they can't be referenced weakly is gone. SwiftObserver now only references anObservable
'smessenger
weakly.
- An observer can now check whether it is observing an observable via
Consistent Variable Operators, SPM, Gitter
-
Removed
- Variable string assignment operator
- Variable number assignment operators
-
Changed
- Reworked Documentation
-
Added
- SPM Support
- Gitter chat
Performance, Consistency, Expressiveness, Safety
- Renamings:
- Some memory management functions have been renamed to be more consistent with the overall terminology.
- The type
Observable.Update
has been renamed toObservable.Message
.
- Non-optional generic types: Variables and messengers do no longer add implicit optionals to their generic value and message types. This makes it possible to create variables with non-optional values and the code is more explicit and consistent.
- You can still initialize variables and messengers without argument, when you choose to make their value or message type optional.
- Dedicated observer pools: All observables now maintain their own dedicated pool of observers. This improves many aspects:
- All observables get highest performance
- The whole API is more consistent
- Custom observable implementations are more expressive and customizable
- Memory management is easier as all observables, when they die, stop their observations
- Meaningful custom observables: Custom observables now adopt the
CustomObservable
protocol. And they provide aMessenger<Message>
instead of thelatestUpdate
.- As long as Swift can't infer the type, you'll also have to specify the associated
Message
type.
- As long as Swift can't infer the type, you'll also have to specify the associated
- Consistent variables:
- The operators on string- and number variables now work on all combinations of optional and non-optional generic and main types. For instance, string concatenation via
+
works on all pairs ofString
,String?
,Var<String>
,Var<String?>
,Var<String>?
andVar<String?>?
. - All variables with values of type
String
,Int
,Float
andDouble
also have a non-optional property that is named after the value type (string
,int
...).
- The operators on string- and number variables now work on all combinations of optional and non-optional generic and main types. For instance, string concatenation via
Messengers
- Added class
Messenger
- Added class
ObservabeObject
as a mostly internally used abstract base class for observables.Var
,Mapping
andMessenger
now derive fromObservableObject
.