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 Colors from '../constants/Colors'; 16import { useResolvedValue } from '../utilities/useResolvedValue'; 17 18export default function SecureStoreScreen() { 19 const [isAvailable, error] = useResolvedValue(SecureStore.isAvailableAsync); 20 21 const warning = React.useMemo(() => { 22 if (error) { 23 return `An unknown error occurred while checking the API availability: ${error.message}`; 24 } else if (isAvailable === null) { 25 return 'Checking availability...'; 26 } else if (isAvailable === false) { 27 return 'SecureStore API is not available on this platform.'; 28 } 29 return null; 30 }, [error, isAvailable]); 31 32 if (warning) { 33 return ( 34 <View style={{ justifyContent: 'center', alignItems: 'center', flex: 1 }}> 35 <Text>{warning}</Text> 36 </View> 37 ); 38 } 39 40 return <SecureStoreView />; 41} 42 43function SecureStoreView() { 44 const [key, setKey] = React.useState<string | undefined>(); 45 const [value, setValue] = React.useState<string | undefined>(); 46 const [service, setService] = React.useState<string | undefined>(); 47 const [requireAuth, setRequireAuth] = React.useState<boolean | undefined>(); 48 49 const _toggleAuth = async () => { 50 setRequireAuth(!requireAuth); 51 }; 52 53 const _setValue = async (value: string, key: string) => { 54 try { 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 placeholderTextColor={Colors.secondaryText} 98 value={value} 99 onChangeText={setValue} 100 /> 101 <TextInput 102 style={styles.textInput} 103 placeholder="Enter a key for the value (ex. password)" 104 placeholderTextColor={Colors.secondaryText} 105 value={key} 106 onChangeText={setKey} 107 /> 108 <TextInput 109 style={styles.textInput} 110 placeholder="Enter a service name (may be blank)" 111 placeholderTextColor={Colors.secondaryText} 112 value={service} 113 onChangeText={setService} 114 /> 115 <View style={styles.authToggleContainer}> 116 <Text>Requires authentication:</Text> 117 <Switch value={requireAuth} onValueChange={_toggleAuth} /> 118 </View> 119 {value && key && ( 120 <ListButton onPress={() => _setValue(value, key)} title="Store value with key" /> 121 )} 122 {key && <ListButton onPress={() => _getValue(key)} title="Get value with key" />} 123 {key && <ListButton onPress={() => _deleteValue(key)} title="Delete value with key" />} 124 </ScrollView> 125 ); 126} 127 128SecureStoreScreen.navigationOptions = { 129 title: 'SecureStore', 130}; 131 132const styles = StyleSheet.create({ 133 container: { 134 flex: 1, 135 padding: 10, 136 }, 137 textInput: { 138 marginBottom: 10, 139 padding: 10, 140 height: 40, 141 ...Platform.select({ 142 ios: { 143 borderColor: '#ccc', 144 borderWidth: 1, 145 borderRadius: 3, 146 }, 147 }), 148 }, 149 authToggleContainer: { 150 flexDirection: 'row', 151 justifyContent: 'space-between', 152 alignItems: 'center', 153 }, 154}); 155