1import * as SecureStore from 'expo-secure-store'; 2import * as React from 'react'; 3import { 4 Alert, 5 Platform, 6 ScrollView, 7 Text, 8 TextInput, 9 View, 10 Switch, 11 StyleSheet, 12} from 'react-native'; 13 14import ListButton from '../components/ListButton'; 15import { useResolvedValue } from '../utilities/useResolvedValue'; 16 17export default function SecureStoreScreen() { 18 const [isAvailable, error] = useResolvedValue(SecureStore.isAvailableAsync); 19 20 const warning = React.useMemo(() => { 21 if (error) { 22 return `An unknown error occurred while checking the API availability: ${error.message}`; 23 } else if (isAvailable === null) { 24 return 'Checking availability...'; 25 } else if (isAvailable === false) { 26 return 'SecureStore API is not available on this platform.'; 27 } 28 return null; 29 }, [error, isAvailable]); 30 31 if (warning) { 32 return ( 33 <View style={{ justifyContent: 'center', alignItems: 'center', flex: 1 }}> 34 <Text>{warning}</Text> 35 </View> 36 ); 37 } 38 39 return <SecureStoreView />; 40} 41 42function SecureStoreView() { 43 const [key, setKey] = React.useState<string | undefined>(); 44 const [value, setValue] = React.useState<string | undefined>(); 45 const [service, setService] = React.useState<string | undefined>(); 46 const [requireAuth, setRequireAuth] = React.useState<boolean | undefined>(); 47 48 const _toggleAuth = async () => { 49 setRequireAuth(!requireAuth); 50 }; 51 52 const _setValue = async (value: string, key: string) => { 53 try { 54 console.log('SecureStore: ' + SecureStore); 55 await SecureStore.setItemAsync(key, value, { 56 keychainService: service, 57 requireAuthentication: requireAuth, 58 authenticationPrompt: 'Authenticate', 59 }); 60 Alert.alert('Success!', 'Value: ' + value + ', stored successfully for key: ' + key, [ 61 { text: 'OK', onPress: () => {} }, 62 ]); 63 } catch (e) { 64 Alert.alert('Error!', e.message, [{ text: 'OK', onPress: () => {} }]); 65 } 66 }; 67 68 const _getValue = async (key: string) => { 69 try { 70 const fetchedValue = await SecureStore.getItemAsync(key, { 71 keychainService: service, 72 requireAuthentication: requireAuth, 73 authenticationPrompt: 'Authenticate', 74 }); 75 Alert.alert('Success!', 'Fetched value: ' + fetchedValue, [ 76 { text: 'OK', onPress: () => {} }, 77 ]); 78 } catch (e) { 79 Alert.alert('Error!', e.message, [{ text: 'OK', onPress: () => {} }]); 80 } 81 }; 82 83 const _deleteValue = async (key: string) => { 84 try { 85 await SecureStore.deleteItemAsync(key, { keychainService: service }); 86 Alert.alert('Success!', 'Value deleted', [{ text: 'OK', onPress: () => {} }]); 87 } catch (e) { 88 Alert.alert('Error!', e.message, [{ text: 'OK', onPress: () => {} }]); 89 } 90 }; 91 92 return ( 93 <ScrollView style={styles.container}> 94 <TextInput 95 style={styles.textInput} 96 placeholder="Enter a value to store (ex. pw123!)" 97 value={value} 98 onChangeText={setValue} 99 /> 100 <TextInput 101 style={styles.textInput} 102 placeholder="Enter a key for the value (ex. password)" 103 value={key} 104 onChangeText={setKey} 105 /> 106 <TextInput 107 style={styles.textInput} 108 placeholder="Enter a service name (may be blank)" 109 value={service} 110 onChangeText={setService} 111 /> 112 <View style={styles.authToggleContainer}> 113 <Text>Requires authentication:</Text> 114 <Switch value={requireAuth} onValueChange={_toggleAuth} /> 115 </View> 116 {value && key && ( 117 <ListButton onPress={() => _setValue(value, key)} title="Store value with key" /> 118 )} 119 {key && <ListButton onPress={() => _getValue(key)} title="Get value with key" />} 120 {key && <ListButton onPress={() => _deleteValue(key)} title="Delete value with key" />} 121 </ScrollView> 122 ); 123} 124 125SecureStoreScreen.navigationOptions = { 126 title: 'SecureStore', 127}; 128 129const styles = StyleSheet.create({ 130 container: { 131 flex: 1, 132 padding: 10, 133 }, 134 textInput: { 135 marginBottom: 10, 136 padding: 10, 137 height: 40, 138 ...Platform.select({ 139 ios: { 140 borderColor: '#ccc', 141 borderWidth: 1, 142 borderRadius: 3, 143 }, 144 }), 145 }, 146 authToggleContainer: { 147 flexDirection: 'row', 148 justifyContent: 'space-between', 149 alignItems: 'center', 150 }, 151}); 152