Camera scanning with SocketCam¶
The application receives the decoded data with the same APIs that are used for the physical readers and they can also be used to manage some of the SocketCam properties the same way you would for a physical reader.
There are 2 requirements to use SocketCam:
The application needs to enable SocketCam.
A software trigger button must be added in the UI to trigger a read from SocketCam using the camera.
CaptureSDK is designed to handle multiple devices at once. An application can enable SocketCam and can still use a physical scanner without any conflicts.
In some deployments when such configuration exists, often the application hides the software trigger button as soon as a physical scanner is connected, and vice-versa when the physical scanner disconnects, the application shows the software trigger button to be able to use the device camera as scanner.
For SocketCam C860 which is an enhanced version of SocketCam C820, you also need to add the following key to your Info.plist
: LSApplicationQueriesSchemes
(Queried URL Schemes) with a new item: sktcompanion
(in lower case).
In order to use it you have to install Socket Mobile Companion on your device.
You can find more details about SocketCam C860 on our website.
Enabling SocketCam¶
In order to enable SocketCam, property SocketCamStatus needs to be set to ‘ENABLE’. This is a persistent setting that can be set during the first launch of the application
let capture = CaptureHelper.sharedInstance
// retrieve the current status of SocketCam
capture.getSocketCamStatusWithCompletionHandler( {(getResult, socketCamStatus) in
if getResult == SKTCaptureErrors.E_NOERROR {
if socketCamStatus == .disable {
// set the status of SocketCam to enable
capture.setSocketCamStatus(.enable, withCompletionHandler: { (setResult) in
if setResult == SKTCaptureErrors.E_NOERROR {
print("SocketCam enabled returned \(setResult.rawValue)")
}
})
}
}
})
SKTCaptureHelper* capture = [SKTCaptureHelper sharedInstance];
// retrieve the current status of SocketCam
[capture getSocketCamStatusWithConfirmationHandler:^(SKTResult result, SKTCaptureSocketCam status) {
if (SKTSUCCESS(result)) {
if (status == SKTCaptureSocketCamDisable) {
// set the status of SocketCam to enable
[capture setSocketCamStatus:SKTCaptureSocketCamEnable completionHandler:^(SKTResult result) {
NSLog(@"setSocketCamStatus enabled returns %ld", (long)result);
}];
}
}
}];
Once the SocketCam status is enabled then the Device Arrival Event is fired and it is ready for the application to use it. See details of Device Arrival are here.
Using SocketCam¶
Now the application is ready to use the camera as a barcode scanner. Usually an application shows a “Scan” trigger button somewhere in the UI to start scanning for a barcode.
Note
Applications can use physical buttons such as the volume up or down button to act as a trigger button.
The way to start the scanning operation is by sending the setTrigger property. Here are the details to Trigger Scanner.
Calling setTrigger
method will internally call setProperty method. Successful completion will launch the SocketCam view controller.
The view controller closes once a barcode is scanned.
The decoded data are sent in a data event as it does for any other physical scanner.
Trigger continuous mode will launch the scan view and continue to scan barcodes until the view is dismissed by the user
Triggering SocketCam¶
With the new version of CaptureSDK available, you will be able to present SocketCam as you wish in a custom view, a popover and still in full screen. Now you decide where and when triggering and removing SocketCam view controller in your application’s flow. And this is your responssability to handle the presentation and the dismissal of the view controller. However, there’s a minimal size of 250px x 250px to observe in order to display all SocketCam’s UI elements.
NB: the signature of the method to trigger has now an extra return parameter: propertyObject
. It contains a dictionary that contains the SocketCam view controller that you
can use as you wish in full screen, as a subview or a popover. Here are the sample code to do it in those various ways.
Also, you are responsible of dismissing/removing the SocketCam view controller from your application flow.
Sample code for SocketCam presented in Full screen¶
var mySocketCamViewController: UIViewController?
// trigger status of SocketCam
captureDevice.setTrigger(.start, withCompletionHandler: { result, propertyObject in
DispatchQueue.main.async {
if let anObject = propertyObject?.object,
let dic = anObject as? [String: Any],
let objectType = dic["SKTObjectType"] as? String,
objectType == "SKTSocketCamViewControllerType",
let socketCamViewController = dic["SKTSocketCamViewController"] as? UIViewController {
socketCamViewController.modalPresentationStyle = .fullScreen
self.present(socketCamViewController, animated: true)
self.mySocketCamViewController = socketCamViewController // retain the socketcam view controller for the dismiss later
}
}
})
extension YourViewController: CaptureHelperDeviceDecodedDataDelegate {
// This delegate is called each time a decoded data is read from a scanner or SocketCam
func didReceiveDecodedData(_ decodedData: SKTCaptureDecodedData?, fromDevice device: CaptureHelperDevice, withResult result:SKTResult) {
print("--->>> didReceiveDecodedData for device: \(device.deviceInfo.deviceType)")
if result == .E_NOERROR {
let str = decodedData!.stringFromDecodedData()
print("Decoded Data \(String(describing: str))")
}
DispatchQueue.main.async {
// Dismiss and reset your socketCamViewController
self.mySocketCamViewController?.dismiss(animated: true, completion: {
self.mySocketCamViewController = nil
})
}
}
}
UIViewController *mySocketCamViewController;
// trigger status of SocketCam
[_captureDevice setTrigger:SKTCaptureTriggerStart completionHandler:^(SKTResult result, SKTCaptureProperty *propertyResult) {
NSDictionary *socketCamDictionary = (NSDictionary *)propertyResult.Object;
NSString *objectType = [socketCamDictionary objectForKey:@"SKTObjectType"];
if ([objectType isEqualToString:@"SKTSocketCamViewControllerType"]) {
dispatch_async(dispatch_get_main_queue(), ^{
if ([[socketCamDictionary objectForKey:@"SKTSocketCamViewController"] isKindOfClass:[UIViewController class]]) {
UIViewController* socketCamViewController = (UIViewController *)[socketCamDictionary objectForKey:@"SKTSocketCamViewController"];
if (socketCamViewController != nil) {
socketCamViewController.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:socketCamViewController animated:YES completion:^{
}];
self.mySocketCamViewController = socketCamViewController; // retain the socketcam view controller for the dismiss later
}
}
});
}
}];
// This delegate is called each time a decoded data is read from a scanner or SocketCam
-(void)didReceiveDecodedData:(SKTCaptureDecodedData *)decodedData fromDevice:(SKTCaptureHelperDevice *)device withResult:(SKTResult)result {
if (SKTSUCCESS(result)) {
dispatch_async(dispatch_get_main_queue(), ^{
self.decodedDataTextField.text = decodedData.stringFromDecodedData;
// Dismiss and reset your socketCamViewController
[self.mySocketCamViewController dismissViewControllerAnimated:YES completion:^{
self.mySocketCamViewController = nil;
}];
});
}
}
Sample code for SocketCam presented as a subview¶
NB: the SocketCam view controller as a subview can't have a size less than 250 x 250px.
var mySocketCamViewController: UIViewController?
var mySocketCamView: UIView? // can be also an @IBOutlet
// trigger status of SocketCam
captureDevice.setTrigger(.start, withCompletionHandler: { result, propertyObject in
DispatchQueue.main.async {
if let anObject = propertyObject?.object,
let dic = anObject as? [String: Any],
let objectType = dic["SKTObjectType"] as? String,
objectType == "SKTSocketCamViewControllerType",
let socketCamViewController = dic["SKTSocketCamViewController"] as? UIViewController {
self.mySocketCamViewController.view.frame = self.mySocketCamView.bounds
self.mySocketCamView?.addSubview(socketCamViewController.view)
self.mySocketCamViewController = socketCamViewController // retain the socketcam view controller for the dismiss later
}
}
})
extension YourViewController: CaptureHelperDeviceDecodedDataDelegate {
// This delegate is called each time a decoded data is read from a scanner or SocketCam
func didReceiveDecodedData(_ decodedData: SKTCaptureDecodedData?, fromDevice device: CaptureHelperDevice, withResult result:SKTResult) {
print("--->>> didReceiveDecodedData for device: \(device.deviceInfo.deviceType)")
if result == .E_NOERROR {
let str = decodedData!.stringFromDecodedData()
print("Decoded Data \(String(describing: str))")
}
DispatchQueue.main.async {
// Remove your socketCamViewController view from the parent and reset it
self.mySocketCamViewController?.view.removeFromSuperview()
self.mySocketCamViewController = nil
}
}
}
UIViewController *mySocketCamViewController;
UIView *mySocketCamView; // can also be an @IBOutlet
// trigger status of SocketCam
[_captureDevice setTrigger:SKTCaptureTriggerStart completionHandler:^(SKTResult result, SKTCaptureProperty *propertyResult) {
NSDictionary *socketCamDictionary = (NSDictionary *)propertyResult.Object;
NSString *objectType = [socketCamDictionary objectForKey:@"SKTObjectType"];
if ([objectType isEqualToString:@"SKTSocketCamViewControllerType"]) {
dispatch_async(dispatch_get_main_queue(), ^{
if ([[socketCamDictionary objectForKey:@"SKTSocketCamViewController"] isKindOfClass:[UIViewController class]]) {
UIViewController* socketCamViewController = (UIViewController *)[socketCamDictionary objectForKey:@"SKTSocketCamViewController"];
if (socketCamViewController != nil) {
self.mySocketCamViewController.view.frame = self.mySocketCamView.bounds;
[self.mySocketCamView addSubview:socketCamViewController.view];
self.mySocketCamViewController = socketCamViewController; // retain the socketcam view controller for the dismiss later
}
}
});
}
}];
// This delegate is called each time a decoded data is read from a scanner or SocketCam
-(void)didReceiveDecodedData:(SKTCaptureDecodedData *)decodedData fromDevice:(SKTCaptureHelperDevice *)device withResult:(SKTResult)result {
if (SKTSUCCESS(result)) {
dispatch_async(dispatch_get_main_queue(), ^{
self.decodedDataTextField.text = decodedData.stringFromDecodedData;
// Remove your socketCamViewController view from the parent and reset it
[self.mySocketCamViewController.view removeFromSuperview];
self.mySocketCamViewController = nil;
});
}
}
Sample code for SocketCam presented as a popover¶
NB: the SocketCam view controller as a popover can't have a size less than 250 x 250px.
var mySocketCamViewController: UIViewController?
var mySocketCamView: UIView? // can be also an @IBOutlet
// trigger status of SocketCam
captureDevice.setTrigger(.start, withCompletionHandler: { result, propertyObject in
DispatchQueue.main.async {
if let anObject = propertyObject?.object,
let dic = anObject as? [String: Any],
let objectType = dic["SKTObjectType"] as? String,
objectType == "SKTSocketCamViewControllerType",
let socketCamViewController = dic["SKTSocketCamViewController"] as? UIViewController {
let popOverVC = socketCamViewController.popoverPresentationController
popOverVC?.delegate = self
popOverVC?.sourceView = // a view of your chosing
socketCamViewController.preferredContentSize = CGSize(width: myWidth, height: myHeight)
socketCamViewController.modalPresentationStyle = .popover
self.present(socketCamViewController, animated: true)
self.mySocketCamViewController = socketCamViewController // retain the socketcam view controller for the dismiss later
}
}
})
extension YourViewController: CaptureHelperDeviceDecodedDataDelegate {
// This delegate is called each time a decoded data is read from a scanner or SocketCam
func didReceiveDecodedData(_ decodedData: SKTCaptureDecodedData?, fromDevice device: CaptureHelperDevice, withResult result:SKTResult) {
print("--->>> didReceiveDecodedData for device: \(device.deviceInfo.deviceType)")
if result == .E_NOERROR {
let str = decodedData!.stringFromDecodedData()
print("Decoded Data \(String(describing: str))")
}
DispatchQueue.main.async {
// Dismiss and reset your socketCamViewController
self.mySocketCamViewController?.dismiss(animated: true, completion: {
self.mySocketCamViewController = nil
})
}
}
}
UIViewController *mySocketCamViewController;
UIView *mySocketCamView; // can also be an @IBOutlet
// trigger status of SocketCam
[_captureDevice setTrigger:SKTCaptureTriggerStart completionHandler:^(SKTResult result, SKTCaptureProperty *propertyResult) {
NSDictionary *socketCamDictionary = (NSDictionary *)propertyResult.Object;
NSString *objectType = [socketCamDictionary objectForKey:@"SKTObjectType"];
if ([objectType isEqualToString:@"SKTSocketCamViewControllerType"]) {
dispatch_async(dispatch_get_main_queue(), ^{
if ([[socketCamDictionary objectForKey:@"SKTSocketCamViewController"] isKindOfClass:[UIViewController class]]) {
UIViewController* socketCamViewController = (UIViewController *)[socketCamDictionary objectForKey:@"SKTSocketCamViewController"];
if (socketCamViewController != nil) {
UIPopoverPresentationController *popOverVC = socketCamViewController.popoverPresentationController;
popOverVC.delegate = self;
popOverVC.sourceView = // a view of your chosing
socketCamViewController.preferredContentSize = CGSizeMake(myWidth, myHeight);
socketCamViewController.modalPresentationStyle = UIModalPresentationPopover;
[self presentViewController:socketCamViewController animated:YES completion:^{
}];
self.mySocketCamViewController = socketCamViewController; // retain the socketcam view controller for the dismiss later
}
}
});
}
}];
// This delegate is called each time a decoded data is read from a scanner or SocketCam
-(void)didReceiveDecodedData:(SKTCaptureDecodedData *)decodedData fromDevice:(SKTCaptureHelperDevice *)device withResult:(SKTResult)result {
if (SKTSUCCESS(result)) {
dispatch_async(dispatch_get_main_queue(), ^{
self.decodedDataTextField.text = decodedData.stringFromDecodedData;
// Dismiss and reset your socketCamViewController
[self.mySocketCamViewController dismissViewControllerAnimated:YES completion:^{
self.mySocketCamViewController = nil;
}];
});
}
}
Depending on how you want to display SocketCam in your application, it may require to show or hide the close button of the SocketCam view controller. Ideally, you can execute this at the device arrival of a SocketCam device, whether it is C820 or C860. In order to do so, add a specific configuration as follow.
let prop = SKTCaptureProperty()
prop.id = SKTCapturePropertyID.configuration
prop.type = .string
prop.stringValue = "SocketCamCloseButton=enabled" /// to show the close button
// OR
prop.stringValue = "SocketCamCloseButton=disabled" /// to hide the close button
self.captureHelper.setProperty(prop) { result, complete in
}
SKTCaptureProperty *prop = [SKTCaptureProperty new];
prop.ID = SKTCapturePropertyIDConfiguration;
prop.Type = SKTCapturePropertyTypeString;
prop.StringValue = @"SocketCamCloseButton=enabled"; /// to show the close button
// OR
prop.StringValue = @"SocketCamCloseButton=disabled"; /// to hide the close button
[_captureHelper setProperty:prop completionHandler:^(SKTResult result, SKTCaptureProperty *property) {
}];
Receive Data¶
To receive the data in your app, you have to implement the decodedData delegate. See details of here.
Upgrading to SocketCam C860 (Pro)¶
SocketCam C860 (Pro) is an enhanced and paid version of SocketCam C820. Users will have the option to upgrade to SocketCam Pro from the SocketCam view. There are no additional code changes required to support this.
If you choose to upgrade from SocketCam C820 to SocketCam C860, you will be redirected to the Socket Mobile Companion app to complete the upgrade process.
Click here to learn more about SocketCam Pro.