The Anyline Plugin

The Anyline SDK is based around the concept of plugins. Plugins can be brought in and configured to implement your scanning use case for your application.

This section describes how to implement and use the Anyline SDK plugins for iOS.

Overview

The examples provided here are based on a simple meter scanning use case. A more detailed discussion can be found in the next section, as well in the iOS API Reference.

Each use case needs the following components to scan:

  • A ScanPlugin - handles image processing and the scanning functionality itself

  • A ScanViewPlugin - handles the scan view user interface and visual feedback

  • A ScanView - a UIView that holds the camera and flash components and manages the aforementioned ScanViewPlugin and ScanPlugin

Adding an Anyline Plugin to your UIViewController

The first step is to add these three main components of Anyline to your UIViewController as properties: the Scan Plugin (handling the scanning part), the Scan View Plugin (handling the UI) and the Scan View (handling camera, flash and overall communication between Anyline components).

Begin by declaring the three components as properties on your UIViewController:

  • Swift

  • Objective-C

// The three components of an Anyline use case
var scanViewPlugin: ALScanViewPlugin!
var scanPlugin: ALScanPlugin!
var scanView: ALScanView!
// The three components of an Anyline use case
@property (nonatomic, strong) ALScanViewPlugin *scanViewPlugin;
@property (nonatomic, strong) ALScanPlugin *scanPlugin;
@property (nonatomic, strong) ALScanView *scanView;

ScanViewPlugin

Initialize the Scan View Plugin

By this time, you should already have initialized the Anyline SDK with the license key for your application. For more details, please see Initialize the Anyline SDK.

The first component you would have to create is the Scan View Plugin, which can be done using a configuration object in JSON form, which we henceforth call a config. This config can either derive from a file included with the app bundle or a hard-coded string variable.

For a discussion on how to configure your plugins, please go to Plugin Configuration.

For the code snippets below, and the ones the follow, it is assumed that the Anyline framework has been imported into the source code where you are using Anyline:

  • Swift

  • Objective-C

import Anyline
#include <Anyline/Anyline.h>

The example below shows one of the more straightforward ways that you can use to create a Scan View Plugin. Here, JSONConfigDictionary is formed by calling the asJSONObject extension method on an NSString object that holds the plugin config in a JSON representation.

The following code is run during the viewDidLoad part of the UIViewController:

  • Swift

  • Objective-C

let JSONConfigString = "{\"flashConfig\":{\"mode\":\"manual\"},\"cameraConfig\":{\"captureResolution\":\"1080p\",\"zoomGesture\":true},\"viewPluginConfig\":{\"pluginConfig\":{\"id\":\"METER\",\"meterConfig\":{\"scanMode\":\"auto_analog_digital_meter\"}}}}"

if let JSONConfigDictionary = JSONConfigString.asJSONObject() {
    do {
        try self.scanViewPlugin = ALScanViewPlugin(jsonDictionary: JSONConfigDictionary)
    } catch {
        // handle the error.
    }
}
NSString *JSONConfigString = @"{\"flashConfig\":{\"mode\":\"manual\"},\"cameraConfig\":{\"captureResolution\":\"1080p\",\"zoomGesture\":true},\"viewPluginConfig\":{\"pluginConfig\":{\"id\":\"METER\",\"meterConfig\":{\"scanMode\":\"auto_analog_digital_meter\"}}}}";
NSDictionary *JSONConfigDictionary = [JSONConfigString asJSONObject];

NSError *error;
self.scanViewPlugin = [[ALScanViewPlugin alloc] initWithJSONDictionary:JSONConfigDictionary error:&error];
if (error) {
  // handle the error.
}

While creating each component, we recommend that you pass in an NSError object to a class’s initializer to get a chance to handle any error conditions.

Note that if an error object has been passed back with a non-null value, then the object associated with the initializer was likely not successfully created, so the error object can provide some insight into the problem and potentially suggest a course of action.

ScanPlugin

Obtain and configure the Scan Plugin

Once the Scan View Plugin is successfully created, the Scan Plugin can be readily obtained through its associated scanPlugin property.

  • Swift

  • Objective-C

