最近Expo使ってiPhone,Android向けのアプリの開発を行っている僕です。
最近は全然php書いてなくて切ないです。
さて、今日はExpoでWebViewを使ってローカルにあるHTMLファイルを表示するのに苦労したお話をします。
バージョンについて
1
2
3
4
5
6
7
|
"dependencies": {
"expo": "^33.0.0",
"react": "16.8.3",
"react-dom": "^16.8.6",
"react-native": "https://github.com/expo/react-native/archive/sdk-33.0.0.tar.gz",
"react-native-web": "^0.11.4"
},
|
こんな感じで、Expoは33と2019/06時点の最新のものを使っています。
今日のコードのサンプルについて
GitHub - polidog/expo-webview-sample
webviewでローカルにあるHTMLを開くときに何が問題か?
例えば以下のコード。これはiPhoneならHTMLがちゃんと表示されます。
しかし、Androidの場合はテキストとして表示されてしまいます。(つまりHTMLタグから表示されてしまいます)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
// LocalHtml.js
import React from 'react';
import { StyleSheet, View, Text } from 'react-native';
import LocalHtmlView from './components/LocalHtmlView'
export default function App() {
return (
<View style={styles.container}>
<LocalHtmlView />
<Text>abc</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
paddingTop: 50,
flex: 1,
justifyContent: 'center',
}
})
|
なぜかは知りませんが、Androidの場合はダメでした。
Androidの場合はどうするのがいいのか?
調べたところによると、Androidの場合はfile://
で始まるファイルパスでhtmlの場所を指定する必要があります。
しかし単純にfile://assets/html/hello.html
とかって指定すればいいわけではありません。
以下の手順でAndroidでHTMLを表示できるようになります。
- expo-assetを使ったdownload処理
- WebViewのプロパティの追加
expo-assetを使ったdownload処理
expot-assetを使ったダウンロード処理を以下のように記述します。
Androidの場合のみダウンロード処理を行う形です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
// LocalHtml.js
import React, { useState, useEffect } from 'react'
import { WebView, Platform } from 'react-native'
import { Asset } from 'expo-asset'
const html = require('../assets/html/hello.html')
export default () => {
const [file, setFile] = useState(null)
useEffect(() => {
if (Platform.OS === 'android') {
download().then((downloadFile) => {
setFile(downloadFile)
})
}
}, [])
return (
<WebView
source={getSource(file)}
/>
)
}
const download = async () => {
let file = Asset.fromModule(html)
if (file.localUri !== null) {
return file
}
await file.downloadAsync() // Optional, saves file into cache
console.log('file', file)
return file
}
const getSource = (file) => {
if (Platform.OS !== 'android') {
return html
}
if (file === null) {
return {}
}
return {
uri: file.localUri
}
}
|
サンプルではReact Hooks使っているのでuseEffectsでdownload処理させています。
Hooks使わない場合はcomponentDidMount
とかでdonwload処理させるのが良さそう。(React詳しくないので適当です。)
WebViewのプロパティの追加
ダウンロード処理の記述だけだとダメで、WebViewにallowFileAccess
のプロパティの設定も必要になります。
ここは単純にtrue
を渡せば問題ないです。
1
2
3
4
5
6
7
8
|
// LocalHtml.js
return (
<WebView
allowFileAccess={true}
source={getSource(file)}
/>
)
|
これでAndroidでも表示されるようになるかと思います。
最後に
僕はReactとかjsとかReactNativeとかアプリ開発とか大体素人なので、これが正しい実装なのか判断できません。
現状のシミュレータ使った場合だとこれで動いた程度なので、もっと良いやり方があったらコメントで教えてください。
参考URL
Embedding a local website on your Expo/React Native project
reactjs - react native (expo) load markdown files - Stack Overflow