Capture Helper for Objective C

Capture Helper is the easiest and recommended way for adding the barcode scanning capabilities to an Objective-C application.

One line of import is required:

#import "SktCaptureHelper.h"

The Socket Mobile Capture SDK uses CocoaPods.org as dependency manager. It is very simple to add Capture to your application by adding this line in the file named Podfile at the root of your application directory:

target 'myApp'
  pod 'SKTCaptureObjC', '~> 1.0'

Once this is done, run the command pod install from the root of your project.

Note

This command will create a workspace file for the project. The project file should be closed and the workspace file should be used instead of the project file.

For more information about Cocoapods, please check: https://guides.cocoapods.org/

Capture Helper Initialization

To use Capture Helper, create an instance of Capture Helper and set the Capture Helper delegate by using either the View Controller that implement the Capture Helper protocol interface or any other class implementing that protocol.

Once the delegate is set then Capture Helper can be opened.

Here is a typical Capture Helper initialization:

@interface ViewController () <SKTCaptureHelperDelegate>
{
    SKTCaptureHelper* _capture;
}

#pragma mark - view lifecycle
- (void)viewDidLoad {
    [super viewDidLoad];

    SKTAppInfo* appInfo = [SKTAppInfo new];
    appInfo.DeveloperID =@"ef32e4f4-d321-98cd-a211-543eb762298c";
    appInfo.AppKey = @"MC0CFA3t4MzdzMa97KWFTN8HEtGeTcYsAhUAk8EQbbFnSBgW2eocLcjiaUhjrY8=";
    appInfo.AppID = @"ios:com.socketmobile.MyTestApp";
    _capture = [SKTCaptureHelper sharedInstance];
    [_capture setDispatchQueue:dispatch_get_main_queue()];
    [_capture pushDelegate:self];
    [_capture openWithAppInfo:appInfo completionHandler:^(SKTResult result) {
        NSLog(@"opening capture returns: %ld", result);
    }];
}

From that moment on, if a Socket Cordless Scanner connects to the host, the delegate didNotifyArrivalForDevice:withResult is invoked and when the user then scans a barcode the delegate didReceiveDecodedData:fromDevice:withResult is invoked with the decoded data, the device information and the overall result code.

Note

The line [_capture setDispatchQueue:dispatch_get_main_queue()]; is used to be able to update the UI controls directly from any Capture Helper delegates or completion handler by making sure they are invoked in the dispatch main queue context.

This line of code is optional if none of the delegate or completion handlers have to update a UI control.

Minimal Implementation

Assuming the only thing that matters to the application is to receive the decoded data, a minimal implementation could be as simple as:

//
//  ViewController.m
//  CaptureHelperUi
//
//  Created by Eric Glaenzer on 5/4/17.
//  Copyright © 2017 Socket Mobile. All rights reserved.
//

#import "ViewController.h"
#import "SktCaptureHelper.h"

@interface ViewController () <SKTCaptureHelperDelegate>
{
    SKTCaptureHelper* _capture;
}
@property (weak, nonatomic) IBOutlet UITextView *decodedDataText;

@end

@implementation ViewController

#pragma mark - view lifecycle
- (void)viewDidLoad {
    [super viewDidLoad];
    _decodedDataText.layer.cornerRadius = 0.0f;
    _decodedDataText.layer.masksToBounds = YES;
    _decodedDataText.layer.borderColor = [[UIColor blackColor] CGColor];
    _decodedDataText.layer.borderWidth = 1.0f;

    SKTAppInfo* appInfo = [SKTAppInfo new];
    appInfo.DeveloperID =@"ef32e4f4-d321-98cd-a211-543eb762298c";
    appInfo.AppKey = @"MC0CFA3t4MzdzMa97KWFTN8HEtGeTcYsAhUAk8EQbbFnSBgW2eocLcjiaUhjrY8=";
    appInfo.AppID = @"ios:com.socketmobile.MyTestApp";
    _capture = [SKTCaptureHelper sharedInstance];
    [_capture setDispatchQueue:dispatch_get_main_queue()];
    [_capture pushDelegate:self];
    [_capture openWithAppInfo:appInfo completionHandler:^(SKTResult result) {
        NSLog(@"opening capture returns: %ld", result);
    }];
}

