バーコードから栄養成分表示へ:React Nativeで食品スキャナー
高性能なVision Cameraライブラリを使用してReact Nativeでバーコードスキャナーを作り、Open Food Facts APIから栄養情報を取得・表示するステップバイステップガイドです。
このページの内容
超市で商品の栄養成分について気になった経験はありませんか?健康意識の高い消費者が増える現代において、こうした情報に即座にアクセスできることは、ゲームチェンジャーと言えます。この記事では、そのためのモバイルアプリの機能を開発した私の旅路をナビゲートします。具体的には、バーコードをスキャンして栄養データを取得・表示するアプリです。
高性能なカメラ関連ライブラリである react-native-vision-camera を使用し、React Nativeでカメラ機能を実装します。データソースとしては、世界中から集まる食品データの無料・オープン・クラウドソースデータベースである Open Food Facts API を利用します。
このプロジェクトは単なる技術的演習ではなく、ユーザーが食品についてより多くの情報に基づいた意思決定ができるようにする、実用的なツールを作成することが目的です。このチュートリアルを終える頃には、React Nativeアプリにカメラ機能を統合し、外部APIと連携してユーザーに価値あるデータを提供する方法を深く理解できるようになるでしょう。
前提条件:
- React NativeとJavaScriptの基本的な理解
- Node.jsとnpm/yarnがインストール済み
- React Native用の開発環境(Android StudioまたはXcodeを含む)が設定済み
- カメラ機能をテストするための物理デバイスの使用を強く推奨
問題の理解
中心的な課題は2つあります:まず、デバイスのカメラを使用して商品からバーコードを確実にキャプチャする必要があります。次に、そのバーコード情報を取得し、巨大なオンラインデータベースから対応する栄養データを取得する必要があります。
技術的課題:
- カメラ統合: デバイスのカメラへのアクセスと管理は複雑で、権限、不同するデバイスの機能、滑らかなユーザーエクスペリエンスの確保などが必要です。
- バーコード検出: 最適なとは言えない照明条件やわずかに損傷したバーコードであっても、スキャンが高速で正確である必要があります。
- API連携: 非同期データ取得の処理、潜在的に複雑なJSONレスポンスの解析、ローディングとエラーの状態の適切な管理が必要です。
幸いなことに、react-native-vision-camera などの最新のライブラリによりカメラ統合が簡素化され、内蔵のコードスキャン機能により、複数のライブラリを必要とすることを避けることができます。
前提条件
コーディングを開始する前に、プロジェクトを設定しましょう。
-
新しいReact Nativeプロジェクトを作成:
bash npx react-native@latest init NutritionScanner cd NutritionScanner -
react-native-vision-cameraをインストール:bash npm install react-native-vision-camera cd ios && pod install -
権限の設定: カメラを使用するには、ネイティブプロジェクトファイルで必要な権限を宣言する必要があります。
-
iOS(
ios/NutritionScanner/Info.plist):xml <key>NSCameraUsageDescription</key> <string>$(PRODUCT_NAME) needs access to your Camera.</string> -
Android(
android/app/src/main/AndroidManifest.xml):xml <uses-permission android:name="android.permission.CAMERA" />
-
プロジェクトが初期化され、権限が設定されたので、ビルドを開始する準備ができました。
ステップ1:バーコードスキャナーの実装
最初の主要なタスクは、カメラを起動してバーコードを積極的にスキャンすることです。
何をするのか
カメラの権限を要求し、カメラフィードを表示し、react-native-vision-camera からフックを使用してリアルタイムでバーコードを検出するコンポーネントを作成します。
実装
新しいファイル src/BarcodeScanner.js を作成します:
// src/BarcodeScanner.js
import React, { useEffect, useState } from 'react';
import { View, StyleSheet, Text, Linking, Button } from 'react-native';
import {
Camera,
useCameraDevice,
useCodeScanner,
} from 'react-native-vision-camera';
const BarcodeScanner = ({ onBarcodeScanned }) => {
const [hasPermission, setHasPermission] = useState(false);
const device = useCameraDevice('back');
useEffect(() => {
const checkCameraPermission = async () => {
const status = await Camera.getCameraPermissionStatus();
if (status === 'not-determined') {
const newStatus = await Camera.requestCameraPermission();
setHasPermission(newStatus === 'granted');
} else {
setHasPermission(status === 'granted');
}
};
checkCameraPermission();
}, []);
const codeScanner = useCodeScanner({
codeTypes: ['ean-13', 'upc-a', 'qr'], // Specify the barcode types you want to scan
onCodeScanned: (codes) => {
if (codes.length > 0) {
onBarcodeScanned(codes[0].value);
}
},
});
if (device == null) {
return <Text>No camera device found.</Text>;
}
if (!hasPermission) {
return (
<View style={styles.container}>
<Text>Camera permission is required to scan barcodes.</Text>
<Button title="Grant Permission" onPress={() => Linking.openSettings()} />
</View>
);
}
return (
<View style={StyleSheet.absoluteFill}>
<Camera
style={StyleSheet.absoluteFill}
device={device}
isActive={true}
codeScanner={codeScanner}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default BarcodeScanner;
動作の仕組み
- コンポーネントがマウントされたときにカメラの権限を確認し、要求するために
useEffectフックを使用します。 useCameraDevice('back')はスキャン用に背面カメラ」を選択します。useCodeScannerフックはスキャンロジックの中核です。スキャンするバーコードの種類とコールバック関数onCodeScannedを提供します。- バーコードが検出されると、
onCodeScannedがトリガーされ、スキャンされた値を親コンポーネントに渡します。
一般的な落とし穴
- 権限が拒否された場合: ユーザーがカメラの権限を拒否した場合、後から付与する方法を提供する必要があります。そのため、アプリの設定を開くボタンを用意しています。
- シミュレーターの制限: カメラはシミュレーターでは動作しません。常に物理デバイスでテストしてください。
ステップ2:栄養データの取得
バーコードをキャプチャできるようになったので、それを使用して Open Food Facts API からデータを取得しましょう。
何をするのか
スキャンしたバーコードで Open Food Facts API を呼び出す関数を作成し、JSONレスポンスを解析して必要な栄養情報を抽出します。
実装
まず、API呼び出し用のサービスファイル src/api.js を作成します:
// src/api.js
const API_URL = 'https://world.openfoodfacts.org/api/v2/product/';
export const fetchNutritionData = async (barcode) => {
try {
const response = await fetch(`${API_URL}${barcode}?fields=product_name,nutriments,image_url`);
if (!response.ok) {
throw new Error('Product not found');
}
const data = await response.json();
if (data.status === 0 || !data.product) {
throw new Error('Product not found in the database.');
}
return data.product;
} catch (error) {
console.error('API Error:', error);
throw error;
}
};
動作の仕組み
- Open Food Facts 製品エンドポイントの基本URLを定義します。
fetchNutritionData関数はバーコードを受け取り、完全なAPI URLを構築してGETリクエストを実行します。- URLに
?fields=...を追加しました。これは Open Food Facts API の強力な機能で、必要なデータのみをリクエストし、レスポンスサイズを削減できます。 - 製品が見つからない場合、ネットワークエラーが発生した場合などの基本的なエラー処理を実装しています。
一般的な落とし穴
- 不完全なデータ: Open Food Facts データベースはクラウドソースであるため、一部の製品には栄養情報が不完全または欠落している場合があります。UIはこれを適切に処理できる必要があります。
- レート制限: 寛大ではあるものの、APIのレート制限に注意してください。特にアプリが人気になった場合はそうです。
すべてをまとめる
次に、BarcodeScanner とAPIサービスをメイン� файла App.js に統合しましょう。
完全な例
// App.js
import React, { useState } from 'react';
import {
SafeAreaView,
StyleSheet,
View,
Text,
Button,
ActivityIndicator,
Image,
} from 'react-native';
import BarcodeScanner from './src/BarcodeScanner';
import { fetchNutritionData } from './src/api';
const App = () => {
const [isScanning, setIsScanning] = useState(false);
const [productData, setProductData] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const handleBarcodeScanned = async (barcode) => {
setIsScanning(false);
setIsLoading(true);
setError(null);
setProductData(null);
try {
const data = await fetchNutritionData(barcode);
setProductData(data);
} catch (err) {
setError(err.message);
} finally {
setIsLoading(false);
}
};
const renderProductInfo = () => {
if (!productData) return null;
const { product_name, nutriments, image_url } = productData;
return (
<View style={styles.infoContainer}>
{image_url && <Image source={{ uri: image_url }} style={styles.productImage} />}
<Text style={styles.title}>{product_name}</Text>
<Text>Calories per 100g: {nutriments['energy-kcal_100g'] || 'N/A'}</Text>
<Text>Carbs per 100g: {nutriments.carbohydrates_100g || 'N/A'}</Text>
<Text>Protein per 100g: {nutriments.proteins_100g || 'N/A'}</Text>
<Text>Fat per 100g: {nutriments.fat_100g || 'N/A'}</Text>
</View>
);
};
if (isScanning) {
return <BarcodeScanner onBarcodeScanned={handleBarcodeScanned} />;
}
return (
<SafeAreaView style={styles.container}>
<Text style={styles.header}>Nutrition Scanner</Text>
<View style={styles.content}>
{isLoading && <ActivityIndicator size="large" />}
{error && <Text style={styles.errorText}>Error: {error}</Text>}
{productData && renderProductInfo()}
</View>
<Button
title={productData ? 'Scan Another Item' : 'Start Scanning'}
onPress={() => {
setProductData(null);
setError(null);
setIsScanning(true);
}}
/>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'space-between', padding: 16 },
header: { fontSize: 24, fontWeight: 'bold', textAlign: 'center' },
content: { flex: 1, justifyContent: 'center', alignItems: 'center' },
infoContainer: { alignItems: 'center' },
productImage: { width: 150, height: 150, resizeMode: 'contain', marginBottom: 12 },
title: { fontSize: 20, fontWeight: 'bold', marginBottom: 8 },
errorText: { color: 'red', textAlign: 'center' },
});
export default App;
動作の仕組み
isScanning、productData、isLoading、errorなど、いくつかのuseStateフックでアプリの状態を管理します。- 「Start Scanning」ボタンが押されると、
isScanningがtrueに設定され、BarcodeScannerコンポーネントが描画されます。 - バーコードがスキャンされると、
handleBarcodeScannedが呼び出されます。isScanningをfalseに設定し、ローディングインジケーターを表示してから、APIサービスを呼び出します。 - 結果は
renderProductInfo関数で表示され、製品の詳細が条件付きで描画されます。
ユーザーエクスペリエンスに関する注意
バーコードスキャナーに共通する問題の1つは、スキャン頻度が高すぎることです。これにより、1つの商品に対して複数のAPI呼び出しが行われる可能性があります。この実装では、最初のスキャン成功后すぐに isScanning を false に設定することで、この問題を効果的に防止し、より制御されたユーザーエクスペリエンスを提供します。
結論
React Nativeアプリケーションで機能的かつ実用的な機能を正常に構築しました。react-native-vision-camera によるカメラ統合、権限の処理、Open Food Facts API からのデータの取得と表示を行いました。
このプロジェクトは素晴らしい基盤となります。以下几个方面に扩展できます:
- より詳細な栄養情報の表示(ビタミン、ミネラル、添加物など)
- スキャンした商品の履歴追加
- オフライン機能の實現(以前にスキャンした製品のキャッシュ)
- Open Food Factsコミュニティへの貢献(まだデータベースに없는製品の写真をユーザーが提出できるようにする)
リソース
- React Native Vision Camera: https://react-native-vision-camera.com/
- Open Food Facts APIドキュメント: https://openfoodfacts.github.io/openfoodfacts-server/
クイックリファレンス
| 側面 | 主要情報 |
|---|---|
| トピック | 開発 |
| 関連性 | 患者教育と認識 |
| アクション | 医療提供者と相談する |
| 頻度 | ケアチーム推奨的那样 |
| 証拠 | 現在の臨床ガイドラインに基づく |
よくある質問
Q: これが自分に該当する怎么看就知道了か? A: 個人的な病歴と家族歷と医療提供者に相談して、個人のリスク因子を評価し、適切な次のステップを決定してください。
Q: 医師に何,就应该询问哪些问题? A: 良い質問には次のようなものがあります:「私の結果はどういう意味ですか?」「私のオプションは何ですか?」「どんな生活習慣の改善が役立ちますか?」「いつフォローアップすべきですか?」
Q: 健康を改善するための生活習慣の変更はありますか? A: ほとんどの健康状態は、根本的な対策から恩恵を受けます:バランスの取れた栄養、規則的な運動(週150分以上)、質の良い睡眠(7〜9時間)、ストレス管理、タバコの回避です。
医療に関する免責事項
この記事は教育目的のみを目的としており、医学的アドバイスではありません。健康、治療、またはケアに関する決定を下す前には、常に資格のある医療提供者に相談してください。個人の状況是不同的であり、一个人に適切なものが別の人に適切なものではない場合があります。
医学的緊急事態が発生した場合は、直ちに911(または現地の緊急番号)に電話してください。