Remotion 2.0 - A virtual office for your hybrid team, right on your desktop | Product Hunt

Is Swift Combine Dead?

Or does it just smell funny?

Apple introduced their Combine framework at the 2019 WWDC, alongside iOS 13, macOS 10.15, and, maybe most notably, SwiftUI. At the time, it seemed like there was quite a bit of excitement that Apple was giving us a first-party reactive programming solution.

A year later, in October 2020, the first Swift Concurrency roadmaps appeared in the Swift forums. And, at the following WWDC in 2021, we had the first full Swift Concurrency release…and no meaningful updates to Combine. Fast forward another year to WWDC 2022 and, yet again, no real Combine updates. During this whole saga, developers began (and continue to) speculate about whether Combine is dead, abandoned by Apple and replaced by Swift Concurrency.

So, is Combine dead? We don’t think so!

Are we using more and more Combine in our codebase? Yes!

Are we using Swift Concurrency as well? Yes!

Is this going to lead to architectural problems that we’ll regret? Hopefully not!

Let’s start with what Combine is built for (and good at): providing a system for responding to changes over time. It makes it easy to guarantee that you won’t “miss” an update by forgetting to notify a delegate somewhere. It is also great at manipulating that stream of data over time, doing things like debouncing, removing duplicates, combining or merging values, and more.

What is it not good at? Maybe its weakest point is how it integrates with other solutions — in particular, Combine doesn’t play particularly well with Swift Concurrency. But, by knowing when to use which tool (more on this later), it’s easy to avoid conflicts between the systems and have them play nicely together.

So, why don’t we think it’s dead and why do we think it’s safe to invest in? We aren’t concerned with the lack of updates to the framework for a few reasons:

  • It’s a mostly-feature-complete library. If you look at the standard API footprint of Rx-style libraries, Combine has this covered already. The standard Rx operators are there and have been there since its introduction.
  • It is heavily tied into SwiftUI, which Apple has made clear is the optimal way to build new apps (yes, this is controversial… that’s probably a topic for a different blog post).
  • It solves scenarios that Swift Concurrency doesn’t, such as having more than one subscriber to a stream of data, merging streams together, etc.

Responding to changes is not a new problem for programmers and there have been countless systems for dealing with this over time. In the Apple ecosystem, the longest-running and most notable is KVO. In terms of non-first-party solutions, a more recent framework is RxSwift (which our codebase also uses…and is also moving away from).

Our code, before a recent refactor, was riddled with KVO. In particular it was used to watch our reference-type model layer and respond to updates on the UI layer.

Here’s a sample of what that looked like:

So, why did we want to get rid of this? After all, it was (for the most part) working. One of the motivations was to have fewer reliances on the Objective-C runtime and the data types that it prefers. To use KVO, your models must be reference types, and, if they’re written in Swift, end up being annotated with lots of @objc. Swift tends to prefer value-type data structures and, while we aren’t doing a transition in the immediate future, this KVO reliance would be a roadblock. Also, while Remotion is mostly an AppKit app, as we add increasingly more SwiftUI, the NSObject and @objc annotations do nothing to help us in the world of SwiftUI. But, even if we don’t have struct-based models, having @Published Combine-y models are very usable in SwiftUI.

So, what does our code look like now?

Is it better? In fact, in some ways, it actually looks more complicated! But, it depends how you define it — the sample doesn’t show all of the @objc and NSObject and KVO callbacks that we got to remove. Overall, the PR to replace KVO with Combine in our app was +2000/-2400.  It’s always nice to have a reduction in lines of code! Also, note that .removeDuplicates line — now there’s also sorts of interesting things that we can do with additional operators in this chain (like throttle, for another example), which is a huge win.
We did hit a couple of gotchas along the way, the biggest being Combine’s use of willSet on @Published properties vs. KVO’s use of didSet.

To illustrate this, let’s consider the following scenario:

Note that in this scenario, in order for the UI to be in-sync with the model, model.isActive must be set before updateUI is called.

But, what if we do this?

Well, because @Published properties fire on willSet instead of didSet, by the time updateUI runs and checks the value of model.isActive, it may still be the *previous* value. What are the solutions to this? For one, you can pass in dependent state into the downstream functions, like this:

Another solution is to use a Combine operator to delay the sink until the next run loop:

The receive(on:) operator will end up waiting until the next run loop, even if we’re already on the main queue. Because the Publisher was fired on willSet, by the end of the previous run loop, the value will be set, and we’re safe to refer to model.isActive in updateUI again.

Does this all sound complicated? It is! Is it easy to fall into traps where your UI is out of sync with your model? Maybe. One of the dangers is that this relies on developers remembering to write code in certain ways and not a static check from the compiler. As we move more towards SwiftUI, we’re moving towards what we think is a more guaranteed model/UI state hookup:

Here, we don’t have to worry at all about willSet vs didSet — we know that if our model has a certain state, our view will reflect it.

