The app is integrated with Amazon cognito client for authentication, and has a few functions that get invoked onPress of a Pressable. I used this: “java -jar bundletool.jar build-apks –bundle=filename.aab –output=newfilename.apks –mode=universal –ks filetomykey –ks-alias _____…” to bundle my aab which I downloaded on expo.dev after building using “eas build –platform android” and I managed get and install apk in my andriod phone.
The problem is I have is that the Pressable and some functions do not work on my phone. I have function that perfoms some logic to retrieve some value from the tokenId used with cognito client. What the function does is: use SecureStore to get desired value from the tokenId and then returns an object. That same function is then used to Great user with their name which is retrieved from the tokenId. This functionality is not working on my phone (Oppo A15 Android v10) and the Pressable does not work also, but they were working when using the Expo Go app to test my app.
I am able sign in successfuly, so congito client function is working, but It seems like there is a problem when it comes to getting data from the tokenId, because now on my phone, The username of the logged in user does not display, it only displays the useState(“Name here”) value instead of the one set. Also I dont get any feedback when touching the Pressable on my phone screen. But all of this works with Expo goScreenshots of my code another screenshotanother screenshotanother screenshot
Here is the code that retrieves the token from SecureStore
import {jwtDecode} from 'jwt-decode'; // Ensure correct import
import * as SecureStore from 'expo-secure-store'; // Ensure you have expo-secure-store installed
import axios from 'axios';
interface User {
id: number;
username: string;
}
interface DecodedToken {
given_name: string;
family_name: string;
}
const apiUrl = 'http://1.1.1.1'; //dummy url
const getUserNameFromToken = async (): Promise<string | null> => {
try {
// Retrieve the stored token
const token = await SecureStore.getItemAsync('idToken');
console.log('Retrieved Token:', token);
if (token) {
const decodedToken = jwtDecode<DecodedToken>(token);
console.log('Decoded Token:', decodedToken);
if (decodedToken.given_name && decodedToken.family_name) {
const firstName = decodedToken.given_name;
const lastName = decodedToken.family_name;
const userName = `${firstName} ${lastName}`;
return userName;
} else {
console.error('Token does not contain given_name or family_name');
return null;
}
} else {
console.error('Token is undefined');
return null;
}
} catch (error) {
console.error('Failed to retrieve or decode token', error);
return null;
}
};
const fetchUsers = async (): Promise<User[]> => {
try {
const response = await axios.get(`${apiUrl}/users`);
return response.data;
} catch (error) {
console.error('Failed to fetch users:', error);
return [];
}
};
export const getMatchedUser = async (): Promise<User | null> => {
try {
const userName = await getUserNameFromToken();
if (!userName) {
return null;
}
const users = await fetchUsers();
const matchedUser = users.find((user) => user.username === userName);
return matchedUser || null;
} catch (error) {
console.error('Error getting matched user:', error);
return null;
}
};
And here is my when user gets authorized, signsUp, and confirms account
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import { CognitoIdentityProviderClient, InitiateAuthCommand, SignUpCommand, ConfirmSignUpCommand } from "@aws-sdk/client-cognito-identity-provider";
import config from "../../config.json"
import * as SecureStore from 'expo-secure-store';
import 'react-native-get-random-values';
import 'react-native-url-polyfill/auto';
import { ReadableStream } from "web-streams-polyfill";
globalThis.ReadableStream = ReadableStream;
import { v4 as uuidv4 } from "uuid";
const readable = new ReadableStream();
export const cognitoClient = new CognitoIdentityProviderClient({
region: config.region,
});
export const signIn = async (username: string, password: string) => {
const params = {
AuthFlow: "USER_PASSWORD_AUTH",
ClientId: config.clientId,
AuthParameters: {
USERNAME: username,
PASSWORD: password,
},
};
try {
const command = new InitiateAuthCommand(params);
const { AuthenticationResult } = await cognitoClient.send(command);
if (AuthenticationResult) {
await SecureStore.setItemAsync("idToken", AuthenticationResult.IdToken || '');
await SecureStore.setItemAsync("accessToken", AuthenticationResult.AccessToken || '');
await SecureStore.setItemAsync("refreshToken", AuthenticationResult.RefreshToken || '');
return AuthenticationResult;
}
} catch (error) {
console.error("Error signing in: ", error);
throw error;
}
};
export const signUp = async (username: string, fName: string, lName: string, email: string, password: string) => {
const params = {
ClientId: config.clientId,
Username: username,
Password: password,
UserAttributes: [
{
Name: "email",
Value: email,
},
{
Name: "given_name",
Value: fName,
},{
Name: "family_name",
Value: lName,
},
],
};
try {
console.log("Params: ",params)
const command = new SignUpCommand(params);
console.log("Command: ",command)
const response = await cognitoClient.send(command);
console.log("Sign up success: ", response);
return response;
} catch (error) {
console.error("Error signing up: ", error);
throw error;
}
};
export const confirmSignUp = async (username: string, code: string) => {
const params = {
ClientId: config.clientId,
Username: username,
ConfirmationCode: code,
};
try {
const command = new ConfirmSignUpCommand(params);
await cognitoClient.send(command);
console.log("User confirmed successfully");
return true;
} catch (error) {
console.error("Error confirming sign up: ", error);
throw error;
}
};
This is when I do Post using Axios (there are three other similar functions, difference is endoints and alert messages):
import axios from 'axios';
const apiUrl = 'http://1.1.1.1:4000/check-in-morning';
const checkInMorning = async (userId) => {
try {
const response = await axios.post(apiUrl, {
data: { userId: userId }
}, {
headers: {
'accept': '*/*',
'Content-Type': 'application/json'
}
});
console.log('Response:', response.data);
return response.data;
} catch (error) {
console.error('Error:', error);
throw error;
}
};
export default checkInMorning;
SignUp page:
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import { CognitoIdentityProviderClient, InitiateAuthCommand, SignUpCommand, ConfirmSignUpCommand } from "@aws-sdk/client-cognito-identity-provider";
import config from "../../config.json"
import * as SecureStore from 'expo-secure-store';
import 'react-native-get-random-values';
import 'react-native-url-polyfill/auto';
import { ReadableStream } from "web-streams-polyfill";
globalThis.ReadableStream = ReadableStream;
import { v4 as uuidv4 } from "uuid";
const readable = new ReadableStream();
export const cognitoClient = new CognitoIdentityProviderClient({
region: config.region,
});
export const signIn = async (username: string, password: string) => {
const params = {
AuthFlow: "USER_PASSWORD_AUTH",
ClientId: config.clientId,
AuthParameters: {
USERNAME: username,
PASSWORD: password,
},
};
try {
const command = new InitiateAuthCommand(params);
const { AuthenticationResult } = await cognitoClient.send(command);
if (AuthenticationResult) {
await SecureStore.setItemAsync("idToken", AuthenticationResult.IdToken || '');
await SecureStore.setItemAsync("accessToken", AuthenticationResult.AccessToken || '');
await SecureStore.setItemAsync("refreshToken", AuthenticationResult.RefreshToken || '');
return AuthenticationResult;
}
} catch (error) {
console.error("Error signing in: ", error);
throw error;
}
};
export const signUp = async (username: string, fName: string, lName: string, email: string, password: string) => {
const params = {
ClientId: config.clientId,
Username: username,
Password: password,
UserAttributes: [
{
Name: "email",
Value: email,
},
{
Name: "given_name",
Value: fName,
},{
Name: "family_name",
Value: lName,
},
],
};
try {
console.log("Params: ",params)
const command = new SignUpCommand(params);
console.log("Command: ",command)
const response = await cognitoClient.send(command);
console.log("Sign up success: ", response);
return response;
} catch (error) {
console.error("Error signing up: ", error);
throw error;
}
};
export const confirmSignUp = async (username: string, code: string) => {
const params = {
ClientId: config.clientId,
Username: username,
ConfirmationCode: code,
};
try {
const command = new ConfirmSignUpCommand(params);
await cognitoClient.send(command);
console.log("User confirmed successfully");
return true;
} catch (error) {
console.error("Error confirming sign up: ", error);
throw error;
}
};
SignIn page:
import React from "react";
import { View, Text, Image, ScrollView, Alert } from "react-native";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { SafeAreaView } from "react-native-safe-area-context";
import { useForm, Controller } from "react-hook-form";
import { images } from "../../constants";
import FormField from "../../components/FormField";
import CustomButton from "../../components/CustomButton";
import { Link, router } from "expo-router";
import { signIn } from "../services/authService";
import * as SecureStore from "expo-secure-store";
import "react-native-get-random-values";
import { v4 as uuidv4 } from "uuid";
const SignIn = () => {
const {
control,
handleSubmit,
formState: { errors },
} = useForm();
const onSubmit = async (data) => {
try {
const session = await signIn(data.username, data.password);
console.log("Sign in successful", session);
if (session && session.AccessToken) {
await SecureStore.setItemAsync("accessToken", session.AccessToken);
// Retrieve the token to check if it's stored correctly
const storedToken = await SecureStore.getItemAsync("accessToken");
if (storedToken) {
router.push("../(tabs)/home");
} else {
console.error("Session token was not set properly.");
}
} else {
console.error("SignIn session or AccessToken is undefined.");
}
} catch (error) {
console.error("Sign in failed:", error);
alert("Sign in failed. Please try again.");
}
};
return (
<GestureHandlerRootView>
<SafeAreaView className="bg-primary h-full">
<View className="w-full justify-center px-4">
<Text className="text-white text-2xl mt-10 mb-10 font-psemibold">
Log in to{" "}
<Text className="text-secondary-200">app name</Text>
</Text>
</View>
<ScrollView contentContainerStyle={{ height: "90%" }}>
<View className="w-full justify-center min-h-[8vh] px-4 my-6">
<Controller
control={control}
render={({ field }) => (
<FormField
title="Username"
placeholder="Enter your username"
value={field.value}
handleChangeText={field.onChange}
fieldName="username"
otherStyles="mt-7"
errorMessage={errors.username && errors.username.message} // Pass error message
/>
)}
name="username"
rules={{
required: "Username is required",
pattern: {
value: /^[A-Z][a-z]+_[A-Z][a-z]+$/,
message:
"Username must be in the format 'Firstname_Lastname'",
},
}}
/>
<Controller
className="mt-7"
control={control}
render={({ field }) => (
<FormField
title="Password"
placeholder="Enter your password"
value={field.value}
handleChangeText={field.onChange}
otherStyles="mt-7"
// secureTextEntry={title === 'Password' && !showPassword}
errorMessage={errors.password && errors.password.message} // Pass error message
/>
)}
name="password"
rules={{
required: "Password is required",
minLength: {
value: 8,
message: "Password must be at least 8 characters long",
},
pattern: {
value: /^(?=.*d)(?=.*[a-zA-Z])(?=.*[W_]).{8,}$/,
message:
"Password must contain at least one letter, one number, and one special character",
},
}}
/>
<CustomButton
title="Sign In"
handlePress={handleSubmit(onSubmit)}
containerStyles={"mt-7"}
/>
<View className="justify-center pt-5 flex-row gap-2">
<Text className="text-lg text-gray-100 ">
Don't have account?
</Text>
<Link
href="/sign-up"
className="text-lg text-secondary font-psemibold"
>
Sign Up
</Link>
</View>
</View>
</ScrollView>
</SafeAreaView>
</GestureHandlerRootView>
);
};
export default SignIn;
Confirm signUp:
import React from "react";
import { View, Text, Image, ScrollView, Alert } from "react-native";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { SafeAreaView } from "react-native-safe-area-context";
import { useForm, Controller } from "react-hook-form";
import { images } from "../../constants";
import FormField from "../../components/FormField";
import CustomButton from "../../components/CustomButton";
import { Link, router } from "expo-router";
import { confirmSignUp } from "../services/authService";
const ConfirmPage = () => {
const usernamePattern = /^[A-Z][a-z]+_[A-Z][a-z]+$/;
const {
control,
handleSubmit,
formState: { errors },
} = useForm();
const onSubmit = async (data) => {
if (!usernamePattern.test(data.username)) {
alert("Please enter your first name and last name separated by an underscore (e.g., Joe_Doe)");
return;
}
try {
await confirmSignUp(data.username, data.code);
alert(`Account confirmed successfully!nSign in on next page.`);
router.push('./sign-in');
} catch (error) {
alert(`Failed to confirm account: ${error} Email:${ String(data.email)} Code:${String(data.code)}`);
}
};
return (
<GestureHandlerRootView>
<SafeAreaView className="bg-primary h-full">
<ScrollView>
<View className="w-full justify-center min-h-[85vh] px-4 my-6">
<Text className="text-white text-2xl font-psemibold">
Account verification
</Text>
<Controller
control={control}
render={({ field }) => (
<FormField
title="Username"
placeholder="Enter your username"
value={field.value}
handleChangeText={field.onChange}
fieldName="username"
otherStyles="mt-7"
errorMessage={errors.username && errors.username.message} // Pass error message
/>
)}
name="username"
rules={{
required: "Username is required",
pattern: {
value: /^[A-Z][a-z]+_[A-Z][a-z]+$/,
message:
"Username must be in the format 'Firstname_Lastname'",
},
}}
/>
<Controller
className="mt-7"
control={control}
render={({ field }) => (
<FormField
title="Code"
placeholder="Enter your code"
value={field.value}
handleChangeText={field.onChange}
otherStyles="mt-7"
errorMessage={errors.code && errors.code.message}
/>
)}
name="code"
rules={{
required: "Code is required",
maxLength: {
value: 10,
message: "Code must be no more than 10 digits long",
},
pattern: {
value: /^d{1,10}$/,
message: "Code must be a valid number up to 10 digits",
},
}}
/>
<CustomButton
title="Confirm"
handlePress={handleSubmit(onSubmit)}
containerStyles={"mt-7"}
/>
</View>
</ScrollView>
</SafeAreaView>
</GestureHandlerRootView>
);
};
export default ConfirmPage;
Home page (where I cannot press Pressable, or it’s pressable but the onPress functions do not get invoked):
import React, { useState, useEffect } from "react";
import { Text, View, Pressable } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { getMatchedUser } from "../services/authUtils";
import checkInMorning from '../activities/morning'
import checkInLunch from '../activities/backLunch'
import checkOutAfternoon from '../activities/afternoon'
import checkOutLunch from "../activities/outLunch";
export default function Home() {
const [userName, setUserName] = useState("Username Here");
const [matchedUser, setMatchedUser] = useState(null);
useEffect(() => {
const fetchMatchedUser = async () => {
const user = await getMatchedUser();
if (user) {
setUserName(user.username);
setMatchedUser(user);
} else {
setUserName("No matching user found");
setMatchedUser(null);
}
};
fetchMatchedUser();
}, []);
const handleCheckInMorning = async () => {
try {
const result = await checkInMorning(matchedUser.id);
console.log('Check-in successful:', result);
alert("Check-in Successful");
} catch (error) {
console.error('Check-in failed:', error);
}
};
const handleCheckInLunch = async () => {
try {
const result = await checkInLunch(matchedUser.id);
console.log('Check-in successful:', result);
alert("Check-in Successful");
} catch (error) {
console.error('Check-in failed:', error);
}
};
const handleCheckOutAfternoon = async () => {
try {
const result = await checkOutAfternoon(matchedUser.id);
console.log('Check-out successful:', result);
alert("Check-Out Successful");
} catch (error) {
console.error('Check-in failed:', error);
}
};
const handleCheckOutLunch = async () => {
try {
const result = await checkOutLunch(matchedUser.id);
console.log('Check-out successful:', result);
alert("Check-Out Successful");
} catch (error) {
console.error('Check-in failed:', error);
}
};
return (
<GestureHandlerRootView>
<SafeAreaView className="bg-[#d9d9d9] h-full py-10">
<View className="justify-center items-center">
<Text>Hi {userName}</Text>
{matchedUser ? (
<Text>
Welcome, {matchedUser.username} (ID: {matchedUser.id})
</Text>
) : (
<Text>No matching user found</Text>
)}
<Pressable className="w-[200] h-20 m-10 bg-secondary justify-center items-center " onPressOut={handleCheckInMorning}>
<Text className="text-white font-psemibold">Check In morning</Text>
</Pressable>
<Pressable className="w-[200] h-20 m-10 bg-secondary justify-center items-center " onPressOut={handleCheckInLunch}>
<Text className="text-white font-psemibold">Check In back from lunch</Text>
</Pressable>
<Pressable className="w-[200] h-20 m-10 bg-secondary justify-center items-center " onPressOut={handleCheckOutLunch}>
<Text className="text-white font-psemibold">Check Out for lunch</Text>
</Pressable>
<Pressable className="w-[200] h-20 m-10 bg-secondary justify-center items-center " onPressOut={handleCheckOutAfternoon}>
<Text className="text-white font-psemibold">Check Out afternoon</Text>
</Pressable>
</View>
</SafeAreaView>
</GestureHandlerRootView>
);
}
My package file:
{
"name": "app-name",
"version": "1.0.0",
"main": "expo-router/entry",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web"
},
"dependencies": {
"@aws-amplify/backend": "^1.0.1",
"@aws-amplify/backend-cli": "^1.0.2",
"@aws-amplify/react-native": "^1.1.0",
"@aws-amplify/ui-react-native": "^2.2.0",
"@aws-sdk/client-cognito-identity-provider": "^3.533.0",
"@babel/plugin-transform-async-generator-functions": "^7.24.3",
"@babel/plugin-transform-class-properties": "^7.24.1",
"@babel/plugin-transform-logical-assignment-operators": "^7.24.1",
"@babel/plugin-transform-nullish-coalescing-operator": "^7.24.1",
"@babel/plugin-transform-numeric-separator": "^7.24.1",
"@babel/plugin-transform-object-rest-spread": "^7.24.5",
"@babel/plugin-transform-optional-chaining": "^7.24.5",
"@react-native-async-storage/async-storage": "^1.23.1",
"@react-native-community/geolocation": "^3.2.1",
"@react-native-community/netinfo": "^11.3.1",
"@types/react": "~18.2.45",
"aws-amplify": "^6.3.0",
"aws-cdk": "^2.141.0",
"aws-cdk-lib": "^2.141.0",
"axios": "^1.6.8",
"constructs": "^10.3.0",
"core-js": "^3.37.0",
"esbuild": "^0.21.2",
"expo": "~51.0.2",
"expo-av": "~14.0.4",
"expo-constants": "~16.0.1",
"expo-device": "~6.0.2",
"expo-document-picker": "~12.0.1",
"expo-font": "~12.0.5",
"expo-linking": "~6.3.1",
"expo-location": "~17.0.1",
"expo-router": "^3.5.11",
"expo-secure-store": "~13.0.1",
"expo-status-bar": "~1.12.1",
"install": "^0.13.0",
"jwt-decode": "^4.0.0",
"nativewind": "^2.0.11",
"react": "18.2.0",
"react-hook-form": "^7.51.4",
"react-native": "^0.74.1",
"react-native-animatable": "^1.4.0",
"react-native-appwrite": "^0.2.2",
"react-native-gesture-handler": "~2.16.1",
"react-native-get-random-values": "^1.11.0",
"react-native-modalize": "^2.1.1",
"react-native-reanimated": "~3.10.1",
"react-native-safe-area-context": "^4.10.1",
"react-native-screens": "^3.31.1",
"react-native-url-polyfill": "^2.0.0",
"react-native-webview": "13.8.6",
"tsx": "^4.10.1",
"typescript": "~5.3.3",
"uuid": "^9.0.1",
"web-streams-polyfill": "^4.0.0"
},
"devDependencies": {
"@babel/core": "^7.24.3",
"tailwindcss": "3.3.2"
},
"private": true
}
App.json:
{
"expo": {
"name": "app_name",
"slug": "snug_name",
"version": "1.0.1",
"orientation": "portrait",
"icon": "./assets/icon2.png",
"scheme": "myapp",
"userInterfaceStyle": "automatic",
"splash": {
"image": "./assets/splash2.1.png",
"resizeMode": "contain",
"backgroundColor": "#276966"
},
"ios": {
"supportsTablet": true,
"config": {
"usesNonExemptEncryption": false
}
},
"android": {
"package": "com.package.name",
"versionCode": 2,
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive_icon.png",
"backgroundColor": "#ffffff"
},
"permissions": [
"android.permission.ACCESS_COARSE_LOCATION",
"android.permission.ACCESS_FINE_LOCATION"
]
},
"web": {
"bundler": "metro",
"output": "static",
"favicon": "./assets/images/favicon.png"
},
"plugins": [
"expo-router",
[
"expo-secure-store",
{
"faceIDPermission": "Allow $(PRODUCT_NAME) to access your Face ID biometric data."
}
],
[
"expo-location",
{
"locationAlwaysAndWhenInUsePermission": "Allow $(PRODUCT_NAME) to use your location."
}
]
],
"experiments": {
"typedRoutes": true
},
"extra": {
"router": {
"origin": false
},
"eas": {
"projectId": "myprojectIdhere"
}
}
}
}
All of these work when I run my app on Expo Go but I face some problems on the apk
I tried running app on Expo Go again to confirm if built the updated version. And Everything works as expected on the Expo Go app, but on not with the installed apk
SignIn page is similar to signUp