Joe Noël

Joe Noël

Software | Audio | Music

Focusrite Control for iOS

5 minutes
Cover image for 'Focusrite Control for iOS'

In a typical home studio, most people work on their own. They are the engineer; They are the artist; They are the producer.

This creates a problem when they need to tweak something on their computer from the other side of the room.

When we were developing Focusrite Control, we recognised the potential for a mobile app to allow users to control monitor mixes without being next to the computer. We decided to develop an iOS application to enable users to control their devices from their iPhone or iPad.

Controlling the scope

In the early design discussions, we decided to keep the scope of the app as lean as possible. The app would be a companion to the desktop app and not a direct replacement.

We centred on a few core use cases to allow users to tweak settings across the room from their computer: control of monitor mixes, control of output levels, control gain levels for remote-controlled preamps.

There were a lot of features in the desktop version that didn't make sense for a mobile version of Focusrite Control. We didn't include anything that we considered too 'administrative' such as channel names and device settings.

Choosing a framework

One of the first tasks was to choose a technology stack. The plan was to start with an iOS application and add support for Android later.

I ended up choosing React Native. Why?

Firstly, it's cross-platform. This would save precious time when developing the Android version.

Secondly, although it's cross-platform, it still uses native components. Many of the web-based cross-platform solutions just don't quite have that native look-and-feel. I liked how easy it was to add custom, native user interface elements.

React Native

I enjoyed working with React Native.

I found it fast to develop and iterate on the user interface.

I was coming from a C++ world, building user interfaces procedurally. This means for a new component, you need to construct it, add it to the view hierarchy, give it a size, register for events, update it when data changes, resize it when its parent changes etc.

I found it refreshing to build the user interface declaratively. Just create a component, bind its props and state to something useful and stick it in the view hierarchy. Done. Much easier to develop and much easier to reason about.

One of the biggest selling points for React Native was the ability to mix-and-match between React Native components and custom, native components. I ended up relying on custom components more than I was anticipating! They allow you to take full advantage of the Core Animation framework to get smooth animations that don't depend on the JavaScript runtime.

Of course, as soon as you write a custom component, you lose the cross-platform benefits. However, I felt that re-writing a few small, focused components for Android would be less painful than re-creating all of the logic to tie them together.

Considering the tender years of React Native, the tooling was quite amazing. I loved how crashes were reported on the screen. The ability to change the code without re-compiling blew my mind!

Debugging JavaScript in Chrome was hugely convenient. Although, to begin with, I didn't realise that when debugging in Chrome, you are running the V8 engine in Chrome, rather than the JavaScript runtime on the device. This caught me out when debugging some performance issues on older devices. As soon as I started debugging, the performance issues would magically disappear. Definitely one to watch out for!

Bitrot. Already?!

Eventually, the project shipped, it was on the App Store, and people started using it - :tada:! After a couple of bug fix releases I moved on to other projects.

Several months passed, we talked about the EU a lot, and I was busy on another project. That's when I learned a lesson about bitrot and dependency hell.

After an update to iOS, some customers started reporting that the app was crashing on startup. A little bit of debugging and I was able to point the finger to a third-party dependency. The fix was a simple one and we went to submit a pull request to the maintainers. Sure enough, they'd spotted the problem themselves.

Whoops, we should have checked that first. Nevermind, we'll just update the dependency...

The problem was that this dependency required a new version of React Native. We were using version 0.37. Things were moving fast in React Native land and we were now quite out-of-date. Several of the other dependencies weren't compatible with the latest version of React Native. Ah.

This lead to a real headache for what should have been a simple fix.

So what are the lessons?

In future, I would probably think twice about using a fast-moving technology for a project that isn't being updated regularly. If I was working on the project every day, I probably would have kept it up-to-date. The gap in the development of a few months was aeons in the scale of the React Native ecosystem.

I would also make sure that any dependencies that I bring in are regularly kept up-to-date. As it happened, the bug that caused the crash was fixed, but it was the knock-on effect of having to update other dependencies that rained hell.


Overall, it was an enjoyable, fast-paced project to work on. I felt proud to see my first app in the App Store. I also appreciated validation for the extra work to make the desktop version of Focusrite Control a client-server application. React Native was fun, although if I were to use it again I would be more careful with dependencies.

Published: 2020-05-05