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

Preparing for SwiftUI in AppKit Code by Using NSStackViews Instead of Nib Files

TL;DR: If you’re excited about SwiftUI but still need to write AppKit, use NSStackViews. They’ll bring some similar benefits, and make it easier to migrate to SwiftUI later.

The nib file revolution is over

For macOS and iOS projects, the future is certainly SwiftUI. The SwiftUI approach to interface layout using code is an efficient, easy-to-understand representation of internal state. But what if you are working with a legacy code base, and aren’t ready to fully commit to SwiftUI?

As a long-time macOS/iOS developer, I’ve been using “nib” files for years. Decades! These data representations of a user interface were revolutionary at the time: Not only could you “WYSIWYG” lay out your interface visually, you could connect your code with the interface elements bidirectionally, avoiding a ton of glue code required in other interface-building paradigms. Along the way, Apple also gave us Storyboard files, with additional advantages such as transitions and navigation.

However, as the years have gone by, many disadvantages to using nib files have cropped up. Adaptive UIs means that pixel-based layouts were are longer relevant in many cases. Unlike the traditional springs-and-struts, layout constraints are tedious to specify and get just right. Merge conflicts in a multi-author team happen more than we’d like. And in a complex project with many nested custom views and view controllers, boilerplate glue code has become more and more necessary.

Pulling SwiftUI-like benefits into AppKit

My Remotion dock

Our native Mac app, Remotion, started out being written in AppKit before SwiftUI came on the scene. Of course, we recognize that SwiftUI is where we all should be moving — as Paul Hudson said, "SwiftUI is the future. Not the distant future, the imminent future.”

@twostraws on Twitter: UIKit will be important for years, but be under no delusions: SwiftUI is the future. Not the distant future, the imminent future – I use it for all my new apps and others are too, and when iOS 14 ships we're likely to see a huge expansion making it great for many more projects.

Some benefits of SwiftUI over legacy AppKit (and UIKit) building techniques include:

  • Easier to adapt to varying screen/window sizes
  • Not having to deal with merge conflicts in nib/storyboard files
  • A terse, easy-to-understand representation of layout and internal state

Though we will be gradually implementing some pieces of Remotion in SwiftUI, we can still make headway toward that future by building our interfaces in a way that’s closer to SwiftUI than to legacy, nib/storyboard-based interfaces.

Stacks and stacks of stacks

If you’ve looked into SwiftUI, you’ll notice that the HStack and VStack constructs are ubiquitous. There’s a reason for that: many user interfaces can be decomposed into nested groups of horizontal or vertical “stacks.” Thinking of a layout in terms of nested stacks is also a great way to make sure that a layout is adaptive to changing container sizes, such as the variety of iPhone screen sizes or resizable Mac window sizes.

For instance, we could decompose the main window of Apple’s Calendar application into a series of nested vertical and horizontal stacks.

When not to use stack views

Remotion's preferences, implemented with nibs—not stack views

Of course, some kinds of windows and views are still great candidates for specifying using a nib or a storyboard. Static, unchanging layouts such as preference panes or inspector panels, or custom alerts and dialogs are great for nibs; our App uses them in these cases. Complex, multi-level interfaces such as assistants and master-detail navigation can be built up with storyboards. (Our onboarding window is ideal for this technique.)

However much in the Remotion application is a dynamic layout, consisting of a one- or two-dimensional collection of interface elements that can’t really be specified in a graphical manner because the contents aren’t determined at implementation time. For these, we need to use one of Apple’s other container classes. For some, like our call window that shows a matrix of circular videos of participants in a call, the NSCollectionView is the best approach. In other cases, such as our lists of users and rooms, the NSTableView is the best way to present the data.

But for mixed-bag situations, such as our inspectors for users and rooms, we’ve found that the best way to build up the interface is with nested stack views.

We’ve found stack views are best implemented programmatically

While it’s possible to build a nib file with stack views, we’ve avoided doing this for two main reasons:

  • The contents of the stack view often aren’t known until run-time. For example, different views are presented depending on whether a user depicted is the user of the app or one of their teammates, whether they are online or offline, whether they are currently talking to another teammate, etc.
  • We have a lot of custom views that require their own custom view controllers, and it’s not possible to embed a view controller with associated view within a nib. We found ourselves creating empty container NSView objects in the nib, and then writing glue code to insert a view controller and view into that container. That ended up being quite a mess!
As an example, here is an inspector for one of our rooms, as exploded by Xcode’s “Debug View Hierarchy” tool. Notice how many stack views are used to compose the layout.

How to programmatically write view controllers with stack views

In order to break out of the pattern of loading a view from a nib file and a view controller, the easiest way is to override loadView() and set the value of the view controller’s view yourself. Be sure not to call super.loadView() since that will attempt to load a nib file.

Note: We’ve found through trial and error that setting an NSStackView or NSCollectionView to be view controller’s view can result in some errors when the view was deallocated, so we always add an empty NSView and add our stack view or collection view nested inside of this.

Because we are creating a lot of stack views in code, we’ve created a new convenience constructor so we can specify typical parameters quickly. It’s based loosely on the built-in init(views: [NSView]) which returns a horizontal stack view with the given views in the “leading” gravity area, and has translatesAutoresizingMaskIntoConstraints set to false. In our constructor, everything has a default value so it can be left unspecified.

This allows us to specify a stack view with very little code! For instance:

Looking ahead to SwiftUI

Although this code looks nothing like SwiftUI and migrating will still be a major investment, we get some similar benefits: Code is broken down into small components. Merge conflicts are rare and easy to resolve. And the UI already uses the helpful conceptual layout of nested horizontal stack and vertical stacks—so we won’t need to rethink the view hierarchy when it’s time to migrate.

Of course, SwiftUI still has a number of advantages. In areas where we are starting to migrate our legacy stackview-based layouts to SwiftUI, the amount of setup and layout code is greatly reduced and it’s much easier to conceptualize how data flows to build up the interface.

How is your team approaching adopting SwiftUI? Would love to hear your thoughts and feedback at dan at remotion dot com.

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 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 an 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. 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 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 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. Quick questions get answered easily and in the moment, without a having to schedule a meeting or go back-and-forth in messages.

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.

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 coworking sessions to 4-6 people to keep things from getting distracting 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 team, share what you're imagining in your calendar invite and at the top of each session 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 space and listen to music together — like we might work side-by-side at the office.

Listen to music together.

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

Set up Coworking Rooms in Remotion.

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

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, our virtual office? Get free access today.

Cowork like
everyone's together.