Figが5人のエンジニアチームとExpoで数百万人の食の安全を守る方法
ユーザー • 2026年3月31日 • 5分で読める
Jake Lynch ゲスト著者
Taylor Harrison ゲスト著者
Figは食事制限のある数百万人が安全な食品を見つけるのを支援しています。5人のチームがExpo SDKとEAS Updateを使用して、確実に配信し、迅速に開発を進める方法をご紹介します。
Fig(Food is Good)は、あらゆる食事制限やアレルギーを持つ人々が実際に食べられる食品を見つけ、健康に深刻な影響を与える可能性のある反応を避けるのを支援しています。成分をスキャンして安全な製品を発見することから、レストランが特定のニーズにどの程度対応しているかを確認することまで、Figは複雑な食事ニーズを持つ人々にとって食事をより簡単で安全にします。
数百万人(Figチームのほとんどを含む!)がFigを頼りに安全で確信の持てる食品選択を行っています。昨年、私たちはExpo App Awardのファイナリストになりました。Expoチームは私たちのアプリのネイティブな感触とスナッピーな機能性を評価してくれたので、このブログではそれについて説明します。
なぜExpoなのか?
Figは標準的なReact Nativeアプリとして始まりましたが、開発を加速し信頼性を向上させるためにExpoを徐々に採用してきました。私たちは5人のエンジニアからなる小さなチームなので、複雑さに時間を無駄にする余裕はありません。
壊れて保守されなくなったコミュニティパッケージに何度か痛い目に遭った後、時間をかけてSDKを統合することにしました。私たちはライブラリを信頼するようになり、将来的にはさらに多くを統合する予定です。
昨年MicrosoftがAppCenterを廃止した際、重要なJavaScriptパッチのためのover-the-airアップデートもExpoのEASシステムに移行しました。
Expo SDKがFigの成功に不可欠だった理由
Figが小さなチームで迅速にパフォーマンスの高いアプリを開発するのに役立ったExpo SDKパッケージの例をいくつか紹介します:
expo-image
expo-imageにより、Figは画像とSVGを効率的にレンダリングして、コミュニティメンバーにとってアプリ体験を親しみやすくできます。
標準のReact Native ImageコンポーネントはSVGをサポートしていないため、最初はすべてのSVGレンダリングニーズ、特にアイコンにreact-native-svgを使用していました。SVG文字列をSvgXmlに渡すことで、stroke、stroke-width、fillなどの属性の設定が非常に簡単になりました。

しかし、Software Mansionのこのブログ投稿を読んだ後、SVGからコンポーネントツリーを作成することにはコストがかかり、静的アイコンには過剰であることがわかりました。
アイコンをexpo-imageに移行してネイティブライブラリがSVGレンダリングを処理できるようにし、SVG内部を操作/アニメーション化する必要がある場合のみreact-native-svgを使用するようにしました。
expo-imageのSVG変更機能は限定的(tintColorはすべての非透明ピクセルの色を設定)なので、stroke-widthなどを変更する必要がある場合はいくつかのSVGを追加でバンドルすることになりますが、パフォーマンスのトレードオフは価値があると考えています。
react-native-svg:
import thumbsUpIcon from '@assets/icons/thumbs-up.svg';
import { iconDefault } from '@utils/colors.util';
const Icon: FunctionComponent<IconProps> = ({
iconSize,
color,
rotation,
transform,
height,
width,
...otherProps
}) => {
const parsedIconSize = !height && !width ? getIconSize(iconSize) : undefined;
const sizeProps: { height?: NumberProp; width?: NumberProp } = {};
if (parsedIconSize || height) {
sizeProps.height = parsedIconSize ?? height;
}
if (parsedIconSize || width) {
sizeProps.width = parsedIconSize ?? width;
}
return (
<SvgXml
color={color ?? iconDefault}
transform={rotation ? [{ rotate: `${rotation}deg` }] : transform}
{...sizeProps}
{...otherProps}
/>
);
};
expo-image:
import { Image } from 'expo-image';
import styled, { css } from 'styled-components/native';
export const iconAssets = {
thumbsUp: require('@assets/icons/thumbs-up.svg'),
...
} as const;
export const StyledImage = styled(Image)<{
width?: number;
height?: number;
rotation?: number;
}>`
${({ width }) => width !== undefined && css`width: ${width}px;`}
${({ height }) => height !== undefined && css`height: ${height}px;`}
${({ rotation }) => !!rotation && css`transform: rotate(${rotation}deg);`}
`;
const Icon: FunctionComponent<IconProps> = ({
iconSource: iconName,
iconSize,
height,
width,
rotation,
color,
...otherProps
}) => {
const parsedIconSize = !height && !width ? getIconSize(iconSize) : undefined;
return (
<StyledImage
tintColor={color}
source={typeof iconName === 'object' ? iconName : iconAssets[iconName]}
width={parsedIconSize ?? width}
height={parsedIconSize ?? height}
rotation={rotation}
contentFit="contain"
{...otherProps}
/>
);
};
expo-location
Figはレストラン検索機能の位置データ取得にexpo-locationを使用しています。より積極的に保守されているライブラリから位置データをより細かく制御するために、@react-native-community/geolocationから移行しました。
- 概算座標とより正確な座標を同時に取得
- 十分に新しい場合はキャッシュされた位置データを利用
- これにより、マップビューとレストラン検索に近くのレストランを迅速に表示できる
位置情報はコンテキストに保存され、アプリ全体で使用されます。特に新しいレストラン検索機能で活用されています。

