Reusing view components with ease in SwiftUI


Building components in SwiftUI feels productive; declarative, high-level, and concise code, with instant previews. As you keep building a large enough codebase you also realize the power of component reuse.

The Now Playing Indicator🔗

Muziqi has a now playing indicator that shows if a song or an album in a list is currently playing. It looks like this:

At first, the indicator showed up in several Lists that contained SongRows: an album's detail view, playlist's detail view, the all songs list, and a few more. SongRow was being heavily reused. But a list or a cell being reused is a common pattern in any UI framework.

Concise reuse🔗

Recently I decided to reuse the indicator as part of the button that presents the full visualizer view, to indicate that this is not just any button, but a portal to an exciting, dynamic view! This took me just a single line (alright, it did include the if statement, so three lines):

Button { 
} label: {
    // ...
    if currentlyPlaing {
        NowPlayingIndicator()
    }
}

And just like that, I have the view in a completely new context:

Building the NowPlayingIndicator was fairly easy with a bunch of dynamic Shapes. But then writing these three lines to get to reuse all of that in a completely new context, felt like magic. I am also super happy with how the dependencies are set up, thanks to EnvironmentObject.

Dependencies🔗

There is a lot of audio processing shenanigans underneath. A VisualizerDataProcesser installs a tap on AVAudioPlayerNode, and is computing frequencies and loudness as playback continues. In UIKit land, trying to pass this information to disparate parts of the app is rough; you can either use a singleton or set up dependency injection that makes your Swift code look like Java. With SwiftUI, EnvironmentObject makes passing the object a matter of attaching .environmentObject(visualizerDataProcessor) to a root view. Materializing the object in NowPlayingIndicator is also a simple matter of @EnvironmentObject var vizProcessor: VisualizerDataProcesser.




SwiftUI is not without its share of pain, but building a sophisticated app in UIKit would have taken me at least twice as much time with much worse code.