self.scanPlugin = self.scanViewPlugin.scanPlugin
self.scanPlugin.delegate = self
self.scanPlugin = self.scanViewPlugin.scanPlugin;
self.scanPlugin.delegate = self;

The main reason you would need to have a reference to a Scan Plugin object is for you to set its delegate to this class, the UIViewController where your scanning use case resides.

Implementing the ScanPluginDelegate

Then, you would also have to declare your UIViewController as implementing the ALScanPluginDelegate protocol:

  • Swift

  • Objective-C

class MyScanViewController: UIViewController, ALScanPluginDelegate { … }
class MyScanViewController: UIViewController <ALScanPluginDelegate>

Afterwards you will need to implement a number of methods from that protocol. This will be discussed in more detail in a subsequent section.

Just to give you a small example, you can add the following method implementation in your view controller:

  • Swift

  • Objective-C

func scanPlugin(_ scanPlugin: ALScanPlugin, resultReceived scanResult: ALScanResult) {
    print("the result: \(scanResult.pluginResult.meterResult?.value ?? "")")
}
- (void)scanPlugin:(ALScanPlugin *)scanPlugin resultReceived:(ALScanResult *)scanResult {
    NSLog(@"the result: ", scanResult.pluginResult.meterResult.value);
}

ScanView

Lastly, you will want to instantiate a ScanView with the previously created ScanViewPlugin and a ScanViewConfig object.

Create a ScanViewConfig object

A ScanViewConfig object controls how the ScanView appears and behaves. Like the ScanViewPluginConfig before it, it is a JSON object that can be created from a JSON string:

  • Swift

  • Objective-C

let scanViewConfigJSONStr = "{\"flashConfig\":{\"mode\":\"manual\"},\"cameraConfig\":{\"captureResolution\":\"1080p\",\"zoomGesture\":true},\"viewPluginConfig\":{\"pluginConfig\":{\"id\":\"METER\",\"meterConfig\":{\"scanMode\":\"auto_analog_digital_meter\"}}}}"
if let scanViewConfigDict = scanViewConfigJSONStr.asJSONObject() {
    do {
        let scanViewConfig = try ALScanViewConfig(jsonDictionary: scanViewConfigDict)
    } catch {
        // handle error.
    }
}
NSError *error;

NSString *scanViewConfigJSONStr = @"{\"flashConfig\":{\"mode\":\"manual\"},\"cameraConfig\":{\"captureResolution\":\"1080p\",\"zoomGesture\":true},\"viewPluginConfig\":{\"pluginConfig\":{\"id\":\"METER\",\"meterConfig\":{\"scanMode\":\"auto_analog_digital_meter\"}}}}";
NSDictionary *scanViewConfigDictionary = [scanViewConfigJSONStr asJSONObject];

ALScanViewConfig *scanViewConfig = [[ALScanViewConfig alloc] initWithJSONDictionary:scanViewConfigDictionary error:&error];
if (error) {
  // handle the error
}

Create the Scan View and add it to the view hierarchy

With the ScanViewPlugin object and the ScanViewConfig object (as well as the CGRect frame for the view), you can now create the ScanView object and start the camera module:

  • Swift

  • Objective-C

do {
    self.scanView = try ALScanView(frame: frame,
                                   scanViewPlugin: self.scanViewPlugin,
                                   scanViewConfig: scanViewConfig)
} catch {
    // handle errors
}

self.view.addSubview(self.scanView)
self.scanView.startCamera()
self.scanView = [[ALScanView alloc] initWithFrame:frame
                                   scanViewPlugin:scanViewPlugin
                                   scanViewConfig:scanViewConfig
                                            error:&error];

if (error) {
  // handle the error
}

[self.view addSubview:self.scanView];
[self.scanView startCamera];

Final steps

Once the scan view is displayed and running, the scan view plugin can be started. This will finally begin to process the frame images that are regularly supplied by the camera module.

The following call is preferably done in the viewDidAppear part of the UIViewController:

  • Swift

  • Objective-C

do {
    try self.scanViewPlugin.start()
} catch {
    // handle errors
}
[self.scanViewPlugin startWithError:&error];

Stopping the scan process

The scan process normally ends when the plugin manages to return a result by calling the delegate method -[scanPlugin:resultReceived:]. You would then have an ALResult object containing the scan results (to be discussed later).