#pragma mark - view UI control handlers
- (IBAction)clearDecodedDataText:(id)sender {
    self.decodedDataText.text = @"";
}

#pragma mark - SKTCaptureHelper delegate
/**
 * called when decoded data are received from a device
 *
 * @param decodedData contains the decoded data
 * @param device identifies the device from which the decoded data comes from
 * @param result contains an error if something wrong happen while getting the decoded data
 * or if the SoftScan trigger operation has been cancelled
 */
-(void)didReceiveDecodedData:(SKTCaptureDecodedData*) decodedData fromDevice:(SKTCaptureHelperDevice*) device withResult:(SKTResult) result{
      NSString *text = [[decodedData.stringFromDecodedData componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] componentsJoinedByString:@""];
      self.decodedDataText.text =
      [self.decodedDataText.text stringByAppendingString: text];
      self.decodedDataText.text =
      [self.decodedDataText.text stringByAppendingString: @"\r\n"];
}
@end

Of course this sample won’t help the user if there is an error occurring while using Capture.

For this reason we highly recommend to handle the error event and to collect the Capture version information.

Device Connection Awareness

This is a great way to confirm if the scanner is correctly connected to the application or even to drive the UI when the scanner connects or scans.

Capture Helper automatically opens the device as soon as it receives internally its connection notification. Once the device is open, Capture Helper fires the device arrival event with a SKTCaptureHelperDevice instance to represent this particular device.

That SKTCaptureHelperDevice instance is used at each event related to the device and can be used to retrieve or configure the device settings.

For an application to be aware of device connections, there are 2 delegates that can be monitored: didNotifyArrivalForDevice:withResult and didNotifyRemovalForDevice:withResult.

Here is a sample code on how to register for these delegates along with the didReceiveDecodedData:fromDevice:withResult delegate to get the device decoded data and the didReceiveError:withMessage delegate to trace the eventual errors that could come from Capture.

The device name is displayed in a status text field in this particular case:

//
//  ViewController.m
//  CaptureHelperUi
//
//  Created by Eric Glaenzer on 5/4/17.
//  Copyright © 2017 Socket Mobile. All rights reserved.
//

#import "ViewController.h"
#import "SktCaptureHelper.h"

@interface ViewController () <SKTCaptureHelperDelegate>
{
    SKTCaptureHelper* _capture;
}
@property (weak, nonatomic) IBOutlet UILabel *status;
@property (weak, nonatomic) IBOutlet UITextView *decodedDataText;

@end

@implementation ViewController

#pragma mark - view lifecycle
- (void)viewDidLoad {
    [super viewDidLoad];
    _decodedDataText.layer.cornerRadius = 0.0f;
    _decodedDataText.layer.masksToBounds = YES;
    _decodedDataText.layer.borderColor = [[UIColor blackColor] CGColor];
    _decodedDataText.layer.borderWidth = 1.0f;

    SKTAppInfo* appInfo = [SKTAppInfo new];
    appInfo.DeveloperID =@"ef32e4f4-d321-98cd-a211-543eb762298c";
    appInfo.AppKey = @"MC0CFA3t4MzdzMa97KWFTN8HEtGeTcYsAhUAk8EQbbFnSBgW2eocLcjiaUhjrY8=";
    appInfo.AppID = @"ios:com.socketmobile.MyTestApp";
    _capture = [SKTCaptureHelper sharedInstance];
    [_capture setDispatchQueue:dispatch_get_main_queue()];
    [_capture pushDelegate:self];
    [_capture openWithAppInfo:appInfo completionHandler:^(SKTResult result) {
        NSLog(@"opening capture returns: %ld", result);
    }];
}

#pragma mark - Utilities
-(void)updateStatusFromDevices:(NSArray*)devices {
    NSString* status = @"";
    for(SKTCaptureHelperDevice* device in devices){
        status = [NSString stringWithFormat:@"%@ %@", status, device.friendlyName];
    }
    if(status.length == 0){
        status = @"No device connected";
    }
    self.status.text = status;
}

#pragma mark - view UI control handlers
- (IBAction)clearDecodedDataText:(id)sender {
    self.decodedDataText.text = @"";
}

