Using Contactless Reader/Writer with Capture Helper

Initializing Capture Helper

Please refer to Capture Helper initialization.

Device Manager

The Socket Mobile contactless reader/writer (D600) uses Bluetooth Low Energy (BLE) to communicate with the host.

Capture SDK provides a new Device Manager object in order to provide some control over the BLE transport.

The Device Manager reference is received in the 2 delegates didNotifyArrivalForDeviceManager and didNotifyRemovalForDeviceManager defined in the SKTCaptureHelperDelegate.

Here is an example of such a delegate handler in a sample app:

func didNotifyArrivalForDeviceManager(_ device: CaptureHelperDeviceManager, withResult result: SKTResult) {
    d600SupportOnOffSwitch.isEnabled = true
    device .getFavoriteDevicesWithCompletionHandler { (result, favorites) in
        if result == .E_NOERROR {
            if let fav = favorites as String! {
                self.d600SupportOnOffSwitch.setOn(fav.count>0, animated: true)
            }
        }
    }
}

func didNotifyRemovalForDeviceManager(_ device: CaptureHelperDeviceManager, withResult result: SKTResult) {
    d600SupportOnOffSwitch.isEnabled = false

}

In this example a UI switch is activated in the function of the content of the favorite devices in which its function is described below.

Favorite Devices

To facilitate the operations of discovering and connecting to a contactless reader/writer, a concept of favorite devices has been introduced.

The Device Manager has a property to configure in a persistent way the favorite devices.

As soon as the Device Manager favorite devices is set to either a device identifier or "*" the auto discovery and connect mode starts automatically.

In this mode, the Device Manager starts a discovery and connects to the contactless reader/writer corresponding to the identifier found in the favorite devices property or to the first contactless reader/writer it finds in the case where the favorite devices is set to "*".

Note

Using auto discovery and connect mode can increase power consumption. Once the contactless reader/writer is connected, the consumption level drops. But as soon as the discovery restarts when, for example when the contactless reader/writer is no longer ON or in range, then the power consumption increases slightly. Setting the favorite devices to an empty string will stop the auto discovery and connect mode, and BLE discovery won’t happen again until the favorite devices is set or the discovery is implicitly started by using the BLE Device Manager startDiscovery property.

Starting Contactless Reader/Writer Auto Discovery Mode

The following code sample shows how to turn on by default in an app the auto discovery and connect mode to support contactless reader/writer, so that as soon as there is a device turned on in the vicinity, the app will automatically connect to it:

func didNotifyArrivalForDeviceManager(_ device: CaptureHelperDeviceManager, withResult result: SKTResult) {
    print("device manager arrival notification")

    device.getFavoriteDevicesWithCompletionHandler { (result, favorites) in
        print("getting the favorite devices returned \(result.rawValue)")
        if result == SKTCaptureErrors.E_NOERROR {
            if let fav = favorites as String! {
                // if favorites is empty (meaning D600 auto-discovery is off)
                // then set it to "*" to connect to any D600 in the vicinity
                // To turn off the BLE auto reconnection, set the favorites to
                // an empty string
                if fav.isEmpty {
                    device.setFavoriteDevices("*", withCompletionHandler: { (result) in
                        print("setting new favorites returned \(result.rawValue)")
                    })
                }
            }
        }
    }
}

To turn off this feature, just set the favorite devices to an empty string:

deviceManager.setFavoriteDevices("", withCompletionHandler: { (result) in
    print("setting the Device Manager favorites to empty string returns \(result.rawValue)")
})

Presence of a Contactless Reader/Writer

The presence of contactless reader/write device reference is handled by the application by implementing the didNotifyArrivalForDevice and didNotifyRemovalForDevice delegates from the SKTCaptureHelperDevicePresenceDelegate protocol.

Here is an example of such handlers:

func didNotifyArrivalForDevice(_ device: CaptureHelperDevice, withResult result: SKTResult) {
    print("didNotifyArrivalForDevice: \(String(describing: device.deviceInfo.name))")
    self.updateStatus()
    lastDevice = device
}

func didNotifyRemovalForDevice(_ device: CaptureHelperDevice, withResult result: SKTResult) {
    print("didNotifyRemovalForDevice: \(String(describing: device.deviceInfo.name))")
    self.updateStatus()
    lastDevice = nil
}

func updateStatus() {
    let devices = CaptureHelper.sharedInstance.getDevices()
    var status = ""
    for device in devices {
        if status.isEmpty == false {
            status += " "
        }
        status += device.deviceInfo.name!
    }
    if status.isEmpty {
        status = "Waiting for a scanner to connect..."
    }
    self.statusLabel.text = status;
}

Note

This code shown above updates the UI from the didNotifyArrivalForDevice and from the didNotifyRemovalForDevice which requires it to be executed in the main thread. This could be accomplished in different ways, but Capture Helper provides a property to simplify this process as shown here:

capture.dispatchQueue = DispatchQueue.main

This is usually done before opening Capture. The same logic applies for each device received in the didNotifyArrivalForDevice if the callback of a device function needs to update the UI.

To keep a particular device a favorite device, its device unique identifier can be retrieved using the Device Manager getDeviceUniqueIdentifierFromDeviceGuid API and it can then be used to set the Device Manager favorite devices as shown below:

