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

  • Swift
  • ObjectiveC
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:

  • Swift
  • ObjectiveC
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:

  • Swift
  • ObjectiveC
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

  • Swift
  • ObjectiveC
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:

  • Swift
  • ObjectiveC
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:

  • Swift
  • ObjectiveC
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:

  • Swift
  • ObjectiveC
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:

  • Swift
  • ObjectiveC
@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:

  • Swift
  • ObjectiveC
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:

  • Swift
  • ObjectiveC
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

  • Swift
  • ObjectiveC
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:

  • Swift
  • ObjectiveC
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!
    }
}];