You can also stop scanning at any time by calling stop on the ScanViewPlugin.

  • Swift

  • Objective-C

self.scanViewPlugin.stop()
[self.scanViewPlugin stop];

Complete Source Code

The source code for the above example is shown here:

  • Swift

  • Objective-C

import Anyline

class SwiftScanViewController: UIViewController {

    var scanViewPlugin: ALScanViewPlugin!

    var scanPlugin: ALScanPlugin!

    var scanView: ALScanView!

    enum Constants {
        static let meterConfig = "{\"viewPluginConfig\":{\"pluginConfig\":{\"id\":\"my-meter-scan\",\"meterConfig\":{\"scanMode\":\"auto_analog_digital_meter\"}},\"cutoutConfig\":{\"width\":750,\"alignment\":\"top_half\",\"ratioFromSize\":{\"width\":3,\"height\":1}},\"scanFeedbackConfig\":{\"strokeWidth\":2,\"beepOnResult\":true,\"vibrateOnResult\":true,\"blinkAnimationOnResult\":true}}}"
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        do {
            guard let configDict = Constants.meterConfig.asJSONObject() else {
                // handle error
                return;
            }

            // Step 1. Create a scan view plugin config with a JSON dictionary. You may use `asJSONObject`
            // to transform a JSON string representation of a config into a dictionary suitable for
            // the Anyline iOS SDK.
            let scanViewPluginConfig = try ALScanViewPluginConfig(jsonDictionary: configDict)

            // Step 2. Create the scan view plugin using the scan view plugin config. A cutout config and a
            // scan feedback config can also be included, but in this case, they are omitted, so defaults
            // will be used.
            // NOTE: creating a scan view plugin with a config from a JSON string is also possible:
            // self.scanViewPlugin = ALScanViewPluginFactory.withJSONDictionary(configDict)
            self.scanViewPlugin = try ALScanViewPlugin(config: scanViewPluginConfig)

            // Step 3. To get notified for the scan result, set an ALScanPluginDelegate. This property is
            // owned by the scan plugin, which you can find from the scan view plugin.
            self.scanPlugin = self.scanViewPlugin.scanPlugin
            self.scanPlugin.delegate = self

            // Step 4. Using the scan view plugin as parameter, create the scan view and add it to the
            // view hierarchy.
            self.scanView = try ALScanView(frame: .zero, scanViewPlugin: self.scanViewPlugin)
            self.view.addSubview(self.scanView)
            self.scanView.translatesAutoresizingMaskIntoConstraints = false
            self.scanView.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
            self.scanView.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true
            self.scanView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
            self.scanView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true

            // Step 5. Start the scan view camera, as well as the scan view plugin to start processing
            // frames (in viewDidAppear:)
            self.scanView.startCamera()

        } catch {
            // handle the error(s).
        }
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        do {
            try self.scanViewPlugin.start()
        } catch {
            // handle the error.
        }
    }
}

extension SwiftScanViewController: ALScanPluginDelegate {

    func scanPlugin(_ scanPlugin: ALScanPlugin, resultReceived scanResult: ALScanResult) {
        print("the scan result: \(scanResult.pluginResult.asJSONString())")
    }
}
#import <Anyline/Anyline.h>

@interface ALSimpleScanViewController () <ALScanPluginDelegate>

@property (nonatomic, strong) ALScanView *scanView;

@property (nonatomic, strong) ALScanViewPlugin *scanViewPlugin;

@property (nonatomic, strong) ALScanPlugin *scanPlugin;

@property (nonatomic, readonly) NSString *scanViewConfigJSONStr;

@end