func didNotifyArrivalForDevice(_ device: CaptureHelperDevice, withResult result: SKTResult) {
    print("didNotifyArrivalForDevice: \(String(describing: device.deviceInfo.name))")
    self.updateStatus()
    device.dispatchQueue = DispatchQueue.main
    let deviceManagers = CaptureHelper.sharedInstance.getDeviceManagers()
    deviceManagers[0].getFavoriteDevicesWithCompletionHandler { (result, favoriteDevices) in
        print("getting the favorite devices returns result: \(result.rawValue)")
        if result == SKTCaptureErrors.E_NOERROR {
            if let fav = favoriteDevices as String! {
                if fav == "*" {
                    deviceManagers[0].getDeviceUniqueIdentifierFromDeviceGuid(device.deviceInfo.guid!)
                    { (result, deviceIdentifier) in
                        print("getting the device unique identifier returns result: \(result.rawValue)")
                        if result == SKTCaptureErrors.E_NOERROR {
                            deviceManagers[0].setFavoriteDevices(deviceIdentifier!, withCompletionHandler:
                            { (result) in
                              print("setting the favorite to \(String(describing: device.deviceInfo.name)) result: \(result.rawValue)")
                            })
                        }
                    }
                }
            }
        }
    }
    lastDevice = device
}

This example sets the device favorite to the first contactless reader/writer that connects to the host if the device favorite was set with a *. By doing so, this contactless reader/writer becomes the preferred device to be connected to this host, until the favorite device string is reset to either an empty string to stop the auto discovery and connect mode, or to * to connect to another or the same contactless reader/writer device.

Contactless Reader/Writer Discovery

The Device Manager offers an API to discover the contactless reader/writer devices that are in the vicinity.

The application must implement the delegate from the CaptureHelperDeviceManagerDiscoveryDelegate. See CaptureHelperDeviceManagerDiscoveryDelegate for more details.

Once a device has been discovered, its identifier UUID can be used to set the Device Manager favorite devices. As soon as the Device Manager favorite devices is set with this UUID, the Device Manager will then connect to this particular contactless reader/writer device.

Here is an example showing how to start the device discovery:

@IBAction func pressStartDiscovery(_ sender: Any) {
    let deviceManagers = CaptureHelper.sharedInstance.getDeviceManagers()
    if deviceManagers.count > 0 {
        let deviceManager = deviceManagers[0];
        deviceManager.startDiscoveryWithTimeout(5000) { (result) in
            print("start discovery returns result \(result.rawValue)")
        }
    }
}

Then for each discovered device the didDiscoverDevice delegate is called:

func didDiscoverDevice(_ device: String, fromDeviceManager deviceManager: CaptureHelperDeviceManager){
    let data  = device.data(using: .utf8)
    let deviceInfo = try! PropertyListSerialization.propertyList(from:data!, options: [], format: nil) as! [String:Any]
    print("device discover: \(deviceInfo)")
    deviceManager.setFavoriteDevices(deviceInfo["identifierUUID"] as! String) { (result) in
        print("setting the favorite devices returns: \(result.rawValue)")
    }
}

Here is how the device: String might look:

{
  identifierUUID = "BE495AA0-A93C-4274-9006-F3BC2428ACDF";
  name = "Socket D600 [7EF619]";
  serviceUUID = "6CB501B7-96F6-4EEF-ACB1-D7535F153CF0";
}

In this case the last discovered device is set as favorite. The device identifierUUID is what can be used to set the favorite devices with in order to connect to that particular contactless reader/writer device.

The device discovery ends once the time out has elapsed and notifies the app by calling the didDiscoveryEndWithResult delegate of the SKTCaptureHelperDelegate:

func didEndDiscoveryWithResult(_ result: SKTResult, fromDeviceManager deviceManager: CaptureHelperDeviceManager){
    print("end discovery with result: \(result.rawValue)")
}

Contactless Reader/Writer Data Format

When the contactless reader/writer reads data from a card, it can display this data in four different formats:

  • Tag Type and ID: SKTCaptureDataFormat.tagTypeAndId This Data Format will display the type of card (NFC Forum, etc.) as well as the unique identifier associated with the card.

  • ID Only: SKTCaptureDataFormat.idOnly This Data Format will only display the unique identifier from the card. (This format is not supported)

  • Tag Type and Data: SKTCaptureDataFormat.tagTypeAndData This Data Format will display the type of card (NFC Forum, etc.) as well as the expected data on the card. This data can be translated into a String format or otherwise if expected.

  • Data Only SKTCaptureDataFormat.dataOnly This Data Format will display only the data from the card. (This format is not supported.)

Setting and Getting the current Data Format

You can change the current data format by using one of the aforementioned data format types. Example of setting to tagTypeAndData:

device.setDataFormat(dataFormat: SKTCaptureDataFormat.tagTypeAndData) { [weak self] (result) in
  guard let strongSelf = self else { return }
    // Check if result == SKTResult.E_NOERROR
    if result == SKTResult.E_NOERROR {
        // success!
    } else {
        // Error!
    }
}

Getting the current data format is similar to setting:

device.getDataFormatWithCompletionHandler({ [weak self] (result, dataFormat) in
    guard let strongSelf = self else { return }
    if result == SKTResult.E_NOERROR {
        // Success!
        if let dataFormat = dataFormat {
            if dataFormat == SKTCaptureDataFormat.tagTypeAndId {
                // Update the UI in some way...
            } else if dataFormat == SKTCaptureDataFormat.tagTypeAndData {
                // Update the UI in some other way...
            }
        }
    } else {
        // Error!
    }
})