Tuesday, March 27, 2018

Running Expo offline

Offline Expo:

I wanted to continue working on my expo project on my laptop while no wifi was present.   It was very hard to find information on how to do get it running, or if it was even possible.

Finally found the 2 magic commands!

This command will start the offline server.   No login required.
exp start --offline

This command will start the app in a running android emulator.
exp android --offline


To make this even easier (and so I didn't have to remember the commands) I just added them to my scripts in package.json

"scripts": {
"server": "exp start --offline",
"emulator": "exp android --offline"
}


Then I just run on one terminal:
npm run emulator


And in another terminal:
npm run server


Documentation:
To get the full offline experience, I also downloaded react-native, react-native-elements and expo from github so I can access the docs offline!   The gh-pages branch in react-native and react-native-elements contain the doc files, though the links won't work, so you have to browse to them manually.   Still better than no docs at all!

Thursday, March 22, 2018

Expo Icon Fonts with React Native and React Native Elements!

I was trying to use the Avatar and Icon objects from react-native-elements I kept getting the following error:
fontFamily 'MaterialIcons' is not a system font and has not been loaded through Expo.Font.loadAsync.

- If you intended to use a system font, make sure you typed the name correctly and that it is supported by your device operating system.

- If this is a custom font, be sure to load it with Expo.Font.loadAsync.

It was driving me nuts.   Googling for the answer just brought up snippets of information about what to do.   So finally I pieced together the parts to get it working.

You have to load the fonts before they are used.   It seems that if you ever blow away your node_modules and then do npm install again, you lose the built in loading.  So you have to do it manually.  Here is how!

I made sure the @expo/vector icons are loaded:

npm install --save @expo/vector-icons


Then I changed the App to load them directly:

import React from 'react';
import { View } from 'react-native';
import { Avatar } from 'react-native-elements';
import { AppLoading, Font } from 'expo';

import FontAwesome  
from './node_modules/@expo/vector-icons/fonts/FontAwesome.ttf';
import MaterialIcons  
from './node_modules/@expo/vector-icons/fonts/MaterialIcons.ttf';
export default class App extends React.Component {
state = {
fontLoaded: false
};

async componentWillMount() {
try {
await Font.loadAsync({
FontAwesome,
MaterialIcons
});
this.setState({ fontLoaded: true });
} catch (error) {
console.log('error loading icon fonts', error);
}
}
render() {
if (!this.state.fontLoaded) {
return <AppLoading />;
}

return (
<View>
<Text>My App</Text>
<Avatar
small
rounded
icon={{ name: 'add' }}
/>
</View>
);
}
}

So now the fonts load before the app is shown.   While they are loading, the AppLoading continues to render the loading screen before showing any of the app.   The fonts get loaded, then the state is set so the AppLoading component no longer renders and it continues to your app.

But why throw all that into the main App.js?    It's messy.   So I made an AppFontLoader utility that looks like this:


import React from 'react';
import { AppLoading, Font } from 'expo';

import FontAwesome 
from '../../node_modules/@expo/vector-icons/fonts/FontAwesome.ttf';
import MaterialIcons  
from '../../node_modules/@expo/vector-icons/fonts/MaterialIcons.ttf';

class AppFontLoader extends React.Component {
state = {
fontLoaded: false
};

async componentWillMount() {
try {
await Font.loadAsync({
FontAwesome,
MaterialIcons
});
this.setState({ fontLoaded: true });
} catch (error) {
console.log('error loading icon fonts', error);
}
}

render() {
if (!this.state.fontLoaded) {
return <AppLoading />;
}

return this.props.children;
}
}

export { AppFontLoader };

Now the App.js gets simplified!

import React from 'react';
import { View } from 'react-native';
import { Avatar } from 'react-native-elements';
import { AppFontLoader } from './src/utils';
export default class App extends React.Component { render() {
return ( 
<AppFontLoader>
 <View>
<Text>My App</Text>
<Avatar
small
rounded
icon={{ name: 'add' }}
/>
</View>
</AppFontLoader>
);
}
}

I hope this helps you.   It shouldn't take 4 hours to figure this out!