NSString * const kConfigJSONString = @"{\"cameraConfig\":{\"captureResolution\":\"1080p\",\"pictureResolution\":\"1080p\"},\"flashConfig\":{\"mode\":\"manual_on\",\"alignment\":\"top_left\"},\"viewPluginConfig\":{\"pluginConfig\":{\"id\":\"com.anyline.configs.plugin.auto-meter\",\"meterConfig\":{\"scanMode\":\"auto_analog_digital_meter\"},\"cancelOnResult\":true},\"cutoutConfig\":{\"animation\":\"fade\",\"maxWidthPercent\":\"100%\",\"maxHeightPercent\":\"100%\",\"width\":750,\"alignment\":\"top_half\",\"ratioFromSize\":{\"width\":3,\"height\":1},\"offset\":{\"x\":0,\"y\":0},\"cropOffset\":{\"x\":0,\"y\":0},\"cropPadding\":{\"x\":0,\"y\":0},\"cornerRadius\":8,\"strokeColor\":\"#8be9fd\",\"strokeWidth\":3,\"outerColor\":\"#282A36\",\"feedbackStrokeColor\":\"#0099FF\",\"outerAlpha\":0.85},\"scanFeedbackConfig\":{\"style\":\"rect\",\"visualFeedbackRedrawTimeout\":100,\"strokeWidth\":2,\"strokeColor\":\"#0099FF\",\"fillColor\":\"#220099FF\",\"beepOnResult\":true,\"vibrateOnResult\":true,\"blinkAnimationOnResult\":true}}}";

@implementation ALSimpleScanViewController

- (void)viewDidLoad {
[super viewDidLoad];

    // Step 1. Create a scan view plugin config with a JSON dictionary. You may use `asJSONObject`
    // to transform the JSON string of a plugin config into a dictionary representation suitable for
    // the Anyline iOS SDK.

    NSDictionary *configDict = [kConfigJSONString asJSONObject];

    NSError *error;
    ALScanViewPluginConfig *scanViewPluginConfig = [[ALScanViewPluginConfig alloc]
                                                    initWithJSONDictionary:configDict error:&error];
    if (!scanViewPluginConfig) {
        if (error) {
            NSLog(@"there was an error: %@", error.localizedDescription);
            // handle the error.
        }
        return;
    }

    // Step 2. Create the scan view plugin using the scan view plugin config. A cutout config and a
    // scan feedback config can also be included, but in this case, they are omitted, so defaults
    // will be used.
    //
    // NOTE: creating a scan view plugin with a config from a JSON string is also possible:
    // self.scanViewPlugin = [ALScanViewPluginFactory withJSONDictionary:scanViewConfigDict];

    self.scanViewPlugin = [[ALScanViewPlugin alloc] initWithConfig:scanViewPluginConfig error:&error];
    if (!self.scanViewPlugin) {
        if (error) {
            NSLog(@"there was an error: %@", error.localizedDescription);
        }
        return;
    }

    // Step 3. To get notified for the scan result, set an ALScanPluginDelegate. This property is
    // owned by the scan plugin, which you can find from the scan view plugin.
    self.scanPlugin = self.scanViewPlugin.scanPlugin;
    self.scanPlugin.delegate = self;

    // Step 4. Using the scan view plugin as parameter, create the scan view and add it to the
    // view hierarchy.
    self.scanView = [[ALScanView alloc] initWithFrame:CGRectZero
                                       scanViewPlugin:self.scanViewPlugin error:&error];
    if (!self.scanView) {
        if (error) {
            NSLog(@"there was an error: %@", error.localizedDescription);
            // handle the error.
        }
        return;
    }

    [self.view addSubview:self.scanView];

    self.scanView.translatesAutoresizingMaskIntoConstraints = false;
    [self.scanView.leftAnchor constraintEqualToAnchor:self.view.leftAnchor].active = YES;
    [self.scanView.rightAnchor constraintEqualToAnchor:self.view.rightAnchor].active = YES;
    [self.scanView.topAnchor constraintEqualToAnchor:self.view.topAnchor].active = YES;
    [self.scanView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor].active = YES;


    // Step 5. Start the scan view camera, as well as the scan view plugin to start processing
    // frames (in viewDidAppear:)
    [self.scanView startCamera];
}

- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];

    NSError *error;
    BOOL success = [self.scanViewPlugin startWithError:&error];
    if (!success) {
        // handle the error.
        if (error) {
            NSLog(@"there was an error: %@", error.localizedDescription);
        }
    }
}

// MARK: - ALScanPluginDelegate

- (void)scanPlugin:(ALScanPlugin *)scanPlugin resultReceived:(ALScanResult *)scanResult {
    NSLog(@"Scan Result: %@", scanResult.pluginResult.meterResult.value);
}

@end

Questions or need further assistance? Please reach out to the Anyline Support Helpdesk.