├── package.json ├── .gitignore ├── LICENSE ├── pin-code-style.js ├── CHANGELOG.md ├── yarn.lock ├── pin-code.js └── README.md /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-pin-code", 3 | "version": "0.5.4-beta.0", 4 | "description": "A simple pin code component for react-native", 5 | "main": "pin-code.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/gkueny/react-native-pin-code.git" 12 | }, 13 | "keywords": [ 14 | "react-native", 15 | "pin-code", 16 | "pin", 17 | "code" 18 | ], 19 | "author": "gkueny", 20 | "license": "ISC", 21 | "bugs": { 22 | "url": "https://github.com/gkueny/react-native-pin-code/issues" 23 | }, 24 | "peerDependencies": { 25 | "react": ">=15.4.0", 26 | "react-native": ">=0.40" 27 | }, 28 | "homepage": "https://github.com/gkueny/react-native-pin-code#readme", 29 | "dependencies": { 30 | "prop-types": "^15.7.2" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | npm-debug.log 37 | yarn-error.log 38 | 39 | # BUCK 40 | buck-out/ 41 | \.buckd/ 42 | *.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 50 | 51 | fastlane/report.xml 52 | fastlane/Preview.html 53 | fastlane/screenshots 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Gaëtan Kueny 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /pin-code-style.js: -------------------------------------------------------------------------------- 1 | import {Dimensions, StyleSheet} from 'react-native'; 2 | 3 | const {height, width} = Dimensions.get('window'); 4 | 5 | export const codePinStyles = StyleSheet.create({ 6 | container: { 7 | height : 150, 8 | width : width - 30, 9 | backgroundColor : '#FFF' 10 | }, 11 | containerPin: { 12 | width : width - 30, 13 | height : 40, 14 | flexDirection : 'row', 15 | justifyContent : 'space-around', 16 | alignItems : 'center', 17 | marginTop : 20, 18 | }, 19 | pin: { 20 | backgroundColor : '#F0F0F0', 21 | textAlign : 'center', 22 | flex : 1, 23 | marginLeft : 20, 24 | marginRight : 20, 25 | borderRadius : 5, 26 | shadowColor : '#000000', 27 | shadowOffset : { 28 | width : 1, 29 | height : 1 30 | }, 31 | shadowRadius : 5, 32 | shadowOpacity : 0.4 33 | }, 34 | text: { 35 | textAlign : 'center', 36 | color : 'gray', 37 | fontSize : 20, 38 | marginTop : 30 39 | }, 40 | error: { 41 | textAlign : 'center', 42 | color : 'red', 43 | paddingTop : 10 44 | } 45 | }); 46 | 47 | 48 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 7 | 8 | ## [v0.5.3] - 2018-10-01 9 | 10 | ### Fixed 11 | 12 | - Fix clean inputs (see [#22](https://github.com/gkueny/react-native-pin-code/issues/22)) 13 | 14 | ## [v0.5.1] - 2018-09-09 15 | 16 | ### Fixed 17 | 18 | - Fix clear input fields on success (see [#16](https://github.com/gkueny/react-native-pin-code/issues/16)) 19 | - Fix focus 20 | 21 | ## [v0.5.0] - 2018-09-03 22 | 23 | ### Added 24 | 25 | - Supports auto-fill and past of codes. 26 | 27 | ### Fixed 28 | 29 | - Fixed crash which occurs when using iOS 12's security code auto-fill feature. 30 | - Corrected Typos in Readme/Changelog 31 | 32 | ## [v0.4.1] - 2018-02-09 33 | 34 | ### Fixed 35 | 36 | - allow to not pass `code` prop. 37 | 38 | ## [v0.4.0] - 2018-02-09 39 | 40 | ### Added 41 | 42 | - handle backspace event 43 | 44 | ## [v0.3.0] - 2018-02-04 45 | 46 | ### Added 47 | 48 | - New prop `checkPinCode`(optional). Called to check pin code (`code` prop is not used if `checkPinCode` prop is present) 49 | - New prop `obfuscation` (optional). Obfuscate code if set to true 50 | 51 | ### Changed 52 | 53 | - Update Readme to add simple example. 54 | 55 | ### Fixed 56 | 57 | - Use `code.length` if `number` prop is not used. 58 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | asap@~2.0.3: 6 | version "2.0.5" 7 | resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f" 8 | 9 | core-js@^1.0.0: 10 | version "1.2.7" 11 | resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" 12 | 13 | encoding@^0.1.11: 14 | version "0.1.12" 15 | resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" 16 | dependencies: 17 | iconv-lite "~0.4.13" 18 | 19 | fbjs@^0.8.9: 20 | version "0.8.12" 21 | resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.12.tgz#10b5d92f76d45575fd63a217d4ea02bea2f8ed04" 22 | dependencies: 23 | core-js "^1.0.0" 24 | isomorphic-fetch "^2.1.1" 25 | loose-envify "^1.0.0" 26 | object-assign "^4.1.0" 27 | promise "^7.1.1" 28 | setimmediate "^1.0.5" 29 | ua-parser-js "^0.7.9" 30 | 31 | iconv-lite@~0.4.13: 32 | version "0.4.17" 33 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.17.tgz#4fdaa3b38acbc2c031b045d0edcdfe1ecab18c8d" 34 | 35 | is-stream@^1.0.1: 36 | version "1.1.0" 37 | resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" 38 | 39 | isomorphic-fetch@^2.1.1: 40 | version "2.2.1" 41 | resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" 42 | dependencies: 43 | node-fetch "^1.0.1" 44 | whatwg-fetch ">=0.10.0" 45 | 46 | js-tokens@^3.0.0: 47 | version "3.0.1" 48 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7" 49 | 50 | loose-envify@^1.0.0, loose-envify@^1.3.1: 51 | version "1.3.1" 52 | resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" 53 | dependencies: 54 | js-tokens "^3.0.0" 55 | 56 | node-fetch@^1.0.1: 57 | version "1.7.0" 58 | resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.0.tgz#3ff6c56544f9b7fb00682338bb55ee6f54a8a0ef" 59 | dependencies: 60 | encoding "^0.1.11" 61 | is-stream "^1.0.1" 62 | 63 | object-assign@^4.1.0: 64 | version "4.1.1" 65 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 66 | 67 | promise@^7.1.1: 68 | version "7.1.1" 69 | resolved "https://registry.yarnpkg.com/promise/-/promise-7.1.1.tgz#489654c692616b8aa55b0724fa809bb7db49c5bf" 70 | dependencies: 71 | asap "~2.0.3" 72 | 73 | prop-types@^15.5.10: 74 | version "15.5.10" 75 | resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154" 76 | dependencies: 77 | fbjs "^0.8.9" 78 | loose-envify "^1.3.1" 79 | 80 | setimmediate@^1.0.5: 81 | version "1.0.5" 82 | resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" 83 | 84 | ua-parser-js@^0.7.9: 85 | version "0.7.12" 86 | resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.12.tgz#04c81a99bdd5dc52263ea29d24c6bf8d4818a4bb" 87 | 88 | whatwg-fetch@>=0.10.0: 89 | version "2.0.3" 90 | resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84" 91 | -------------------------------------------------------------------------------- /pin-code.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { PropTypes } from "prop-types"; 3 | import { TextInput, View, Text } from "react-native"; 4 | 5 | import { codePinStyles } from "./pin-code-style"; 6 | 7 | class CodePin extends Component { 8 | constructor(props) { 9 | super(props); 10 | 11 | const codeLength = props.number || props.code.length; 12 | 13 | this.state = { 14 | error: "", 15 | number: codeLength, 16 | code: new Array(codeLength).fill(""), 17 | edit: null, 18 | reset: false 19 | }; 20 | 21 | this.textInputsRefs = []; 22 | 23 | this.clean = this.clean.bind(this); 24 | this.focus = this.focus.bind(this); 25 | this.isFocus = this.isFocus.bind(this); 26 | this.handleEdit = this.handleEdit.bind(this); 27 | this.onKeyPress = this.onKeyPress.bind(this); 28 | } 29 | 30 | componentWillReceiveProps(newProps) { 31 | const codeLength = newProps.number || newProps.code.length; 32 | 33 | if (newProps.number !== this.props.number) { 34 | this.setState({ 35 | number: codeLength, 36 | edit: null 37 | }); 38 | } 39 | } 40 | 41 | clean() { 42 | this.setState(prevState => { 43 | return { 44 | code: new Array(prevState.number).fill(""), 45 | edit: null, 46 | reset: true 47 | }; 48 | }); 49 | this.focus(0); 50 | } 51 | 52 | focus(id) { 53 | // Check to ensure that input exists. This is important in the case of autofill. 54 | if (this.textInputsRefs[id]) this.textInputsRefs[id].focus(); 55 | } 56 | 57 | isFocus(id) { 58 | let newCode = this.state.code.slice(); 59 | 60 | for (let i = 0; i < newCode.length; i++) if (i >= id) newCode[i] = ""; 61 | 62 | this.setState({ 63 | code: newCode, 64 | edit: id 65 | }); 66 | } 67 | 68 | handleEdit(number, id) { 69 | let newCode = this.state.code.slice(); 70 | 71 | // Detecting if the entire code has been pasted or autofilled into 72 | // the first field. 73 | const hasAutofilled = number.length > 1 && id === 0; 74 | 75 | if (hasAutofilled) { 76 | const autofillCode = number.split(""); 77 | 78 | // Need to update state so UI updates. 79 | this.setState( 80 | { 81 | code: newCode.map((code, i) => { 82 | return autofillCode[i] ? autofillCode[i] : code; 83 | }), 84 | edit: 85 | autofillCode.length >= this.props.number 86 | ? this.props.number - 1 87 | : autofillCode.length, 88 | reset: false 89 | }, 90 | () => { 91 | this.focus(this.state.edit); 92 | } 93 | ); 94 | } else { 95 | newCode[id] = number[0]; 96 | } 97 | 98 | // User filling the last pin ? 99 | if ( 100 | id === this.state.number - 1 || 101 | (hasAutofilled && number.length === this.props.number) 102 | ) { 103 | this.focus(0); 104 | 105 | // App pass a checkPinCode function 106 | if (this.props.checkPinCode) { 107 | this.props.checkPinCode(newCode.join(""), success => { 108 | // App say it's different than code 109 | if (!success) { 110 | this.setState({ 111 | error: this.props.error, 112 | code: new Array(this.state.number).fill(""), 113 | edit: 0, 114 | reset: true 115 | }); 116 | } else { 117 | // Is Okey !!! 118 | this.setState( 119 | prevState => ({ 120 | edit: prevState.edit + 1, 121 | code: newCode, 122 | reset: true 123 | }), 124 | this.props.success 125 | ); 126 | } 127 | }); 128 | 129 | return; 130 | } 131 | 132 | // no checkPinCode function 133 | // But it's different than code 134 | if (this.props.code !== newCode.join("")) { 135 | this.setState({ 136 | error: this.props.error, 137 | code: new Array(this.state.number).fill(""), 138 | edit: 0, 139 | reset: true 140 | }); 141 | 142 | return; 143 | } 144 | 145 | this.setState( 146 | prevState => ({ 147 | edit: prevState.edit + 1, 148 | code: newCode, 149 | reset: true 150 | }), 151 | this.props.success 152 | ); 153 | 154 | return; 155 | } 156 | 157 | if (!hasAutofilled) { 158 | this.focus(this.state.edit + 1); 159 | 160 | this.setState(prevState => { 161 | return { 162 | error: "", 163 | code: newCode, 164 | edit: prevState.edit + 1, 165 | reset: false 166 | }; 167 | }); 168 | } 169 | } 170 | 171 | onKeyPress(e) { 172 | if (e.nativeEvent.key === "Backspace") { 173 | const edit = this.state.edit; 174 | const toFocus = edit > 0 ? edit - 1 : 0; 175 | this.focus(toFocus); 176 | } 177 | } 178 | 179 | render() { 180 | const { 181 | text, 182 | success, 183 | pinStyle, 184 | textStyle, 185 | errorStyle, 186 | obfuscation, 187 | containerStyle, 188 | containerPinStyle, 189 | ...props 190 | } = this.props; 191 | 192 | pins = []; 193 | for (let index = 0; index < this.state.number; index++) { 194 | const id = index; 195 | const value = this.state.code[id] 196 | ? obfuscation 197 | ? "*" 198 | : this.state.code[id].toString() 199 | : ""; 200 | 201 | pins.push( 202 | (this.textInputsRefs[id] = ref)} 205 | onChangeText={text => this.handleEdit(text, id)} 206 | onFocus={() => this.isFocus(id)} 207 | value={value} 208 | maxLength={id === 0 ? this.props.number : 1} 209 | style={[codePinStyles.pin, pinStyle]} 210 | returnKeyType={"done"} 211 | autoCapitalize={"sentences"} 212 | autoCorrect={false} 213 | autoFocus={ 214 | (id === 0 && 215 | this.state.edit === null && 216 | this.props.autoFocusFirst) || 217 | id === this.state.edit 218 | } 219 | onKeyPress={this.onKeyPress} 220 | {...props} 221 | /> 222 | ); 223 | } 224 | 225 | const error = this.state.error ? ( 226 | {this.state.error} 227 | ) : null; 228 | 229 | return ( 230 | 231 | {text} 232 | 233 | {error} 234 | 235 | 236 | {pins} 237 | 238 | 239 | ); 240 | } 241 | } 242 | 243 | CodePin.propTypes = { 244 | code: PropTypes.string, 245 | success: PropTypes.func.isRequired, 246 | number: PropTypes.number, 247 | checkPinCode: PropTypes.func, 248 | autoFocusFirst: PropTypes.bool, 249 | obfuscation: PropTypes.bool, 250 | pinStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.number]), 251 | containerPinStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.number]), 252 | containerStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.number]), 253 | textStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.number]), 254 | errorStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.number]) 255 | }; 256 | 257 | CodePin.defaultProps = { 258 | code: "", 259 | number: 4, 260 | checkPinCode: null, 261 | autoFocusFirst: true, 262 | obfuscation: false, 263 | text: "Pin code", 264 | error: "Bad pin code.", 265 | pinStyle: {}, 266 | containerPinStyle: {}, 267 | containerStyle: {}, 268 | textStyle: {}, 269 | errorStyle: {} 270 | }; 271 | 272 | export default CodePin; 273 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-pin-code 2 | 3 | ![last release](https://badgen.net/npm/v/react-native-pin-code) ![monthly download](https://badgen.net/npm/dm/react-native-pin-code) ![license](https://badgen.net/github/license/gkueny/react-native-pin-code) 4 | 5 | A simple pin code component 6 | 7 | ![example with blur background](https://media.giphy.com/media/xUPGcffB0VeaMd6DSM/giphy.gif) 8 | ![a simple example](https://media.giphy.com/media/3oKIPsotgoJ8ZGEr5u/giphy.gif) 9 | 10 | ## Installation 11 | 12 | npm install react-native-pin-code --save 13 | 14 | ## Example 15 | 16 | ```js 17 | console.log('hurray!')} // If user fill '2018', success is called 20 | text="A simple Pin code component" // My title 21 | error="You fail" // If user fail (fill '2017' for instance) 22 | autoFocusFirst={false} // disabling auto-focus 23 | /> 24 | ``` 25 | 26 | or 27 | 28 | ```js 29 | callback(code === '1234')} 32 | // Check manually code (ask server for instance) 33 | // and call callback function with 34 | // true (code pin is correct) 35 | // or false (code pin is false) 36 | success={() => console.log('hurray!')} // If user fill '2018', success is called 37 | text="A simple Pin code component" // My title 38 | error="You fail" // If user fail (fill '2017' for instance) 39 | autoFocusFirst={false} // disabling auto-focus 40 | /> 41 | ``` 42 | 43 | > Code prop is not needed if checkPinCode is used 44 | 45 | ## props 46 | 47 | | prop | type | description | isRequired | default value | 48 | | ----------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | 49 | | code | number | pin code | not required | '' | 50 | | success | function | call on success | required | | 51 | | checkPinCode | function | this function is called to check pin code. The parameters are : `code` (the code filled by user) and a `callback` function. callback must be called with true (code pin is correct) or false (code pin is wrong) | not required | null | 52 | | obfuscation | boolean | If true, obfuscate code with '\*' | | false | 53 | | text | string | text to display as title | | 'Pin code.' | 54 | | error | string | text to display on error | | 'Bad pin code.' | 55 | | number | number | number of input to display | | 4 | 56 | | autoFocusFirst | boolean | auto focus first input | | true | 57 | | containerStyle | object, StyleSheet object | container style | | { height: 150, width: width - 30, backgroundColor : '#FFF' } | 58 | | containerPinStyle | object, StyleSheet object | inputs container style | | { width: width - 30, height: 40, flexDirection: 'row', justifyContent: 'space-around', alignItems: 'center', marginTop: 20 } | 59 | | pinStyle | object, StyleSheet object | input style | | { backgroundColor : '#F0F0F0', textAlign: 'center', flex: 1, marginLeft: 20, marginRight: 20, borderRadius: 5, shadowColor: '#000000', shadowOffset: {width: 1,height : 1}, shadowRadius: 5, shadowOpacity : 0.4 } | 60 | | textStyle | object, StyleSheet object | text style | | { textAlign: 'center', color: 'gray', fontSize: 20, marginTop: 30 } | 61 | | errorStyle | object, StyleSheet object | error text style | | { textAlign: 'center', color: 'red', paddingTop: 10 } | 62 | 63 | All other `props` are pass down to `TextInput` component. 64 | 65 | For instance you can customize keyboardtype : 66 | 67 | ```js 68 | 72 | ``` 73 | 74 | ## Functions 75 | 76 | To use pin code component function, use ref : 77 | 78 | ```js 79 | this.ref = ref} 81 | ... 82 | /> 83 | ``` 84 | 85 | ## clean 86 | 87 | clean inputs and focus first input. 88 | 89 | ```js 90 | this.ref.clean(); 91 | ``` 92 | 93 | ## focus 94 | 95 | focus input at specified index 96 | 97 | ```js 98 | this.ref.focus(1); 99 | ``` 100 | 101 | ## Examples 102 | 103 | > [react-native-pin-code-examples](https://github.com/gkueny/react-native-pin-code-examples) 104 | 105 | To test examples (use [expo](https://expo.io)) : 106 | 107 | ``` 108 | git clone https://github.com/gkueny/react-native-pin-code-examples 109 | cd blurExample 110 | npm install 111 | npm run ios 112 | ``` 113 | 114 | or 115 | 116 | ``` 117 | git clone https://github.com/gkueny/react-native-pin-code-examples 118 | cd simpleExample 119 | npm install 120 | npm run ios 121 | ``` 122 | --------------------------------------------------------------------------------