The WebView option allows individuals to verify their identity in a mobile app with the Android WebView or iOS WKWebView.
Android Webview
To launch the WebView, create a WebView and pass in the path specified in the magic user link
// inside Activity#onCreate// assuming some WebView called "webView"WebSettings settings =webView.getSettings();settings.setDomStorageEnabled(true);settings.setJavaScriptEnabled(true);settings.setMediaPlaybackRequiresUserGesture(false);finalUri trustswiftlyUrl =new Uri.Builder().scheme("https").encodedAuthority("<COMPANY_URL>.trustswiftly.com").path("<MAGIC_URL_PATH>").appendQueryParameter("is-webview","true").build();
In addition to adding to the Manifest file, you'll need to override onPermissionRequest and onShowFileChooser depending on your use case. See an example below.
// set fields on your Activitypublicstaticfinalint INPUT_FILE_REQUEST_CODE =1;privatestaticfinalint CAMERA_PERMISSION_REQUEST =1111;privatePermissionRequest cameraPermission;privateValueCallback<Uri[]> filePathCallback;privateString cameraPhotoPath;@OverridepublicvoidonActivityResult(int requestCode,int resultCode,Intent data) {if (requestCode != INPUT_FILE_REQUEST_CODE || filePathCallback ==null) { super.onActivityResult(requestCode, resultCode, data);return; }Uri[] results =null;// Check that the response is a good oneif (resultCode ==Activity.RESULT_OK) {if (data ==null) {// If there is not data, then we may have taken a photoif (cameraPhotoPath !=null) { results =newUri[] { Uri.parse(cameraPhotoPath) }; } } else {String dataString =data.getDataString();if (dataString !=null) { results =newUri[] { Uri.parse(dataString) }; } } }filePathCallback.onReceiveValue(results); filePathCallback =null;}// in the Activity#onCreate methodwebView.setWebChromeClient(newWebChromeClient() { @OverridepublicvoidonPermissionRequest(finalPermissionRequest request) {if (request.getOrigin().toString().equals("https://<COMPANY_URL>.trustswiftly.com/")) {ActivityCompat.requestPermissions(MainActivity.this,newString[] { Manifest.permission.CAMERA }, CAMERA_PERMISSION_REQUEST); cameraPermission = request; } else {request.deny(); } } @OverridepublicbooleanonShowFileChooser(WebView webView,ValueCallback<Uri[]> newFilePathCallback,FileChooserParams fileChooserParams) {if (filePathCallback !=null) {filePathCallback.onReceiveValue(null); } filePathCallback = newFilePathCallback;Intent takePictureIntent =newIntent(MediaStore.ACTION_IMAGE_CAPTURE);if (takePictureIntent.resolveActivity(getPackageManager()) !=null) {// Create the File where the photo should goFile photoFile =null;// Create an image file nameString timeStamp =newSimpleDateFormat("yyyyMMdd_HHmmss",Locale.US).format(newDate());String imageFileName ="JPEG_"+ timeStamp +"_";File storageDir =Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);try { photoFile =File.createTempFile(imageFileName,".jpg", storageDir); } catch (IOException ex) {// Error occurred while creating the File }// Continue only if the File was successfully createdif (photoFile !=null) { cameraPhotoPath ="file:"+photoFile.getAbsolutePath();takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,Uri.fromFile(photoFile)); } else { takePictureIntent =null; } }Intent contentSelectionIntent =newIntent(Intent.ACTION_GET_CONTENT);contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);contentSelectionIntent.setType("image/*");Intent[] intentArray;if (takePictureIntent !=null) { intentArray =newIntent[] { takePictureIntent }; } else { intentArray =newIntent[0]; }Intent chooserIntent =newIntent(Intent.ACTION_CHOOSER);chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);chooserIntent.putExtra(Intent.EXTRA_TITLE,"Image Chooser");chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_CODE);returntrue; }});// overwriting your AppCompatActivity's #onRequestPermissionsResult@OverridepublicvoidonRequestPermissionsResult(int requestCode, @NonNullString[] permissions, @NonNullint[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults);if (requestCode == CAMERA_PERMISSION_REQUEST) {if (grantResults[0] ==PackageManager.PERMISSION_GRANTED) {cameraPermission.grant(cameraPermission.getResources()); } else {cameraPermission.deny(); } }}
Permissions
Be sure to add the required permissions for your use case to your AndroidManifest file. For example:
<uses-permission android:name="android.permission.CAMERA" />
<!-- Microphone permissions are not usually required, except when using a verify method that records video. -->
<!-- <uses-permission android:name="android.permission.RECORD_AUDIO" />-->
<!-- <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />-->
iOS WKWebView
To use WKWebView to launch the magic link flow from a UIViewController
If testing your app and creating multiple verify sessions you should clear the cookies and cache of WKWebView inbetween new user sessions. (Refer to this guide)
Please ensure allowsInlineMediaPlayback is enabled when creating a webview on a webkit browser (mobile Safari). This defaults to false and the camera preview will incorrectly open as a fullscreen live broadcast.
❗Camera Configuration
The WebView Flow requires access to the device camera. Please include the NSCameraUsageDescription key your app's Info.plist file as described in the Apple Developer documentation.
❗️Allow External Network Requests
Trust Swiftly makes external network calls within the Inquiry Flow that need to be allowlisted for the flow to properly function. Certain frameworks such as Cordova require such requests to be allow listed. Please include *.trustswiftly.com/* in such an allow list if needed
Make sure permissions are correctly set for your app bundle.