Step 1: Create an .npmrc or yarnrc.yml file in the root of your project with the following lines replacing GITHUB_ACCESS_TOKEN with the token you've created
.npmrc
//npm.pkg.github.com/:_authToken=GITHUB_ACCESS_TOKEN
@passiolife:registry=https://npm.pkg.github.com
or
yarnrc.yml
npmScopes:
passiolife:
npmRegistryServer: 'https://npm.pkg.github.com'
npmAuthToken: 'GITHUB_ACCESS_TOKEN'
yarn add @passiolife/nutrition-ai-ui-ux
yarn add @passiolife/nutritionai-react-native-sdk-v3
project: {
ios: {},
android: {},
},
dependencies: {
'react-native-linear-gradient': {},
'@react-native-async-storage/async-storage': {},
'@react-native-community/datetimepicker': {},
'react-native-fs': {},
'@notifee/react-native': {},
'react-native-svg': {},
'@passiolife/nutritionai-react-native-sdk-v3': {},
'react-native-vision-camera': {},
'react-native-image-picker': {},
'@react-native-community/slider': {},
'lottie-react-native': {},
'@react-native-voice/voice': {},
},
};
Step 4: For Android, add this implementation line to the dependencies section on app/build.gradle file
dependencies {
// Add this line below for Passio SDK library
implementation files("$rootDir/../node_modules/@passiolife/nutritionai-react-native-sdk-v3/android/libs/passiolib-release.aar")
...
}
yarn add @react-navigation/native
yarn add @react-navigation/native-stack
yarn add @react-navigation/stack
yarn add react-native-gesture-handler
yarn add react-native-reanimated
yarn add react-native-safe-area-context
yarn add react-native-screens
yarn add react-native-sqlite-storage
Add react-native-reanimated/plugin plugin to your babel.config.js.
module.exports = {
presets: [
... // don't add it here :)
],
plugins: [
...
'react-native-reanimated/plugin',
],
};
Privacy - NSCameraUsageDescription
Privacy - NSSpeechRecognitionUsageDescription
Privacy - NSMicrophoneUsageDescription
Privacy - Photo Library Usage Description
<uses-permission android:name="android.permission.CAMERA" />
Dependency | Required Version |
---|---|
react-native-reanimated | >=^3.6.1 |
react-native-gesture-handler | >=2.16.0 |
react-native-safe-area-context | >=4.8.2 |
If you find a duplicate entry for '@react-navigation', ensure that the your project navigation dependencies are match our navigation dependencies require versions.
Dependency | Required Version |
---|---|
@react-navigation/native | >=^6.1.17 |
@react-navigation/native-stack | >=6.1.17 |
@react-navigation/stack | >=6.3.29 |
@react-navigation/bottom-tabs | >=6.5.20 |
react-native-screens | >=3.30.1 |
import React from 'react';
import {
BrandingProvider,
NutritionNavigator,
ServicesProvider,
usePassioConfig
} from '@passiolife/nutrition-ai-ui-ux';
import { NavigationContainer } from '@react-navigation/native';
export default function App() {
const { isReady } = usePassioConfig({ key: "YOUR_PASSIO_KEY" });
if (!isReady) {
return <Loading />;
}
return (
<ServicesProvider>
<BrandingProvider>
<NutritionNavigator />
</BrandingProvider>
</ServicesProvider>
);
}
Step 1: If you'd like to include your data service, follow the steps below. Otherwise, skip to the next step
NutritionDataService
used for Return a function to store or retrieve data through a REST API, local database, or Firebase, etc
export const dataService: NutritionDataService = {
/**
* Retrieves the patient profile.
* @returns A `Promise` resolving to the patient profile.
*/
getPatientProfile: () => getPatientProfile(),
/**
* Saves a food log.
* @param foodLog - The food log to save.
* @returns A `Promise` that resolves when the operation is complete.
*/
async saveFoodLog(foodLog: FoodLog): Promise<void> {
return saveFoodLog(foodLog);
},
/**
* Retrieves food logs.
* @returns A `Promise` resolving to an array of food logs.
*/
async getFoodLogs(): Promise<FoodLog[]> {
return getFoodLogs();
},
/**
* Deletes a food log.
* @param uuid - The UUID of the food log to delete.
* @returns A `Promise` that resolves when the operation is complete.
*/
async deleteFoodLog(uuid: string): Promise<void> {
return deleteFoodLog(uuid);
},
/**
* Deletes a recipe.
* @param uuid - The UUID of the recipe to delete.
* @returns A `Promise` that resolves when the operation is complete.
*/
async deleteRecipe(uuid: string): Promise<void> {
return deleteRecipe(uuid);
},
/**
* Deletes a favorite food item.
* @param uuid - The UUID of the favorite food item to delete.
* @returns A `Promise` that resolves when the operation is complete.
*/
async deleteFavoriteFoodItem(uuid: string): Promise<void> {
return deleteFavoriteFoodItem(uuid);
},
/**
* Retrieves meal logs within a specified date range.
* @param startDate - The start date of the range.
* @param endDate - The end date of the range.
* @returns A `Promise` resolving to an array of meal logs.
*/
async getMealLogs(startDate: Date, endDate: Date): Promise<FoodLog[]> {
return getMealLogs(startDate, endDate);
},
/**
* Saves a favorite food item.
* @param favoriteFoodItem - The favorite food item to save.
* @returns A `Promise` that resolves when the operation is complete.
*/
async saveFavoriteFoodItem(favoriteFoodItem: FavoriteFoodItem): Promise<void> {
return saveFavoriteFoodItem(favoriteFoodItem);
},
/**
* Retrieves favorite food items.
* @returns A `Promise` resolving to an array of favorite food items.
*/
async getFavoriteFoodItems(): Promise<FavoriteFoodItem[]> {
return getFavoriteFoodItems();
},
/**
* Saves a nutrition profile.
* @param nutritionProfile - The nutrition profile to save.
* @returns A `Promise` that resolves when the operation is complete.
*/
saveNutritionProfile(nutritionProfile: NutritionProfile): Promise<void> {
return saveNutritionProfile(nutritionProfile);
},
/**
* Saves a recipe.
* @param recipe - The recipe to save.
* @returns A `Promise` that resolves when the operation is complete.
*/
async saveRecipe(recipe: Recipe): Promise<void> {
return saveRecipe(recipe);
},
/**
* Retrieves recipes.
* @returns A `Promise` resolving to an array of recipes.
*/
async getRecipes(): Promise<Recipe[]> {
return getRecipes();
},
/**
* Retrieves the nutrition profile.
* @returns A `Promise` resolving to the nutrition profile, or `undefined` if not found.
*/
async getNutritionProfile(): Promise<NutritionProfile | undefined> {
return getNutritionProfile();
},
/**
* Retrieves water logs within a specified date range.
* @param startDate - The start date of the range.
* @param endDate - The end date of the range.
* @returns A `Promise` resolving to an array of water logs.
*/
async getWaters(startDate: Date, endDate: Date): Promise<Water[]> {
return getWaters(startDate, endDate);
},
/**
* Deletes a water log.
* @param uuid - The UUID of the water log to delete.
* @returns A `Promise` that resolves when the operation is complete.
*/
async deleteWater(uuid: string): Promise<void> {
return deleteWater(uuid);
},
/**
* Deletes a weight log.
* @param uuid - The UUID of the weight log to delete.
* @returns A `Promise` that resolves when the operation is complete.
*/
async deleteWeight(uuid: string): Promise<void> {
return deleteWeight(uuid);
},
/**
* Retrieves weight logs within a specified date range.
* @param startDate - The start date of the range.
* @param endDate - The end date of the range.
* @returns A `Promise` resolving to an array of weight logs.
*/
async getWeight(startDate: Date, endDate: Date): Promise<Weight[]> {
return getWeight(startDate, endDate);
},
};
Step: 2 If you're want to apply your theme, you can use the following, although it's in experimental mode. otherwise you can skip to next step
Nutrition-UX SDK also provide Branding into BrandingProvider.
// you can change primary color form here
export const branding: Branding = {
primaryColor: 'rgba(79, 70, 229, 1)',
backgroundColor: 'rgba(249, 250, 251, 1)',
black: 'rgba(0, 0, 0, 1)',
text: 'rgba(17, 24, 39, 1)',
border: 'rgba(229, 231, 235, 1)',
calories: 'rgba(245, 158, 11, 1)',
carbs: 'rgba(14, 165, 233, 1)',
error: 'rgba(239, 68, 68, 1)',
fat: 'rgba(139, 92, 246, 1)',
font: 'Passio-Regular',
gray300: 'rgba(209, 213, 219, 1)',
gray500: 'rgba(107, 114, 128, 1)',
indigo50: 'rgba(238, 242, 255, 1)',
proteins: 'rgba(16, 185, 129, 1)',
purple: 'rgba(79, 70, 229, 1)',
searchBody: 'rgba(242, 245, 251, 1)',
secondaryText: 'rgba(107, 114, 128, 1)',
white: 'white',
};
Step:3 : If you're want to check some log event then provide analytic service, although it's in experimental mode
export const analyticsService: AnalyticsService = {
logEvent(event: string) {
console.log(`Analytics: ${event}`); // eslint-disable-line no-console
},
};
import React from 'react';
import {
BrandingProvider,
NutritionNavigator,
ServicesProvider,
usePassioConfig
} from '@passiolife/nutrition-ai-ui-ux';
import { NavigationContainer } from '@react-navigation/native';
export default function App() {
const services: Services = {
dataService,
analyticsService,
};
const { isReady } = usePassioConfig({ key: "YOUR_PASSIO_KEY" });
if (!isReady) {
return <Loading />;
}
return (
<ServicesProvider services={services}>
<BrandingProvider branding={branding}>
<NavigationContainer>
<NutritionNavigator />
</NavigationContainer>
</BrandingProvider>
</ServicesProvider>
);
}
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import React from 'react';
import { enableScreens } from 'react-native-screens';
import { NavigationContainer } from '@react-navigation/native';
import { PassioScreens } from '@passiolife/nutrition-ai-ui-ux';
const Stack = createNativeStackNavigator();
enableScreens();
export const AppNavigator = () => {
return (
<>
<NavigationContainer>
<Stack.Navigator
screenOptions={{ gestureEnabled: false, animation: 'simple_push' }}
initialRouteName={'PassioScreens'}
<Stack.Screen
options={{ headerShown: false }}
name={'PassioScreens'}
component={PassioScreens}
/>
</Stack.Navigator>
</NavigationContainer>
</>
);
};
Callback | Argument | Return | Description |
---|---|---|---|
saveNutritionProfile | NutritionProfile | void | This function provides you NutritionProfile object for save nutrition profile |
saveFoodLog | FoodLog | void | This function provide you FoodLog for save food log |
saveFavoriteFoodItem | FavoriteFoodItem | void | This function provides you FavoriteFoodItem object for save favortie food item |
saveRecipe | Recipe | void | This function provides you Recipe object for save recipe |
deleteRecipe | uuID | void | This function provide you delete recipe uuid for delete recipe |
deleteFoodLog | uuID | void | This function provide you delete foodLog uuid for delete food log |
getNutritionProfile | - | NutritionProfile or undefined | You have to provide NutritionProfile or undefined to this funciton |
getFoodLogs | - | FoodLog | You have to provide FoodLog to this funciton |
getFavoriteFoodItems | - | FavoriteFoodItem[] | You have to provide FavoriteFoodItem[] to this funciton |
getMealLogs | startDate: Date, endDate: Date | FoodLog[] | You have to provide FoodLog[] between this start data and end date to this funciton |
getPatientProfile | void | PatientProfile | You have to provide PatientProfile to this funciton |
getRecipes | void | Recipe[] | You have to provide Recipe[] to this funciton |
To begin development, clone the project and check out the develop branch.
Create a new branch from develop for your assigned ticket with the format feature/my-ticket-#5
where my-ticket
is a few words describing the feature and #5
is the Github issue number. Please make sure you have moved the ticket to the "In Progress" column in Github.
As you develop your feature, run the example app to test and debug your code.
Once your work is complete, verify that you have met all acceptance criteria on the ticket and have sufficient tests to cover the behavior. Then you may create a pull request back to the develop branch which will be reviewed and subsequently approved and merged.
If your project not runnable in IOS then follow below steps
- removed podfile.lock
- remove pods
- remove node_modules in example
- remove node_modules in root
- remove derived data
- remove library cache
- restart system
- yarn at root
- open xcode
If you find a duplicate entry for '@react-navigation', ensure that the navigation dependencies are listed above the other '@react-navigation' dependencies
To resolve the issue, you can use the following resolutions in your package.json:
"resolutions": {
"@types/react": "18.0.12",
"react-native": "0.68.1",
"react-native-reanimated": "3.6.1",
"@react-native-community/slider": "4.5.0",
"react-native-safe-area-context": "4.8.2",
...others
}
Even after all the permissions are correct in Android, there is one last thing to make sure this libray is working fine on Android. Please make sure the device has Google Speech Recognizing Engine such as com.google.android.googlequicksearchbox by calling Voice.getSpeechRecognitionServices(). Since Android phones can be configured with so many options, even if a device has googlequicksearchbox engine, it could be configured to use other services. You can check which serivce is used for Voice Assistive App in following steps for most Android phones:
Settings > App Management > Default App > Assistive App and Voice Input > Assistive App
Above flow can vary depending on the Android models and manufactures. For Huawei phones, there might be a chance that the device cannot install Google Services.
How can I get com.google.android.googlequicksearchbox in the device?
Please ask users to install Google Search App.
If you pod or build not sync please add @react-native-async-storage/async-storage
yarn add @react-native-async-storage/async-storage
export interface FoodLog extends ServingInfo {
name: string;
uuid: string;
passioID: PassioID;
refCode?: string;
eventTimestamp: string;
isOpenFood?: boolean;
longName?: string;
meal: MealLabel;
imageName: string;
entityType: PassioIDEntityType | 'user-recipe';
foodItems: FoodItem[];
}
export interface ServingInfo {
selectedUnit: string;
selectedQuantity: number;
servingSizes: ServingSize[];
servingUnits: ServingUnit[];
computedWeight?: ComputedWeight;
}
export interface FoodItem extends ServingInfo {
passioID: PassioID;
name: string;
imageName: string;
entityType: PassioIDEntityType;
computedWeight: ComputedWeight;
ingredientsDescription?: string;
barcode?: string;
nutrients: Nutrient[];
}
export interface Nutrient {
id: NutrientType;
amount: number;
unit: string;
}
export interface NutritionProfile {
caloriesTarget: number;
carbsPercentage: number;
proteinPercentage: number;
fatPercentage: number;
unitLength: UnitSystem;
unitsWeight: UnitSystem;
gender: 'male' | 'female';
height: number;
age: number;
weight: number;
activityLevel: ActivityLevelType;
diet?: DietType;
name: string;
caloriesDeficit?: CaloriesDeficit;
targetWater?: number;
targetWeight?: number;
breakFastNotification?: boolean;
dinnerNotification?: boolean;
lunchNotification?: boolean;
mealPlan?: string;
}