Within just a couple hours of posting yesterday, I had some new information to consider. I wanted to call attention to it before continuing on our RxSwift/Combine comparison.
New Documentation
Yesterday afternoon Apple released iOS 13 beta 2; with it came some new documentation. This new document, Receiving and Handling Events with Combine, is a brief overview of how one can, well, receive and handle events in Combine.
The introduction is good, and demonstrates how one can get a value out of a text field and store it in a custom model object. The documentation also demonstrates the use of operators to do some slightly more advanced modification of the stream in question.
Sample Code
Cutting to the end of the document, here’s the sample code Apple shared:
let sub = NotificationCenter.default
.publisher(for: NSControl.textDidChangeNotification, object: filterField)
.map( { ($0.object as! NSTextField).stringValue } )
.assign(to: \MyViewModel.filterString, on: myViewModel)
I have… a lot of problems with this.
I’m Notifying You I Don’t Like This
Most of my problems with this code are in the first two lines:
let sub = NotificationCenter.default
.publisher(for: NSControl.textDidChangeNotification, object: filterField)
NotificationCenter
is a sort of application (or even system)
bus, where lots of things can all drop data, or pick up pieces of
data that are flying by. It’s a sort of all-things-to-all-people
kind of solution, and that’s by design. There are lots of instances
where you may want to be able to figure out if, say, the keyboard
has just been shown or hidden. NotificationCenter
is a great way
to spread that message around within the system.
I find NotificationCenter
to be a bit of a code smell.
There are absolutely times where I use NotificationCenter
, and
in fact, there are times [like the keyboard notification above] that
NotificationCenter
is the best possible solution for a
problem. However, all too often I feel like using NotificationCenter
is the most convenient solution.
It’s extremely easy to drop something on the NotificationCenter
bus,
and to pick it up somewhere else on the other side of your app.
Furthermore, NotificationCenter
is “stringly” typed, which is
to say, it’s easy to make errors about what notification you’re trying
to post or listen for. Swift does its best to make this a bit better,
but ultimately it’s still NSString
under the hood.
An Aside about Key-Value Observation
A popular way to get notifications about things changing in different pieces of code is a technology that has been around for a long time in Apple platforms: key-value observation. Key-value observation is described by Apple as such:
Key-value observing is a mechanism that allows objects to be notified of changes to specified properties of other objects.
I also noticed, thanks to a tweet from Gui Rambo, that Apple has added bindings for KVO to Combine in this new beta. That means that a lot of my gripes about there being no equivalent to RxCocoa in Combine may have gone away. If I could use KVO, that would probably obviate much of the need for “CombineCocoa”, so to speak.
I got to working on a sample of my own that used KVO to get the value
out of a UITextField
and simply print()
it to the console:
let sub = self.textField.publisher(for: \UITextField.text)
.sink(receiveCompletion: { _ in
print("Completed")
}, receiveValue: {
print("Text field is currently \"\($0)\"")
})
Good to go, right?
I had forgotten a very inconvenient truth:
UIKit, by and large, is not KVO-compliant.
😭
That means without KVO support, my idea doesn’t work. My testing
confirmed it: my code never print()
ed anything as I entered
text in the field.
Thus, my fantasy of KVO eliminating much of the need for UIKit bindings was incredible, but short-lived.
Cancellation
The other problem I have with Combine is that it still isn’t terribly
clear to me where/how Cancellable
objects are supposed to be
cleaned up. It seems that we’re supposed to keep a copy of these as
instance variables. I don’t recall having read any official documentation
about cleanup though.
(If you have, do let me know, please!)
In RxSwift, we had the awfully-named-but-ultimately-convenient
DisposeBag
. It’s trivial to create a CancelBag
in Combine, but
I’m still not 100% clear if that’s really the best approach.
Highs and Lows
All told, quite a lot was added to Combine in beta 2, and I am very excited to see what comes in future betas. Nonetheless, none of these new goodies have really swayed my opinions… yet.
In my next post, we’ll go ahead and cover how error handling works in RxSwift versus how it works in Combine, and the plusses and minuses of both approaches.