Live Music Archive

News

Live Music Archive 1.5

Nov 17, 2020

This is my first news post for Live Music Archive, but I hope to start writing one for each release to talk about the new features, some of the approaches I took, and the challenges I faced along the way.

Live Music Archive 1.5 brought with it three big new features:

Recording Selector

I have been thinking about the Recording Selector since I started building the app. I designed the concert selection interface with it in mind.

Version 1.0
Version 1.5

I knew this was going to be a challenging feature to implement so I didn't want to spend all of my time in version 1.0 getting this working. And boy was it challenging! Let me tell you a little bit about how I did it.

There are three major parts that make up the Recording Selector:

Concert Cell

Let's take a look at the Concert Cell to see how it works. There are two main modes for the Concert Cell: Concert Cell Mode and Recording Selection Mode.

Concert Cell Mode

When the Concert is zoomed out, we show basic information about the concert, up to 4 images, and a badge if there are more than 4 recordings. We display a lot of Concert Cells at a time so by only displaying the bare minimum, we are able to maintain top performance.

Recording Selection Mode

When you tap on a concert, we expand the concert full screen and show a lot more information about the concert. We add details about each recording including the number of downloads, star rating, taper information, and several icons including "More Info", "Recording Type" and "Stream-only".

You'll also notice the three little dots on the bottom. This entire view uses a UIPageController, which allows you to swipe between multiple pages of recordings.

There's a lot going on here!

Data Models

How do you define all of that complexity? With Data Models! Data models let you take your data and tailor it to a specific use case. In this case we're taking a concert and all of its recordings and picking out just the pieces of information we need to draw each cell. Here's a part of the data model for a Concert Cell:

public struct CellModel { public var artistName: String public var artistIdentifier: String public var concertIdentifier: String public var venue: String public var date: Date public var tiles: [TileModel] } public struct TileModel { public var identifier: String public var imageUrl: URL? public var taper: String? public var downloads: Int? public var rating: Double? public var favorite: Bool public var streamOnly: Bool public var recordingType: RecordingType? }

Challenges

The Concert Cell is a very complex feature and we are displaying thousands of them at a time so naturally it is going to be heavy strain on system resources. The first few versions of the Concert Cell were incredibly slow, to the point that I was worried that this feature would not be feasible at all. Even on modern devices, scrolling through a collection of concerts made the UI incredibly laggy.

I spent many days profiling every bit of the Concert Cell and tracking down what was causing the lag and it really came down to two main things:

I spent a lot of time rewriting and optimizing the UI and I'm so happy with the resulting performance! Even with the significant added complexity, it's actually faster than previous versions because of the time I spent on this.

Zooming View Controller

The ZoomingViewController handles all of the responsibilities related to the user tapping on a concert and zooming into the Recording Selector. It's used in several different locations in the app including the Artist, Discover, and Favorites Views so I made it reusable across different contexts.

One of its responsibilities is providing an animation lifecycle for the transition between zoomed in and zoomed out modes. There are 6 animation lifecycle events for each zooming in and out (there are ZoomOut versions of each of these):

These lifecycle events allow the Concert Cell (or any other view that uses this) to animate parts of itself at specific points in the animation process. For instance, before zooming in, we hide the cell details, then zoom the view in, and once the zooming-in is complete, we add the concert details to the view and fade them in. Similarly, zooming out, we hide the details first, remove them from the view, perform the zoom out animation, then fade the cell details in.

Challenges

There is some trickiness in taking the Concert Cell away from its Collection View and transferring it to another context. The basics are simple enough, but since a CollectionView can potentially be updated while the Recording Selector is open, you can run into data inconsistency issues and cause app crashes.

To prevent these types of crashes, I did two things:

Concert Cell Recycler

When developing for mobile devices, your resources are limited so you need to be conscientious about how much memory and processing power you use. To facilitate this, iOS makes extensive use of object recycling, which means instead of creating new objects every time you need them, you reuse an existing one that is no longer being used and configure it for a new use.

In Live Music Archive, we display thousands of concerts at a time and, due to the complexity of the Concert Cell, I wanted to be able to reuse them. The Concert Cell Recycler allows us to build a pool of Concert Cells with different configurations and reuse them as-needed.

Live Music Archive uses four main variations of the Concert Cell and they just differ in the number of recordings shown per page: 1, 2, 3, and 4.

One-Up
Two-Up
Three-Up
Four-Up

The ViewControllerRecycler lets us build up pools of the four different types of Concert Cells and reuse them so we're not constantly creating new cells and all the overhead that comes with that.

Challenges

Early versions of the Concert Cell Recycler had some timing issues where I was recycling cells too early so when they were supposed to be displayed, they weren't there. I was recycling the Concert Cell in UICollectionView didEndDisplaying and assigning a new Concert Cell in cellForItem, BUT cellForItem isn't necessarily called when the cell is displayed again. The solution was to recycle the old Concert Cell AND assign the new Concert Cell at the same time in cellForItem. This is the one time you can be sure a UICollectionViewCell is no longer going to be needed.

Conclusion

I hope this gave you a bit of an insight into what goes into building Live Music Archive. This just scratches the surface of what goes on, but it's such an amazing process! There are lots of frustrations, sometimes spending days tracking down a single bug, but it's always so satisfying to see the end result of months of work.

I hope you enjoy the app!