Simple iOS app showing how to use the iOS CaptureSDK Swift Package to connect Socket Mobile barcode scanners, NFC readers and SocketCam (camera scanning) to an iOS application.
- Xcode with an iOS 15.0+ device
- The Socket Mobile
CaptureSDKSwift Package (2.1.22 or later). Xcode resolves it automatically when opening the project.
The CaptureSDK documentation can be found here.
Clone the project and open SingleEntrySwift.xcodeproj.
Your application's Info.plist must contain the following keys:
LSApplicationQueriesSchemes(Queried URL Schemes) with the array itemsktcompanion(in lower case).UISupportedExternalAccessoryProtocolswith the array itemcom.socketmobile.chs. This is required for Bluetooth Classic devices. Adding this protocol requires your application to be whitelisted by Socket Mobile before submission to the App Store. You can submit your app to be whitelisted in the Socket Mobile Developer portal.CFBundleAllowMixedLocalizationsset toYES.NSCameraUsageDescriptionwith a reason to use the camera for SocketCam.NSBluetoothAlwaysUsageDescriptionwith a reason to use Bluetooth.
Build and run the application on a device to test it with a Socket Mobile device or with SocketCam.
The main view shows:
- the decoded data with the Copy and Delete buttons,
- the Add BLE device and Add BT Classic device buttons to discover and connect devices,
- the Set Partnership button to configure the Single Partnership feature,
- the list of Connected Devices (SocketCam is always listed) and, during a Bluetooth LE discovery, the list of Discovered Devices,
- a Settings link showing the CaptureSDK version.
Tapping a connected device opens its features: trigger, friendly name, symbologies, battery level / power state and firmware version.
This sample application shows how to discover, connect and use Socket Mobile devices directly in your own application's flow, without the Socket Mobile Companion app.
We advise you to create some UI to show the discovered devices and the connected devices. Connected devices are remembered by CaptureSDK and reconnect automatically once in the vicinity of your iOS device. To stop a device from reconnecting, you need to remove it (see Removing a device).
| File | Purpose |
|---|---|
SingleEntryViewController.swift |
Opens CaptureSDK and displays the decoded data |
CaptureSdkHandler.swift |
Implements all the CaptureHelper delegates and forwards the events through NotificationCenter |
DevicesViewController.swift |
Bluetooth LE / Classic discovery, connection and removal of devices |
FeaturesViewController.swift |
List of features available for a connected device |
TriggerViewController.swift |
Start, stop and continuous trigger (including SocketCam) |
FriendlyNameViewController.swift |
Get / set the device friendly name |
SymbologiesViewController.swift |
Get / set barcode symbologies and NFC tag types |
BatteryViewController.swift |
Get the battery level and the power state |
FirmwareViewController.swift |
Get the firmware version |
SinglePartnershipViewController.swift |
Single Partnership feature |
CaptureHelperExtension.swift |
Example of a CaptureHelper extension |
CaptureHelper is part of the CaptureSDK package. It wraps the CaptureSDK boilerplate and exposes a simple API through CaptureHelper.sharedInstance and CaptureHelperDevice.
CaptureHelper.sharedInstance is shared across the view hierarchy, so there is no need to pass a reference to it between your views.
CaptureHelper notifies its delegates through a set of protocols. Adopt only the ones your application needs:
| Protocol | Methods |
|---|---|
CaptureHelperDevicePresenceDelegate |
didNotifyArrivalForDevice, didNotifyRemovalForDevice |
CaptureHelperDiscoveryDelegate |
didDiscoverDevice, didEndDiscoveryWithResult |
CaptureHelperDeviceDecodedDataDelegate |
didReceiveDecodedData |
CaptureHelperDevicePowerDelegate |
didChangePowerState, didChangeBatteryLevel |
CaptureHelperDeviceButtonsDelegate |
didChangeButtonsState |
CaptureHelperErrorDelegate |
didReceiveError |
CaptureHelperLoggerDelegate |
didReceiveLogTrace |
CaptureHelperAllDelegate |
All of the above |
When a view requiring scanning capabilities becomes active, it calls pushDelegate to receive the notifications. When it becomes inactive, it calls popDelegate and the previous delegate, if any, receives the notifications again.
The first notification a newly pushed delegate may receive is didNotifyArrivalForDevice for each device already connected, even if other delegates already received it.
To keep this sample simple, all the delegates are implemented in a single object, CaptureSdkHandler, pushed once. It forwards the events to the views with NotificationCenter:
class CaptureSdkHandler: NSObject,
CaptureHelperDevicePresenceDelegate,
CaptureHelperDiscoveryDelegate,
CaptureHelperDeviceDecodedDataDelegate,
CaptureHelperDevicePowerDelegate,
CaptureHelperErrorDelegate,
CaptureHelperLoggerDelegate {
// ...
}openWithAppInfo is the first method to call in order to use CaptureSDK. In this sample it is called in SingleEntryViewController.viewDidLoad.
The application information must match the information provided during the application registration in the Socket Mobile Developer portal.
let appInfo = SKTAppInfo()
appInfo.appKey = "<your AppKey>"
appInfo.appID = "ios:com.mycompany.myapp"
appInfo.developerID = "<your Developer ID>"
let captureHelper = CaptureHelper.sharedInstance
captureHelper.pushDelegate(captureHandler)
// Delegates are called on the main queue, so they can update the UI directly
captureHelper.dispatchQueue = DispatchQueue.main
// Enable the logs before opening CaptureSDK (requires CaptureHelperLoggerDelegate)
captureHelper.setLogger(enable: true)
captureHelper.openWithAppInfo(appInfo) { result in
print("Result of Capture initialization: \(result.rawValue)")
}This method must be called only once in the entire application. The open is asynchronous and its completion handler confirms whether CaptureSDK has been opened successfully.
NOTE: It is NOT recommended to close CaptureSDK, because this forces CaptureSDK to reinitialize the Socket Mobile devices the next time the application opens it. Closing CaptureSDK does not save power. If a view does not want to receive events anymore, it just calls popDelegate.
SocketCam (camera scanning) is enabled by default. SocketCam C820 and C860 are notified through didNotifyArrivalForDevice like any other device, and a scan is started with the trigger (see TriggerViewController.swift):
device.setTrigger(.start) { result, propertyResult in
print("setTrigger start: \(result.rawValue)")
}Socket Mobile devices are mainly Bluetooth Classic and are transitioning to Bluetooth Low Energy. Your customers may still use Bluetooth Classic devices for a while, so think about supporting both discoveries. You can choose which one to launch first.
Both discoveries are started with CaptureHelper.sharedInstance.addBluetoothDevice. No device manager is needed.
Start the Bluetooth LE discovery from your own UI or flow, for instance the peripherals settings of your app:
CaptureHelper.sharedInstance.addBluetoothDevice(.bluetoothLowEnergy) { result in
print("addBluetoothDevice - Bluetooth Low Energy: \(result.rawValue)")
}For each discovered device, the CaptureHelperDiscoveryDelegate method didDiscoverDevice is called. Keep the discovered devices in an array to display them in your own UI:
func didDiscoverDevice(_ device: SKTCaptureDiscoveredDeviceInfo) {
print("didDiscoverDevice: \(device.name ?? "") - \(device.identifierUuid ?? "")")
if !discoveredDevices.contains(where: { $0.identifierUuid == device.identifierUuid }) {
discoveredDevices.append(device)
myDiscoveryTableView.reloadData()
}
}The discovery ends once its timeout has elapsed and didEndDiscoveryWithResult is called:
func didEndDiscoveryWithResult(_ result: SKTResult) {
print("didEndDiscoveryWithResult: \(result.rawValue)")
}To connect to a discovered Bluetooth LE device, call connectToDiscoveredDevice on CaptureHelper:
CaptureHelper.sharedInstance.connectToDiscoveredDevice(selectedDiscoveredDevice) { result in
print("connectToDiscoveredDevice: \(result.rawValue)")
}Once connected, the device is notified through didNotifyArrivalForDevice, which is the place to add it to your list of connected devices.
CaptureHelper.sharedInstance.addBluetoothDevice(.bluetoothClassic) { result in
print("addBluetoothDevice - Bluetooth Classic: \(result.rawValue)")
}This presents the iOS native picker. Bluetooth Classic devices are paired at the iOS Bluetooth Settings level, so once selected, your application receives didNotifyArrivalForDevice directly, provided the device is in Application Mode. Scan the following barcode to set it:
To remove a device and prevent it from reconnecting automatically, call removeBluetoothDevice with the device's GUID:
if let deviceGuid = device.deviceInfo.guid {
CaptureHelper.sharedInstance.removeBluetoothDevice(deviceGuid) { result in
print("removeBluetoothDevice: \(result.rawValue)")
}
}The device is then notified through didNotifyRemovalForDevice.
CaptureHelperDevicePresenceDelegate method called when a device is connected. The device can be SocketCam or any other Socket Mobile device supported by CaptureSDK. Keep the CaptureHelperDevice to call its features later (trigger, friendly name, battery level, etc.).
CaptureHelperDevicePresenceDelegate method called when a device is no longer available (disconnected or removed).
CaptureHelperDeviceDecodedDataDelegate method called when a barcode or an NFC tag has been successfully read:
func didReceiveDecodedData(_ decodedData: SKTCaptureDecodedData?, fromDevice device: CaptureHelperDevice, withResult result: SKTResult) {
if result == .E_NOERROR, let decodedData = decodedData {
print("Decoded Data: \(decodedData.stringFromDecodedData() ?? "")")
} else if result == .E_CANCEL {
// The user cancelled a SocketCam scan
}
}CaptureHelperDevicePowerDelegate method called when the battery level of a device has changed. Use SKTHelper.getCurrentLevel(fromBatteryLevel:) to get the percentage.
The Single Partnership feature makes a Socket Mobile Bluetooth LE device discoverable only by the host it has been partnered with. It is configured with CaptureHelper:
// Through the Web UI, with or without prompt
CaptureHelper.sharedInstance.setSinglePartnership(.webUI) { result in
print("setSinglePartnership: \(result.rawValue)")
}
// Or with your own Service UUID
CaptureHelper.sharedInstance.setSinglePartnership(.uuid, uuidString: "<your service UUID>") { result in
print("setSinglePartnership: \(result.rawValue)")
}
CaptureHelper.sharedInstance.getSinglePartnershipStatus { result, status in
print("getSinglePartnershipStatus: \(status?.rawValue ?? 0)")
}To reset the Single Partnership of a connected device:
device.setResetSinglePartnership { result in
print("setResetSinglePartnership: \(result.rawValue)")
}If a feature is not exposed by CaptureHelper or CaptureHelperDevice, create an extension and use getProperty / setProperty with an SKTCaptureProperty. An example is shown in CaptureHelperExtension.swift.