#pragma mark - SKTCaptureHelper delegate
/**
 * called when a error needs to be reported to the application
 *
 * @param error contains the error code
 * @param message contains an optional message, can be null
 */
-(void)didReceiveError:(SKTResult) error withMessage:(NSString*) message{
    NSLog(@"didReceiveError %ld with message: %@", error, message);
}

/**
 * called when a device has connected to the host
 *
 * @param device identifies the device that just connected
 * @param result contains an error if something went wrong during the device connection
 */
-(void)didNotifyArrivalForDevice:(SKTCaptureHelperDevice*) device withResult:(SKTResult) result{
    [self updateStatusFromDevices:[_capture getDevicesList]];
}

/**
 * called when a device has disconnected from the host
 *
 * @param device identifies the device that has just disconnected
 * @param result contains an error if something went wrong during the device disconnection
 */
-(void)didNotifyRemovalForDevice:(SKTCaptureHelperDevice*) device withResult:(SKTResult) result{
    [self updateStatusFromDevices:[_capture getDevicesList]];
}

/**
 * called when decoded data are received from a device
 *
 * @param decodedData contains the decoded data
 * @param device identifies the device from which the decoded data comes from
 * @param result contains an error if something wrong happen while getting the decoded data
 * or if the SoftScan trigger operation has been cancelled
 */
-(void)didReceiveDecodedData:(SKTCaptureDecodedData*) decodedData fromDevice:(SKTCaptureHelperDevice*) device withResult:(SKTResult) result{
      NSString *text = [[decodedData.stringFromDecodedData componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] componentsJoinedByString:@""];
      self.decodedDataText.text =
      [self.decodedDataText.text stringByAppendingString: text];
      self.decodedDataText.text =
      [self.decodedDataText.text stringByAppendingString: @"\r\n"];
}
@end

Configuring Capture to Handle Contactless Reader/Writer Connection (D600)

Please refer to Starting Contactless Reader/Writer Auto Discovery Mode.

Adding Features to Capture Helper

If your application needs a feature that is not implemented in Capture Helper, that feature can be added by creating a Capture Helper extension class, copy-pasting a similar feature from the provided CaptureHelper, and modifying it to match your application needs.

It is important to handle new Capture Helper code into your application Capture Helper extension class, because that way when you install a new version of the Socket Mobile Capture, it won’t override your specific Capture Helper features.

Here is an example of such Capture Helper extension and in particular an extension for the Capture Helper Device:

#import "SktCaptureHelper.h"

@interface SKTCaptureHelperDevice (SKTCaptureHelperDevice_Additions)
@property NSString* BatteryLevel; // keep the battery level for later use
-(NSString*)getTypeString; // return the device type as a string
@end

And the implementation could be something like:

@implementation SKTCaptureHelperDevice (SKTCaptureHelperDevice_Additions)
@dynamic BatteryLevel;
-(NSString*)getTypeString{
    NSString* type = @"unknown type";
    switch(self.deviceType){
        case SKTCaptureDeviceTypeNone:
            type = @"unknow type";
            break;
        case SKTCaptureDeviceTypeScanner7:
            type = @"CHS 7";
            break;
        case SKTCaptureDeviceTypeScanner7x:
            type = @"CHS 7X";
            break;
        case SKTCaptureDeviceTypeScanner7xi:
            type = @"CHS 7Xi";
            break;
        case SKTCaptureDeviceTypeScanner8qi:
            type = @"CHS 8Qi";
            break;
        case SKTCaptureDeviceTypeScanner8ci:
            type = @"CHS 8ci";
            break;
        case SKTCaptureDeviceTypeScanner9:
            type = @"CRS 9";
            break;
        case SKTCaptureDeviceTypeSoftScan:
            type = @"SoftScan";
            break;
        case SKTCaptureDeviceTypeScannerD700:
            type = @"Socket D700";
            break;
        case SKTCaptureDeviceTypeScannerD730:
            type = @"Socket D730";
            break;
        case SKTCaptureDeviceTypeScannerD750:
            type = @"Socket D750";
            break;
        case SKTCaptureDeviceTypeScannerD600:
            type = @"Socket D600";
            break;
        case SKTCaptureDeviceManagerTypeBle:
            type = @"Socket BLE Device Manager";
            break;
    }
    return type;
}
@end