Unlock With Tap in mobile apps functions similarly to using a traditional proximity card. The app launches an unlock scanning process. When the user holds their phone close to the lock they wish to open, the phone communicates wirelessly with the lock and transmits an access credentials. Once the lock verifies these credentials and confirms access is granted, it unlocks.
In your app, you use seam.phone.native.unlockWithTap.launch to initiate this unlock scanning process. Note that unlockWithTap automatically starts scanning whenever it is possible to scan and stops scanning whenever it is not possible to scan. That is, when an issue obstructs the phone from scanning, the can_scan capability changes to false, and Seam adds the resulting error to the error list. Further, the error explains to the user how to fix the causing issue.
Using the "Unlock With Tap" process involves the following steps:
Before initiating the "Unlock With Tap" process, the user's phone must have the necessary mobile credentials loaded onto it. To verify that the credentials are loaded, these credentials can be retrieved either explicitly or through an event handler.
// Retrieve mobile credentials explicitly.if (seam.phone.get().nativeMetadata.isInitialized) {val credentials = seam.phone.native.credentials.list()}// Retrieve mobile credentials using an event handler.classMyEventHandler implements ISeamEventHandler {handle(event: SeamEvent) =when(event) {is SeamEvent.Phone.Native.Credentials.Refreshed -> {val credentials = seam.phone.native.credentials.list()// Show the credentials in the UI. } }}
// Retrieve mobile credentials explicitly.if(seam.phone.get().nativeMetadata.isInitialized) {let credentials = seam.phone.native.credentials.list()}// Retrieve mobile credentials as soon as when they're refreshed.funchandleEvent(event: SeamEvent) {switch(event) {case .phone(.native(.credentials(.refreshed))):let credentials = seam.phone.native.credentials.list()// show credentials in the ui }}
2. Check the System Requirements
Before you launch the "Unlock With Tapping" process, verify that all the required permissions are enabled.
val requiredPermissions = seam.phone.native.listRequiredAndroidPermissions( enableUnlockWithTap =true)if (requiredPermissions.isNotEmpty()) {// ...}
let requiredPermissions = seam.phone.native.listRequiredIosPermissions( enableUnlockWithTap:true)if(!requiredPermissions.isEmpty) {// ...}
Check the error list—for example, in an event handler—to identify any current obstructions.
funhandleEvent( event: SeamEvent) {// Check if there's a change in the phone state, under the phone namespace.if (event is SeamEvent.Phone) {val phone = seam.phone.get().nativeMetadataif (// Check if unlocking with tap is not possible.!phone.canUnlockWithTap ) {// Gather the missing permissions required for unlocking with tap.if (phone.errors.any { it is SeamError.Phone.Native.MissingRequiredAndroidPermissions }) {val requiredPermissions = seam.phone.native.listRequiredAndroidPermissions( enableUnlockWithTap =true )// Here, implement logic to request these permissions from the user. } } }}
fun handleEvent(event: SeamEvent) {// Check for changes in the phone state under the phone namespace.switch(event) {case .phone:let phone = seam.phone.get().nativeMetadata// Verify if the phone cannot unlock with tap.if(!phone.canUnlockWithTap) {// Gather the missing permissions required for unlocking with tap.if phone.errors.contains(where: { $0 == .phone(.native(.missingRequiredIosPermissions)) }) {let requiredPermissions = seam.phone.native .listRequiredIosPermissions(enableUnlockWithTap:true)// Here, implement logic to request these permissions from the user. } }break }}
3. Launch the "Unlock With Tap" Process
Once the required permissions are enabled, the app can launch the "Unlock With Tap" Process.
// Launch the "Unlock With Tap" Process.if (seam.phone.get().can_unlock_with_tap) {try { seam.phone.native.unlockWithTap.launch( foreground =true, notification =/* Platform-specific notification object */, ) } catch (e: SeamError) {// Handle unrecoverable errors, such as missing permissions. }} else {// Process the error list if unlocking with a tap isn't available.}funhandleEvent(event: SeamEvent) {// Check for changes in the phone state, under the phone namespace.if (event is SeamEvent.Phone) { unlockWithTap = seam.phone.native.unlockWithTap.get()if (unlockWithTap.isRunning) {if (!unlockWithTap.isScanning) {// Set the UI state to indicate that the phone is not scanning.// Check the error map because scanning is expected but is not occurring. seam.phone.get().nativeMetadata.errors// Potential errors:// - BluetoothConnectionRequired: Indicates Bluetooth is off.// - NoCredential: Indicates missing credentials. } else {// Update UI to show that the phone is actively scanning. } } }}
// Launch the "Unlock With Tap" Process.if(seam.phone.get().can_unlock_with_tap) {do {try seam.phone.native.unlockWithTap.launch(foreground:true) } catch {// Handle unrecoverable errors, such as missing permissions. }} else {// Process the error list if unlocking with a tap isn't available.}funchandleEvent(event: SeamEvent) {// Check for changes in the phone state, under the phone namespace.switch(event) {case .phone:let unlockWithTap = seam.phone.native.unlockWithTap.get()if unlockWithTap.isRunning {if!unlockWithTap.isScanning {// Set the UI state to indicate that the phone is not scanning.// Check the error map because scanning is expected but is not occurring.let errors = seam.phone.get().nativeMetadata.errors// Errors to look out for could include:// - BluetoothConnectionRequired: Bluetooth is turned off.// - NoCredential: Missing credentials for operation. } else {// Update UI to show that the phone is actively scanning. } } }}
4. Handle Unlock Status Updates
Use an event handler to handle unlock-related status updates. Use these events to initiate changes to the user interface.
The ReaderCommunicationSuccess/readerCommunicationSuccess event indicates that the phone has communicated successfully with the door reader. That is, depending on the events that the underlying ACS API returns, the Seam mobile SDK may not be able to determine whether the door reader granted access. For some ACSs, the Seam API can only determine whether the communication with the reader succeeded. Other underlying ACS APIs may return events that indicate whether the door reader unlocked successfully.
funhandleEvent( event: SeamEvent) =when (event) {is SeamEvent.Phone.Native.ReaderConnected -> {// Set the UI state to indicate that the phone is connected to the reader. }is SeamEvent.Phone.Native.ReaderCommunicationSuccess -> {// Set the UI state to indicate that the communication with the// reader or lock was successful.// This indicates either the lock has been successfully unlocked// or the key has been sent to the reader. }is SeamEvent.Internal.Phone.Native.ReaderConnectionFailed -> {// Set the UI state to indicate that the phone failed to unlock the reader. }// ... }
funchandleEvent(event: SeamEvent) {switch(event) {case .phone(.native(.readerConnected)):// Set the UI state to indicate that the phone is connected to the reader.breakcase .phone(.native(.readerCommunicationSuccess)):// Set the UI state to indicate that the communication with the// reader or lock was successful.// This indicates either the lock has been successfully unlocked// or the key has been sent to the reader.breakcase .internal(.phone(.native(.readerConnectionFailed))):// Set the UI state to indicate that the phone failed to unlock the reader.break }}
5. Stop the "Unlock With Tap" Process
unlockWithTap continues attempting to scan, until you explicitly stop this function. For example, you may want to disable scanning if the user changes the focus on their phone to a different app. Or after an unlock has been completed successfully.
seam.phone.native.unlockWithTap.stop()
Task {let response =await seam.phone.native.unlockWithTapping.stop()switch(response) {case .success()://Display unlock stop success message to user.breakcase .failure(error):// Display unlock stop failure message to user }}