Turntable by Unspash (CCO Public Domain)
Insurgent radio from Chicago!
Graphic designed by Jeremiah Chiu for WLPN
For iPad and iPhone
Children Studying by sof_sof_0000 (CC0 Public Domain)
"[T]hink of it as a prototype for a different direction of the web."
— James Long
Radio Mast by stux (CC0 Public Domain)
$ npm install -g react-native-cli
$ react-native init my-project
$ npm install -g yo \
generator-react-native
$ yo react-native
Kiwi by Security (CC0 Public Domain)
Using react-native-video by Brent Vatne
Turntable loop video by Scott Schiller, BSD
$ npm install --save react-native-video
Terminal
Consider shrinkwrapping dependencies
import React from 'react-native';
import Video from 'react-native-video';
export default React.createClass({
render() {
return (
<Video source={{uri: 'turntable-loop-h264-512kbps'}}
style={styles.backgroundVideo}
rate={this.state.rate}
muted={this.state.muted}
resizeMode={this.state.resizeMode}
repeat={this.state.repeat} />
)
});
}
Editor
import React from 'react-native';
let { StyleSheet } = React;
export default StyleSheet.create({
backgroundVideo: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0
}
}
Editor
@implementation AudioManager
#import "RCTBridgeModule.h" // or RCTBridge.h
#import "STKAudioPlayer.h"
@interface AudioManager : NSObject
<RCTBridgeModule, STKAudioPlayerDelegate>
@property (nonatomic, strong)
STKAudioPlayer *audioPlayer;
Xcode, Obj-C
- (AudioManager *)init
{
self = [super init];
audioPlayer = [[STKAudioPlayer alloc] init];
[audioPlayer setDelegate:self];
}
Xcode, Obj-C
STK player delegates to class
#import "RCTBridge.h"
@implementation AudioManager
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(play);
RCT_EXPORT_METHOD(pause);
RCT_EXPORT_METHOD(resume);
RCT_EXPORT_METHOD(stop);
@end
Xcode, Obj-C
RCT_EXPORT_METHOD(pause)
{
if (!audioPlayer) {
return;
} else {
[audioPlayer pause];
}
}
Xcode, Obj-C
- (void)audioPlayer:(STKAudioPlayer *)player
stateChanged:(STKAudioPlayerState)state
{
switch(state) {
// don't make me do stuff
}
}
Xcode, Obj-C
Use STK for state change handling
#import "RCTEventDispatcher.h"
@synthesize bridge = _bridge;
Xcode, Obj-C
// dispatch on STK state change
switch (state) {
case STKAudioPlayerStatePlaying:
[self.bridge.eventDispatcher
sendDeviceEventWithName:@"AudioBridgeEvent"
body:@{@"status": @"PLAYING"}];
break;
}
export class AudioPlayer
import { AudioManager } from 'NativeModules';
export class AudioPlayer
static play() { AudioManager.play(); }
static pause() { AudioManager.pause(); }
static resume() { AudioManager.resume(); }
static stop() { AudioManager.stop(); }
}
Editor
export default React.createClass({
getInitialState() {
return { status: 'STOPPED' };
}
});
Editor
componentDidMount() {
this.subscription = DeviceEventEmitter.addListener(
'AudioBridgeEvent', (evt) => this.setState(evt)
);
AudioPlayer.getStatus((error, status) => {
(error) ? console.log(error) : this.setState(status)
});
}
Editor
render() {
return (
<View style={styles.appContainer}>
<TouchableOpacity
onPress={this._onPressLogo}
onLongPress={this._onLongPressLogo}>
<Image
style={styles.appLogo}
source={require('image!RadioButton')} />
</TouchableOpacity>
</View>
);
},
Editor
import { AudioPlayer } from '../lib/audio';
Editor
For use in your React components
_onLongPressLogo() {
AudioPlayer.play();
},
_onPressLogo() {
switch (this.state.status) {
case 'PLAYING':
this.setState({
status: 'PAUSED'
});
AudioPlayer.pause();
break;
}
}
Editor
import React from 'react-native';
let { NetInfo } = React;
Editor, ES6
export default React.createClass({
componentDidMount() {
NetInfo.isConnected.addEventListener(
'change',
this._onConnectivityChange
);
NetInfo.isConnected.fetch().done((isConnected) => {
this.setState({ isConnected });
});
},
_onConnectivityChange(isConnected) {
this.setState({ isConnected });
}
}
Editor, ES6
render() {
let message;
if (this.state.isConnected) {
switch(this.props.status) {
// set status message
}
} else {
message = 'Connect to the Internet.';
}
// ...
}
Editor, ES6
render() {
// ...
return (
<Text style={styles.statusMessage}>
{message}
</Text>
)
}
Editor, ES6/JSX
Or inbetweening
react-motion another option...
$ npm install --save react-tween-state
Terminal, Bash
export default React.createClass({
mixins: [tweenState.Mixin],
componentDidMount() {
this.tweenState('opacity', {
beginValue: 0,
endValue: 1,
duration: 1000
});
}
}
Editor, ES6
render() {
let transitionStyle = {
opacity: this.getTweeningValue('opacity')
};
return (
<View style={transitionStyle}>
// what to tween
</View>
)
}
Editor, ES6
@import AVFoundation
- (void)setSharedAudioSessionCategory
{
NSError *categoryError = nil;
[[AVAudioSession sharedInstance]
setCategory:AVAudioSessionCategoryPlayback
error:&categoryError];
if (categoryError) {
NSLog(@"Error setting category!");
}
}
Xcode, Obj-C
And set background mode in Info.plist
import UIKit
class RootViewController: UIViewController {
override func canBecomeFirstResponder() -> Bool {
return true
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.becomeFirstResponder()
UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
}
}
Xcode, Swift
Swap with vanilla controller in AppDelegate
@import MediaPlayer;
- (void)registerRemoteControlEvents
{
MPRemoteCommandCenter *commandCenter;
commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
[commandCenter.playCommand addTarget:self
action:@selector(didReceivePlayCommand:)];
[commandCenter.pauseCommand addTarget:self
action:@selector(didReceivePauseCommand:)];
commandCenter.stopCommand.enabled = NO;
commandCenter.nextTrackCommand.enabled = NO;
commandCenter.previousTrackCommand.enabled = NO;
}
Xcode, Obj-C
Buttons, control panel, lock screen
@import AVFoundation
Xcode, Obj-C
See a more detailed explanation on SO
Simplicity is the ultimate sophistication
Leonardo Da Vinci
iOS 8 App Icon SVG Template for Inkscape
Static resources like image assets take their name from Images.xcassets.
Use spaces to improve aural comprehension.
Short and sweet.
Install react-native-localization module
Configure Xcode
Create component in app
Learn on the Wiki
Reflecting on React Native development
Screenshot of the Oregon Trail game
Orin_has_died_of_dysentery by Orin Blomberg (CC BY-NC 2.0)
Graphic designed by Jeremiah Chiu for WLPN