Using Contactless Readers/Writers and barcode scanners with Capture Helper¶
Initializing Capture Helper¶
Please refer to Capture Helper initialization.
Device Manager¶
The Socket Mobile contactless NFC readers/writers and barcode scanners use Bluetooth Low Energy to communicate with the host.
The following devices are Bluetooth Low Energy: - D600 (NFC) - S550 (NFC) - S370 (NFC and barcode scanner) - S320 (Barcode scanner)
CaptureSDK 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
}
-(void)didNotifyArrivalForDeviceManager:(SKTCaptureHelperDeviceManager *)deviceManager withResult:(SKTResult)result {
NSLog(@"didNotifyArrivalForDeviceManager %@", deviceManager.friendlyName);
_deviceManager = deviceManager;
[deviceManager getFavoriteDevicesWithCompletionHandler:^(SKTResult result, NSString *favorites) {
NSLog(@"current favorite set to: %@", favorites);
_currentFavorite = favorites;
dispatch_async(dispatch_get_main_queue(), ^{
_d600SupportSwitch.on = favorites.length > 0;
});
}];
}
-(void)didNotifyRemovalForDeviceManager:(SKTCaptureHelperDeviceManager *)deviceManager withResult:(SKTResult)result {
NSLog(@"didNotifyRemovalForDeviceManager %@", deviceManager.friendlyName);
_deviceManager = nil;
}
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
NFC readers/writers and barcode scanners corresponding to the identifier found in the favorite
devices property or to the first NFC reader/writer or barcode scanner 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 NFC reader/writer or barcode scanner 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 Bluetooth Low Energy devices 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 NFC reader/writer and barcode scanner, 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 NFC Reader/Writer auto-discovery is off)
// then set it to "*" to connect to any NFC Reader/Writer 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)")
})
}
}
}
}
}
-(void) didNotifyArrivalForDeviceManager:(SKTCaptureHelperDeviceManager *)deviceManager withResult:(SKTResult)result {
NSLog(@"Device Manager arrival");
if (result == SKTCaptureE_NOERROR) {
[deviceManager getFavoriteDevicesWithCompletionHandler:^(SKTResult result, NSString *favorites) {
NSLog(@"Getting favorite devices returns %ld", result);
if (result == SKTCaptureE_NOERROR) {
if (favorites.length == 0) {
[deviceManager setFavoriteDevices:@"*" completionHandler:^(SKTResult result) {
NSLog(@"Setting favorite devices returns %ld", result);
}];
}
}
}];
};
}
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)")
})
NSLog(@"Turning OFF the device support");
[_deviceManager setFavoriteDevices:@"" completionHandler:^(SKTResult result) {
NSLog(@"setting the favorites returns: %ld", (long)result);
_currentFavorite=@"";
}];
Presence of a NFC reader/writer and barcode scanner¶
The presence of NFC reader/writer or barcode scanner 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;
}
-(void)didNotifyArrivalForDevice:(SKTCaptureHelperDevice *)device withResult:(SKTResult)result {
[self updateStatusFromDevices:[_capture getDevicesList]];
}
-(void)didNotifyRemovalForDevice:(SKTCaptureHelperDevice *)device withResult:(SKTResult)result {
[self updateStatusFromDevices:[_capture getDevicesList]];
}
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.
Note
For a combo device like the S370 which has 2 devices, there will be two didNotifyArrivalForDevice
and two didNotifyRemovalForDevice
notifications.
The following code shows how you can distinghuish and handle them:
func didNotifyArrivalForDevice(_ device: CaptureHelperDevice, withResult result: SKTResult) {
print("didNotifyArrivalForDevice: \(String(describing: device.deviceInfo.name))")
if device.deviceInfo.deviceType == .NFCS370 {
// handle the NFC reader of the S370
} else if device.deviceInfo.deviceType == .scannerS370 {
// handle the Barcode scanner of the S370
}
}
-(void)didNotifyArrivalForDevice:(SKTCaptureHelperDevice *)device withResult:(SKTResult)result {
NSLog(@"Device arrival for %@", device.friendlyName);
if device.deviceInfo.deviceType == SKTCaptureDeviceTypeNFCS370 {
// handle the NFC reader of the S370
} else if device.deviceInfo.deviceType == SKTCaptureDeviceTypeScannerS370 {
// handle the Barcode scanner of the S370
}
}
You can also check if a device is barcode reader or a NFC reader when you get the didNotifyArrivalForDevice
:
func didNotifyArrivalForDevice(_ device: CaptureHelperDevice, withResult result: SKTResult) {
print("didNotifyArrivalForDevice: \(String(describing: device.deviceInfo.name))")
if device.isNFCReader {
// handle the NFC reader of the S370 or another NFC reader like a S550
} else if device.isBarcodeReader {
// handle the Barcode scanner of the S370 or another barcode scanner like a S320 or S740
}
}
-(void)didNotifyArrivalForDevice:(SKTCaptureHelperDevice *)device withResult:(SKTResult)result {
NSLog(@"Device arrival for %@", device.friendlyName);
if ([device isNFCReader]) {
// handle the NFC reader of the S370 or another NFC reader like a S550
} else if ([device isBarcodeReader]) {
// handle the Barcode scanner of the S370 or another barcode scanner like a S320 or S740
}
}
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
}
-(void)didNotifyArrivalForDevice:(SKTCaptureHelperDevice*) device withResult:(SKTResult) result{
[self updateStatusFromDevices:[_capture getDevicesList]];
NSLog(@"Device arrival for %@ and favorite is %@", device.friendlyName, _currentFavorite);
// set this scanner has favorite if favorite is still set as *
if([_currentFavorite compare:@"*"] == NSOrderedSame){
[_deviceManager getDeviceUniqueIdentifierFromDeviceGuid:device.guid completionHandler:^(SKTResult result, NSString *deviceUniqueIdentifier) {
NSLog(@"asking for the device unique identifier returns %ld with value: %@", (long)result, deviceUniqueIdentifier);
if(SKTSUCCESS(result)){
_currentFavorite = deviceUniqueIdentifier;
NSString* favorite = deviceUniqueIdentifier;
[_deviceManager setFavoriteDevices:_currentFavorite completionHandler:^(SKTResult result) {
NSLog(@"new favorite returns %ld and is set to %@", (long)result, favorite);
}];
}
}];
}
}
This example sets the device favorite to the first NFC reader/writer and barcode scanner that
connects to the host if the device favorite was set with a *
.
By doing so, this NFC reader/writer or barcode scanner 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 NFC reader/writer or barcode scanner device.
NFC reader/writer and barcode scanner Discovery¶
The Device Manager offers an API to discover the NFC reader/writer or barcode scanner 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 NFC reader/writer or barcode scanner device.
Here is an example showing how to start the device discovery:
@IBAction func pressStartDiscovery() {
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)")
}
}
}
-(IBAction)onPressDiscoveryButton {
if (_bleDeviceManager) {
[_bleDeviceManager startDiscoveryWithTimeout:5000 completionHandler:^(SKTResult result) {
NSLog(@"Discovery starts with result: %ld", result);
}];
}
}
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)")
}
}
-(void)didDiscoverDevice:(NSString *)device fromDeviceManager:(SKTCaptureHelperDeviceManager *)deviceManager {
NSLog(@"%@",device);
NSDictionary *newDevice = [NSPropertyListSerialization
propertyListWithData:[device dataUsingEncoding:NSUTF8StringEncoding]
options:kNilOptions
format:NULL
error:NULL];
NSLog(@"device Name: %@", newDevice[@"name"]);
NSLog(@"device UUID: %@", newDevice[@"identifierUUID"]);
if ([ViewController addDevice:newDevice inTheArray:_devices]) {
dispatch_async(dispatch_get_main_queue(), ^{
[_discoveredDevicesTableView reloadData];
});
}
}
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 NFC reader/writer or barcode scanner 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)")
}
-(void)didDiscoveryEndWithResult:(SKTResult)result fromDeviceManager:(SKTCaptureHelperDeviceManager *)deviceManager {
NSLog(@"Discovery ends with result: %ld", result);
}
NFC reader/writer and barcode scanner Data Format¶
When the NFC reader/writer or barcode scanner 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¶
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!
}
}
[device setDataFormat:SKTCaptureDataFormatTagTypeAndData completionHandler:^(SKTResult result) {
if(SKTSUCCESS(result)) {
// 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!
}
})
[device getDataFormatWithCompletionHandler:^(SKTResult result, SKTCaptureDataFormat dataFormat) {
if(SKTSUCCESS(result)) {
// Success!
if(dataFormat == SKTCaptureDataFormatTagTypeAndId) {
// Update the UI in some way...
} else {
// Update the UI in some other way...
}
} else {
// Error!
}
}];