import * as Location from 'expo-location';
const locationMaximumAge = 3 * 60 * 1000;
const locationRequiredAccuracy = 11;
const approximateLocationRequiredAccuracy = 3001;
export const LocationProvider: FunctionComponent<PropsWithChildren<LocationProviderProps>> = ({ children }) => {
const [coordinates, setCoordinates] = useState<Coordinates>();
const [approximateCoordinates, setApproximateCoordinates] = useState<Coordinates>();
const {
permissionStatus: locationPermissionStatus,
triggerRequestPermission: triggerRequestLocationPermission,
} = usePermission('location');
const fetchCoordinates = useCallback(async (
setNewCoords: (coords: Coordinates) => void,
requiredAccuracy: number,
locationAccuracy: Location.LocationAccuracy,
maxAge?: number,
) => {
try {
let location = await Location.getLastKnownPositionAsync({
requiredAccuracy,
maxAge,
});
if (!location) {
location = await Location.getCurrentPositionAsync({
accuracy: locationAccuracy,
});
}
const newCoordinates = location?.coords;
setNewCoords(newCoordinates);
return newCoordinates;
} catch (e) {
logFigError('Error getting location', {
error: e,
});
}
}, [mockLatitude, mockLongitude]);
const updateLocation = useCallback(async () => {
const [, newCoordinates] = await Promise.all([
fetchCoordinates(
setApproximateCoordinates,
approximateLocationRequiredAccuracy,
Location.LocationAccuracy.Lowest,
),
fetchCoordinates(
setCoordinates,
locationRequiredAccuracy,
Location.LocationAccuracy.High,
locationMaximumAge,
),
]);
return newCoordinates;
}, [fetchCoordinates]);
useEffectOnce(locationPermissionStatus === 'granted', () => {
updateLocation();
});
return (
<LocationContext.Provider value={{
coordinates,
approximateCoordinates,
updateLocation
}}>
{children}
</LocationContext.Provider>
);
};
expo-store-review
Figは、プラットフォームアップデートがAPIを非推奨にした際にコミュニティパッケージが壊れることで何度か痛い目に遭っています。一例として、iOS 18がSKStoreReviewControllerを非推奨にし、react-native-in-app-reviewが動作しなくなったことがありました。
expo-store-reviewにより、Figは必要なすべてのオペレーティングシステムバージョンをサポートする、ユーザーに評価とフィードバックを求める新しいライブラリを迅速に採用できました。
画面の統合は簡単で、多くのコミュニティライブラリとは異なり、ExpoのライブラリはベースとなるAPIが変更されても継続的に保守されています。
import * as StoreReview from 'expo-store-review';
if (await StoreReview.hasAction()) {
StoreReview.requestReview();
}
ExpoのOTAアップデートで重要なパッチをプッシュ
App CenterがCodePushを2025年3月に廃止した後、Figは重要な修正と実験的体験テストを即座に配信し続けるためにExpo EAS Updatesを採用しました。これにより、コードを配信する際の信頼性を持ち、本番環境に到達したバグを迅速に対処できるようになりました。
本番ビルドには引き続きBitriseを使用していますが、必要に応じてユーザーに重要なアップデートを配信するExpoのOTAアップデートシステムを信頼できることを気に入っています。
ExpoでのFigの未来
Figは、保守負担を軽減し、ビルドの複雑さを簡素化し、プラットフォームが拡張するにつれて新しいネイティブ機能を解放するために、より多くのExpoモジュールを統合し続けます。
React Nativeバージョンのアップグレードは、かつて物事が壊れるまで避けていた恐ろしいタスクでした。より多くのExpoパッケージに移行するにつれて、アップグレードプロセスはより簡単で信頼性が高くなり、開発サイクルを無駄にすることなく、サポートされているReact Nativeバージョンを維持できるようになりました。