Gotchas like the above aside, we’ve been moving forward to add Combine in more and more areas of our codebase (in fact, it’s become a running joke with one another engineer on the team — last week I found myself saying “You know, eventually we’ll have a bug that I won’t fix with Combine). In particular, we are replacing more and more infrastructure that broadcasts or consumes ‘state’ of any type. For example, did you happen to catch our post on modulating music volume during conversations? It’s built with Combine.

Another area, which we’ve just begun to experiment with, is to translate our nested-reference-type model structure into something that is easier to read from our UI layer, which, as I mentioned earlier, has increasingly more SwiftUI. Here’s an example of what some of our current model layer looks like (a very abstract representation):

This presents some interesting challenges to monitor this effectively, especially because some of the nested properties (like Conversation and ConversationMedia) are Optionals (These objects, by the way, before our transition away from KVO, would’ve been NSObjects with properties monitored with KVO). SwiftUI doesn’t do well with this structure. Even though everything is an ObservableObject, because nested ObservableObjects don’t work without manually hooking up objectWillChange, things like this don’t work out of the box:

But, if all of the models were structs instead of objects, it would work. So, what do we need to do to make that translation happen and guarantee that it’s updated? Something like this:

This is certainly not trivial to implement or keep up, but it allows us to get a simple looking state model for the UI. It also makes the separate components relatively testable! I’ll note that the above scenario, by the way, is something that Swift Concurrency absolutely does not solve — this is a problem that fits perfectly into Combine’s wheelhouse.

We don’t think Combine is going away. We think that it’s a fantastic tool to solve certain types of problems. Are you using Combine in your projects? We’d love to hear your experiences! Tweet @jnpdx or @remotionco with your thoughts.

The case for virtual coworking: build a connected remote culture.

Regularly coworking with your hybrid or remote team can help you build the social cohesion that makes work feel less like work.

Here are the biggest reasons we think virtual coworking is an effective way to create a close-knit remote culture:

1. It fosters casual conversations.

Building a connected remote culture is all about fostering 1:1 or small group organic conversations. Virtual coworking makes space for those conversations. When you spend time together outside of agenda-driven meetings, spontaneous chats naturally occur, as they would in a traditional office.

2. It's more inclusive than scheduled social events.

It can be draining for introverts to have to participate in scheduled, purely social conversations. Virtual coworking allows the team to spend time together and occasionally chat without having to constantly be "on," making it more inclusive for introverts and extroverts alike.

3. It's easy to say yes to.

Purely social events are important, but if your remote team is busy or on a tight deadline, it's tough to find the time for social chats without it feeling like an obligation. Coworking is much easier to get your distributed team onboard with because it doesn't take time away from getting work done.

4. It improves remote collaboration.

Coworking can lead to unblocking and shorter feedback loops and stronger remote collaboration. Quick questions get answered easily and in the moment, without a having to schedule a meeting or go back-and-forth in messages. Coworking also builds peer accountability.

5. It's scalable.

Coworking works for teams of all sizes and is a great way to scale your remote culture as your team grows. It's helpful to create opportunities for teammates from different functions to get to know one another.

6. It creates shared momentum.

Virtual coworking helps remote workers for the same reason you might get a membership at a traditional coworking space: the feeling of togetherness is motivating!

Get started with virtual coworking: choose the type most aligned with your priorities.

It takes intentionality to make virtual coworking feel natural and energizing enough to stick—it's not as simple as leaving a Zoom call open all day.

Here are a few of the ways we've set coworking up for our team. We recommend choosing one to start with. If it works, make it routine and experiment with other types from there.

Best practices for virtual coworking.

Keep group sizes small.

Limit your work sessions to 4-6 people to minimize distraction and help make introverted teammates comfortable chatting.

Signal boost coworking.

Set a norm of letting the entire team know when you're hopping into a coworking room or session.

Make it routine.

Once you've figured out what kind of coworking works for your team, make it a regular, opt-in event. Set up a recurring calendar event to do it at the same time each week to maximize the impact.

Set expectations ahead of time.

When you're first introducing coworking to your remote team, share what you're imagining in your calendar invite and at the top of work sessions to get everyone on the same page. For example:

Let's try virtual coworking! We'll work independently on our own projects with our cameras off, but we'll share virtual space and listen to music together — like we might work side-by-side at a physical office.

Listen to music together.

Play music while you work in a virtual room to create a shared environment and add a little bit of personality to your virtual coworking session.

Set up Coworking Rooms in Remotion.

Most of the above is doable with any video chat app or virtual office, but much easier with Remotion—which we designed with a lightweight, smooth coworking experience in mind. Remotion is the perfect virtual coworking platform—easily set up virtual rooms that your teammates can hop into for different styles of coworking.

While Remotion's virtual workspace is free to use with your remote team, if you're curious about joining a virtual coworking community built on our platform—check out Swift Remote Studio for iOS, Mac, and Swift developers.

Want to try coworking in Remotion? Get free access today .

We'd love to hear how coworking goes for you, or what practices you've found helpful on your team — let us know @remotionco on Twitter.

Want to try coworking in Remotion? Get free access today.