React Native Tips

Following Blair MacIntyre's lead, I've accepted that the only way to write more is to write more. No more waiting for the perfect topic at the perfect time with perfect editing. It's better to publish something unpolished than to not publish at all.

So.. I've been frustrated with every music service. What I want is cloud hosting of my massive MP3 library, sharable with my son and wife. I've used iTunes, Amazon Music, and Google Music over the years. The problem is that none of them are good. They all do matching where they save bandwidth by guessing which songs I have that are already on their servers. Then finally, once I get everything set up perfectly, they redesign their system and everything breaks again.

I think the fundamental problem is that the money has gone into streaming subscription services. I don't want that. I want to own my music. Clearly I'm old. And get off my lawn, you damn kids! However, I have a superpower: I write code. I can just build my own service... he said... two years ago....

So, I'm still at it. My personal system works well enough on the web that I decided it was time to build a mobile client. While I'd love to do it as a Progressive Web App, on iOS the music will still stop as soon as the safari instance goes into the background. As an alternative I've started digging into React Native.

React Native

The good news is, React Native does what it says on the tin. I write Javascript code and get a native installed iOS app, without touching any Objective C, Swift, or launching the monstrosity that is XCode. And 99% of the time this is true.

The bad news is you still have to dig into native code for anything that isn't built in, which is a lot, but the React Native ecosystem is big enough to provide a lot of these as modules. On the other hand, when it comes to deployment the docs are less than stellar.

For playing music, and interfacing with the native iOS notification/control center APIs I'm using the react-native-track-player module. I had some bugs with the released version so I'm using the dev branch and it seems to work fine. My current version has a list of artists, albums, songs, and then a player screen (currently with dummy album art). The fonts are native and scale to match system settings. Quite nice.

Layout and Style

React Native uses Flexbox for layout, but this is it's own interpretation of Flexbox that's very different than CSS. I'd prefer CSS Grid, since it's made my magical faires, but with enough nested flex boxes you can get the job done.

Anyway, after banging on this for a few days, here's some tips that may come in handy:

Pretty much any box (View, Image, Text) can have a border on it, though Text's interpretation of foreground and background is wonky, so I'd put any borders and background on the parent View. Here's an image with rounded corners, which nicely trims off the edge of the image.

<Image 
source={{uri:image_url}}
style={{
width:250,
height:250,
borderRadius:20
}}
/>

To make something be inside of a circle, just make a square view with borderRadius set to half the dimensions:

        const s = 80
const playWrapper= {
width: s,
height:s,
borderRadius: s/2,
}

Native Compiling

To switch between debug and release version, or to switch between using the simulator vs a real device, you have to change to settings in XCode. Or do you?!! You can actually use the react-native commandline tool to do all of these things.

run on a simulator

react-native run-ios --simulator="iPhone 6"

run on a real device

react-native run-ios --device=\"Joshua's iPhone\"

run in debug mode, on the simulator

react-native run-ios --simulator=\"iPhone 6\" --configuration=\"Debug\"

run on my real phone in release mode

react-native run-ios --device=\"Joshua's iPhone\" --configuration=\"Release\"

When you switch between release and debug mode, you probably want to also switch between using a static JS bundle versus loading from the live debug server. According to the docs you need to edit AppDelegate.m to do this. However, the XCode compiler will include a compile time flag indicating which mode you are in, so you can modify the code to switch automatically. The top of your app delegate should look like this:

- (BOOL)application:(UIApplication *)application 
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSURL *jsCodeLocation;
#ifdef DEBUG
jsCodeLocation = [[RCTBundleURLProvider sharedSettings]
jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main"
withExtension:@"jsbundle"];
#endif
...

Voila, no code editing. You can even make this be npm scripts.

in memoriam

In the end I'm reasonably happy with React Native. It lets me build a native-ish iOS app that streams music with no native code while still tying to iOS specific APIs. And it's surprisingly fast. Far faster than the abomination that is Amazon Music. Soon I shall say goodbye to you, O waster of battery.

Talk to me about it on Twitter

Posted May 3rd, 2018

Tagged: react javascript musicapp