Plugin Configuration

Anyline offers a wide range of scanning capabilities covering use cases for ID, License Plate, Barcode, Meter, and so on. A Plugin is a component of the Anyline SDK that implements a scanning use case.

It is possible to configure the scanning behavior either through a JSON configuration or via code. We’ll cover both in this section.

The Configuration object and JSON

A config object determines how a ScanPlugin, ScanViewPlugin, or ScanView appears or operates. Each of these three components can be initialized using the following config objects:

  • An ALPluginConfig configures an ALScanPlugin,

  • An ALScanViewPluginConfig configures an ALScanViewPlugin, and

  • An ALScanViewConfig configures an ALScanView.

To initialize a config object, pass a child (or children) config object corresponding to its type, or its JSON representation in an NSDictionary, as a parameter in its initializer.

For an in-depth guide on how to structure your configurations through JSON, please refer to Anyline View Configuration.

To give an example, take the ALPluginConfig class (NOTE: this is simplified):

  • Swift

  • Objective-C

class ALPluginConfig {
  var identifier: String
  var cancelOnResult: NSNumber?
  var startScanDelay: NSNumber?
  var barcodeConfig: ALBarcodeConfig?
  // ...
}
@interface ALPluginConfig : NSObject

@property (nonatomic, copy)             NSString *identifier;
@property (nonatomic, nullable, strong) NSNumber *cancelOnResult;
@property (nonatomic, nullable, strong) NSNumber *startScanDelay;

@property (nonatomic, nullable, strong) ALBarcodeConfig *barcodeConfig;
// ...

@end

It contains an identifier string for the plugin, as well as configuration details for all possible use cases supported by Anyline. An example of such a use case, Meter Scanning, can be traced back to a property named meterConfig under ALPluginConfig, the interface of which looks like the following:

  • Swift

  • Objective-C

class ALMeterConfig {
  var scanMode: ALMeterConfigScanMode
  var minConfidence: NSNumber?
}
@interface ALMeterConfig : NSObject

@property (nonatomic, assign) ALMeterConfigScanMode *scanMode;
@property (nonatomic, nullable, strong) NSNumber *minConfidence;

@end

To construct an Anyline component or its config object, it is usually more convenient to prepare an NSDictionary holding the JSON configuration for your use case, and then create the Anyline component or config object using initializer methods that accept the dictionary as a parameter:

  • Swift

  • Objective-C

// the JSON configuration string (here hardcoded, but more typically loaded from a file included with the app bundle)
let pluginConfigStr = "{\"pluginConfig\":...}"

// convert to a dictionary type (handle if null)
let pluginConfigDict: [AnyHashable: Any]! = pluginConfigStr.asJSONObject();

// initialize the Scan Plugin with the config dictionary
do {
  let scanPlugin = try ALScanPlugin(jsonDictionary: pluginConfigDict)
} catch {
  // check the error object and handle if necessary
}
NSError *error;

// the JSON configuration string (here hardcoded, but more typically loaded from a file included with the app bundle)
NSString *pluginConfigStr = @"{\"pluginConfig\":...}";

// convert to a dictionary type
NSDictionary *pluginConfigDict = [pluginConfigStr asJSONObject];

// initialize the Scan Plugin with the config dictionary
ALScanPlugin *scanPlugin = [[ALScanPlugin alloc] initWithJSONDictionary:pluginConfigDict error:&error];

// check the error object and handle if necessary

Alternative methods to initialize these components are also available, for when you want additional run-time control over the configuration, or if you want to forego passing an error object. However, provided that you structure your config JSON correctly, the above example remains the simplest way to set up your Anyline scan components.

You may use the String extension methods toJSONObject and asJSONObject to transform a string containing your JSON configuration into a suitable [AnyHashable: Any] type for your Anyline class initializers.

If deserializing the JSON string is not possible, then both functions will return a null value. In addition, the first version is a throwing method which allows you to inspect the error object in case the operation failed to complete successfully.

Configs

PluginConfig

A PluginConfig determines, among other things, the scanning use case and essential characteristics of an Anyline plugin’s scanning behavior. Its main purpose is to be used to initialize an ALScanPlugin object, whose constructor can take this config object as a parameter.

An ALPluginConfig object will also include further customization possibilities for specific uses cases.

It is perhaps easier to see this config object through its JSON representation (which can also be obtained via the asJSONString method). Here we give one such representation of a scan plugin config designed to scan electric meters:

"pluginConfig": {
    "meterConfig": {
        "scanMode": "auto_analog_digital_meter",
        "id": "METER",
        "cancelOnResult": true
    },
}

From the JSON above, one can see that the PluginConfig consists of a property named meterConfig, which further contains specific information that it is going to be run with the auto_analog_digital_meter mode.

For additional details regarding plugin configs, check ALPluginConfig.h.

To initialize a ScanPlugin, pass a dictionary based on the JSON config object above:

  • Swift

  • Objective-C

do {
  let scanPlugin = try ALScanPlugin(jsonDictionary: configDict)
} catch {
  // handle the error object
}
ALScanPlugin *scanPlugin = [[ALScanPlugin alloc] initWithJSONDictionary:configDict error:&error];

or with an ALPluginConfig object (which is essentially formed using the same configDict object):

  • Swift

  • Objective-C

do {
  let pluginConfig = try ALPluginConfig(jsonDictionary: configDict)
  // check error to see if you can safely proceed with the next operation
  let scanPlugin = try ALScanPlugin(config: pluginConfig)
} catch {
  // handle the error object
}
ALPluginConfig *pluginConfig = [[ALPluginConfig alloc] initWithJSONDictionary:configDict error:&error];
// check error to see if you can safely proceed with the next operation
ALScanPlugin *scanPlugin = [[ALScanPlugin alloc] initWithConfig:pluginConfig];
As soon as a Plugin component is instantiated with its config object, you will no longer be able to change the object during runtime.

ScanViewPluginConfig

A ScanViewPluginConfig is an aggregate config structure that holds a PluginConfig, a CutoutConfig, and a ScanFeedbackConfig:

  • The PluginConfig, as discussed in the previous section, determines the use case and scanning behavior of the plugin.

  • A CutoutConfig includes details about the camera view cutout to be displayed on the scan view, including where it lies within the scan view, or how much space the cutout region occupies.

  • A ScanFeedbackConfig holds properties to help you configure how the objects of interest positioned within the camera cutout are visually treated, and what happens when a scan result is found.

An example JSON representation of a ScanViewPluginConfig is shown below for the meter use case:

"viewPluginConfig": {
    "pluginConfig": {
        "id": "METER",
        "meterConfig": {
            "scanMode": "auto_analog_digital_meter"
        },
        "cancelOnResult": true
    },
    "cutoutConfig": {
        "maxWidthPercent": "75%",
        "maxHeightPercent": "90%",
        "ratioFromSize": { "width": 7, "height": 3 },
        "cornerRadius": 8,
        "strokeColor": "#8be9fd",
        "strokeWidth": 3
    },
    "scanFeedbackConfig": {
        "style": "rect",
        "strokeWidth": 2,
        "strokeColor": "#0099FF",
        "fillColor": "#220099FF",
        "beepOnResult": true,
        "vibrateOnResult": true,
        "blinkAnimationOnResult": true
    }
}

To create a ScanViewPluginConfig, prepare an NSDictionary from a JSON object like the one above, and then pass it as a parameter to its initializer:

  • Swift

  • Objective-C

do {
  let scanViewPluginConfig = try ALScanViewPluginConfig(jsonDictionary: configDict)
  let scanViewPlugin = try ALScanViewPlugin(config: scanViewPluginConfig)
} catch {
  // handle the error object
}
ALScanViewPluginConfig *scanViewPluginConfig = [[ALScanViewPluginConfig alloc] initWithJSONDictionary:configDictionary
                                                                                                error:&error];
ALScanViewPlugin *scanViewPlugin = [[ALScanViewPlugin alloc] initWithConfig:scanViewPluginConfig error:&error];
When creating an ALScanViewPlugin in this way, a corresponding ScanPlugin is also automatically created (based on the details found within pluginConfig node of the JSON) and becomes part of the ScanViewPlugin.

Or alternatively, you can create an ALScanViewPluginConfig object using an ALPluginConfig, along with (optionally) an ALCutoutConfig and an ALScanFeedbackConfig:

  • Swift

  • Objective-C

do {
  let scanViewPluginConfig = try ALScanViewPluginConfig(pluginConfig: pluginConfig,
                                                        cutoutConfig: cutoutConfig,
                                                  scanFeedbackConfig: scanFeedbackConfig)
  let scanViewPlugin = try ALScanViewPlugin(config: scanViewPluginConfig)
} catch {
  // handle any errors appropriately
}
ALScanViewPluginConfig *scanViewPluginConfig = [[ALScanViewPluginConfig alloc] initWithPluginConfig:pluginConfig
                                                                                       cutoutConfig:cutoutConfig
                                                                                 scanFeedbackConfig:scanFeedbackConfig
                                                                                              error:&error];
// check the error object and handle appropriately
ALScanViewPlugin *scanViewPlugin = [[ALScanViewPlugin alloc] initWithConfig:scanViewPluginConfig
                                                                      error:&error];
// check the error object and handle appropriately

ScanViewConfig

An ALScanViewConfig contains configuration details controlling the camera and flash (torch) modules of the ScanView, both of which are also represented in Anyline by their own config classes (respectively, ALCameraConfig and ALFlashConfig).

An aggregated example of the configs in JSON form can look like the following:

"cameraConfig": {
    "captureResolution": "1080p"
},
"flashConfig": {
    "mode": "manual",
    "alignment": "bottom_left",
    "offset": { "x": 0, "y": -40 }
},

To create an ALScanViewConfig, form it in the usual way via JSON…​

  • Swift

  • Objective-C

do {
  let scanViewConfig = try ALScanViewConfig(jsonDictionary: JSONDict)
  let scanView = try ALScanView(frame: frame, scanViewPlugin: scanViewPlugin, scanViewConfig: scanViewConfig)
} catch {
  // handle errors
}
ALScanViewConfig *scanViewConfig = [[ALScanViewConfig alloc] initWithJSONDictionary:JSONDict error:&error];
ALScanView *scanView = [[ALScanView alloc] initWithFrame:frame
                                          scanViewPlugin:scanViewPlugin
                                          scanViewConfig:scanViewConfig
                                                   error:&error];

or through its constituent config types:

  • Swift

  • Objective-C

do {
  let scanViewConfig = try ALScanViewConfig(cameraConfig: cameraConfig, flashConfig: flashConfig)
  let scanView = try ALScanView(frame: frame, scanViewPlugin: scanViewPlugin, scanViewConfig: scanViewConfig)
} catch {
  // handle errors
}
ALScanViewConfig *scanViewConfig = [[ALScanViewConfig alloc] initWithCameraConfig:cameraConfig
                                                                      flashConfig:flashConfig];
ALScanView *scanView = [[ALScanView alloc] initWithFrame:frame
                                          scanViewPlugin:scanViewPlugin
                                          scanViewConfig:scanViewConfig
                                                   error:&error];

CameraConfig and FlashConfig

The FlashConfig allows you to configure the torch module, which can include the visibility and location of the flash toggle button within the scan view. You can also set a custom image to be used in the torch toggle view.

The CameraConfig defines a few properties determining the camera module used. At the moment, the main property is zoomGesture, which, when true, allows pinch-to-zoom behavior for the running scan view.

For the time being, only 1080 is supported for the Camera Config property captureResolution.

It is possible to create ALCameraConfig and ALFlashConfig individually, if needed. Each of the two config objects can be used to create an ALScanViewConfig.

  • Swift

  • Objective-C

do {
  let cameraConfig = try ALCameraConfig(jsonDictionary: cameraJSONConfig)
  let flashConfig = try ALFlashConfig(jsonDictionary: flashJSONConfig)
  let scanViewConfig = try ALScanViewConfig(cameraConfig: cameraConfig, flashConfig: flashConfig)
} catch {
  // handle errors
}
ALFlashConfig *cameraConfig = [[ALCameraConfig alloc] initWithJSONDictionary:cameraJSONConfig]
ALFlashConfig *flashConfig = [[ALFlashConfig alloc] initWithJSONDictionary:flashJSONConfig]
ALScanViewConfig *scanViewConfig = [[ALScanViewConfig alloc] initWithCameraConfig:cameraConfig flashConfig:flashConfig];

To set a custom image asset for the flash toggle, first add it to the application bundle and then indicate its name in the flashConfig.image attribute of the Anyline configuration you are going to use.

Setting a custom flash toggle image
{
  "flashConfig": {
    "mode": "manual_off",
    "alignment": "top_left",
    "image": "my_custom_flash_image"
  }
}
The image ideally should have dimensions of 28 x 28, and have the PNG format with transparency. Add scaled versions of the same image (@2x, @3x) as necessary.

Tips and Best Practices

Convenience class initializers

Each Anyline Config class also has a .withXXX() class initializer method that you can use in place of a regular initializer.

For instance,

  • Swift

  • Objective-C

ALScanViewPluginConfig.withPluginConfig(pluginConfig)
[ALScanViewPluginConfig withPluginConfig:pluginConfig];

is an equivalent call to

  • Swift

  • Objective-C

try? ALScanViewPluginConfig.init(pluginConfig: pluginConfig)
[[ALScanViewPluginConfig alloc] initWithPluginConfig:pluginConfig error:&error];

With the first method, no do-catch is required, but it may still potentially return null values if there are errors initializing the scan view plugin config.

Use a single JSON file per use case

In most scenarios, you should find it most convenient to keep a single JSON file holding together the aggregate configuration for your scanning use case, with all component configs listed together, in your application bundle.

One example below (sample_id_config.json) describes a possible configuration for a Universal (Latin) ID use case:

sample_id_config.json
{
  "cameraConfig": {
    "captureResolution": "1080p"
  },
  "flashConfig": {
    "mode": "manual",
    "alignment": "top_right",
    "offset": { "x": 0, "y": 0 }
  },
  "viewPluginConfig": {
    "pluginConfig": {
      "id": "com.anyline.configs.plugin.id_mrz_dl",
      "universalIdConfig": {
        "allowedLayouts": {
          "mrz": [],
          "drivingLicense": [],
          "idFront": []
        },
        "alphabet": "latin",
        "drivingLicense": {
          "surname": {"scanOption": 0, "minConfidence": 40},
          "givenNames": {"scanOption": 0, "minConfidence": 40},
          "dateOfBirth": {"scanOption": 0, "minConfidence": 50},
          "placeOfBirth": {"scanOption": 1, "minConfidence": 50},
          "dateOfIssue": {"scanOption": 0, "minConfidence": 50},
          "dateOfExpiry": {"scanOption": 1, "minConfidence": 50},
          "authority": {"scanOption": 1, "minConfidence": 30},
          "documentNumber": {"scanOption": 0, "minConfidence": 40},
          "categories": {"scanOption": 1, "minConfidence": 30},
          "address": {"scanOption": 1}
        },
        "idFront": {
          "surname": {"scanOption": 0, "minConfidence": 60},
          "givenNames": {"scanOption": 0, "minConfidence": 60},
          "dateOfBirth": {"scanOption": 0, "minConfidence": 60},
          "placeOfBirth": {"scanOption": 1, "minConfidence": 60},
          "dateOfExpiry": {"scanOption": 1, "minConfidence": 60},
          "cardAccessNumber": {"scanOption": 1, "minConfidence": 60},
          "documentNumber": {"scanOption": 0, "minConfidence": 60},
          "nationality": {"scanOption": 1, "minConfidence": 60}
        }
      },
      "cancelOnResult": true
    },
    "cutoutConfig": {
      "animation": "fade",
      "maxWidthPercent": "90%",
      "maxHeightPercent": "90%",
      "width": 0,
      "alignment": "center",
      "ratioFromSize": { "width": 86, "height": 54 },
      "offset": { "x": 0, "y": 60 },
      "cropOffset": { "x": 0, "y": 0 },
      "cropPadding": { "x": 0, "y": 0 },
      "cornerRadius": 8,
      "strokeColor": "#8BE9FD",
      "strokeWidth": 2,
      "feedbackStrokeColor": "#0099FF",
      "outerColor": "#000000",
      "outerAlpha": 0.3
    },
    "scanFeedbackConfig": {
      "style": "animated_rect",
      "strokeWidth": 0,
      "strokeColor": "#8be9fd",
      "fillColor": "#908be9fd",
      "cornerRadius": 2,
      "beepOnResult": true,
      "vibrateOnResult": false,
      "blinkAnimationOnResult": false
    }
  }
}

Using this config JSON, you should be able to form the ALScanViewPlugin, ALScanPlugin and ALScanView in the following way:

  • Swift

  • Objective-C

// get the JSON string for the aggregate config
let JSONString: String = self.getJSONString(for: "sample_id_config")

// NOTE: implementation found elsewhere, simplified here
let frame: CGRect = self.getScanViewFrame();
do {
    let configJSONDictionary = try JSONString.toJSONObject()

    // takes "viewPluginConfig", which covers "pluginConfig", "cutoutConfig", and "scanFeedbackConfig"
    let scanViewPlugin = try ALScanViewPlugin(jsonDictionary: configJSONDictionary)

    // get the scan plugin from the scan view plugin
    let scanPlugin = scanViewPlugin.scanPlugin
    scanPlugin.delegate = self

    // takes "cameraConfig" and "flashConfig" from the configJSONDictionary
    let scanViewConfig = try ALScanViewConfig(jsonDictionary: configJSONDictionary)

    let scanView = try ALScanView(frame: frame, scanViewPlugin: scanViewPlugin, scanViewConfig: scanViewConfig)

} catch {
    // handle the error object.
}

// Next steps
// - implement the ALScanPluginDelegate
// - install the scan view into the view hierarchy
// - start the scan view camera
// - start the scan view plugin
// get the JSON string for the aggregate config
NSString *JSONString = [self getJSONStringForFile:@"sample_id_config"];

// NOTE: implementation found elsewhere, simplified here
CGRect frame = [self getScanViewFrame];

NSError *error;
NSDictionary *configJSONDictionary = [JSONString toJSONObject:&error];
if (!configJSONDictionary) {
    // handle the error object
}

// takes "viewPluginConfig", which covers "pluginConfig", "cutoutConfig", and "scanFeedbackConfig"
ALScanViewPlugin *scanViewPlugin = [[ALScanViewPlugin alloc] initWithJSONDictionary:configJSONDictionary error:&error];
if (!scanViewPlugin) {
    if (error) {
        NSLog(@"there was an error: %@", error.localizedDescription);
    }
    return;
}

// get the scan plugin from the scan view plugin
ALScanPlugin *scanPlugin = scanViewPlugin.scanPlugin;
scanPlugin.delegate = self;

// takes "cameraConfig" and "flashConfig" from the configJSONDictionary
ALScanViewConfig *scanViewConfig = [[ALScanViewConfig alloc] initWithJSONDictionary:configJSONDictionary error:&error];

// take scanViewPlugin, and scanViewConfig
ALScanView *scanView = [[ALScanView alloc] initWithFrame:frame
                                          scanViewPlugin:scanViewPlugin
                                          scanViewConfig:scanViewConfig
                                                   error:&error];
if (!scanView) {
    // handle the error object
}

// Next steps
// - implement the ALScanPluginDelegate
// - install the scan view into the view hierarchy
// - start the scan view camera
// - start the scan view plugin

Building components programmatically

Constructing each component programmatically takes a bit more work, but certainly is still possible. Doing this offers you the possibility to dynamically adapt your scanning use case based on your application’s workflow or user input.

Of course, you can still combine the two approaches and construct components with JSON and then adjust them afterward in your code.

In the example below, we show how to build up such a use case for your app. Assume here that we’re working here inside your view controller:

  • Swift

  • Objective-C

func setupScanView() {
    do {
        let meterConfig = ALMeterConfig()

        // configure the scan plugin (Dial Meter)
        meterConfig.scanMode = .dialMeter()

        let pluginConfig = ALPluginConfig()
        pluginConfig.meterConfig = meterConfig
        pluginConfig.identifier = "Dial Meter"
        pluginConfig.cancelOnResult = NSNumber(booleanLiteral: true)

        // create the cutout config

        var JSONString = ALCutoutConfig.default().asJSONString()
        var JSONDictionary = JSONString.asJSONObject() ?? [:]

        let cutoutConfig = ALCutoutConfig(alignment: .topHalf,
                                          animation: .none,
                                          ratioFrom: .init(width: 3, height: 1),
                                          offset: .zero,
                                          width: 720,
                                          maxHeightPercent: 100,
                                          maxWidthPercent: 100,
                                          cornerRadius: 4,
                                          strokeWidth: 2,
                                          strokeColor: "0099FF",
                                          feedbackStrokeColor: "0099FF",
                                          outerColor: "30000000",
                                          cropOffset: .zero,
                                          cropPadding: .zero,
                                          image: nil)

        // create the scan feedback config using a JSON dictionary: when a value for a property is not set
        // some default will be used.

        JSONString = ALScanFeedbackConfig.default().asJSONString()
        JSONDictionary = JSONString.asJSONObject() ?? [:]
        JSONDictionary["beepOnResult"] = NSNumber(booleanLiteral: true)
        JSONDictionary["vibrateOnResult"] = NSNumber(booleanLiteral: true)

        let scanFeedbackConfig = ALScanFeedbackConfig.withJSONDictionary(JSONDictionary)

        // assemble and create the scan view plugin config
        let scanViewPluginConfig = try ALScanViewPluginConfig(pluginConfig: pluginConfig,
                                                              cutoutConfig: cutoutConfig,
                                                              scanFeedbackConfig: scanFeedbackConfig)

        // Create the scan view plugin
        self.scanViewPlugin = try ALScanViewPlugin(config: scanViewPluginConfig)


        // Note that you don't need to instantiate an ALScanPlugin yourself --
        // the ScanViewPlugin already creates this, and you simply access its scanPlugin property.
        self.scanViewPlugin.scanPlugin.delegate = self

        // create the camera config: no customization was done, so was left as it is
        let cameraConfig = ALCameraConfig.default()

        // configure and create the flash config
        JSONString = ALFlashConfig.default().asJSONString()
        JSONDictionary = JSONString.asJSONObject() ?? [:]

        JSONDictionary["alignment"] = "bottom_right"

        let flashConfig = ALFlashConfig.withJSONDictionary(JSONDictionary)

        // note that you can pass null to either config here and the config defaults would be used.
        let scanViewConfig = ALScanViewConfig(cameraConfig: cameraConfig, flashConfig: flashConfig)

        self.scanView = try ALScanView(frame: .zero,
                                       scanViewPlugin: scanViewPlugin,
                                       scanViewConfig: scanViewConfig)
    } catch {
        // handle errors
    }
}
- (void)setupScanView {
    // It is not shown here, but if an NSError is passed as a parameter make sure to
    // check it after each call and handle appropriately.
    NSError *error;

    // configure the scan plugin (Dial Meter)
    ALMeterConfig *meterConfig = [[ALMeterConfig alloc] init];
    meterConfig.scanMode = [ALMeterConfigScanMode dialMeter];

    ALPluginConfig *pluginConfig = [[ALPluginConfig alloc] init];
    pluginConfig.meterConfig = meterConfig;
    pluginConfig.identifier = @"Dial Meter";
    pluginConfig.cancelOnResult = @(YES);

    // create the cutout config
    NSString *JSONString = [[ALCutoutConfig defaultCutoutConfig] asJSONString];
    NSMutableDictionary *JSONDictionary = [NSMutableDictionary dictionaryWithDictionary:[JSONString asJSONObject]];

    // configure the cutout config using a memberwise initializer
    ALCutoutConfig *cutoutConfig = [[ALCutoutConfig alloc] initWithAlignment:ALCutoutAlignmentTopHalf
                                                                   animation:ALCutoutAnimationStyleNone
                                                               ratioFromSize:CGSizeMake(3, 1)
                                                                      offset:CGPointZero
                                                                       width:720
                                                            maxHeightPercent:100
                                                             maxWidthPercent:100
                                                                cornerRadius:4
                                                                 strokeWidth:2
                                                                 strokeColor:@"0099FF"
                                                         feedbackStrokeColor:@"0099FF"
                                                                  outerColor:@"30000000"
                                                                  cropOffset:CGPointZero
                                                                 cropPadding:CGSizeZero
                                                                       image:nil];

    // create the scan feedback config using a JSON dictionary: when a value for a property is not set
    // some default will be used.
    JSONString = [[ALScanFeedbackConfig defaultScanFeedbackConfig] asJSONString];
    JSONDictionary = [NSMutableDictionary dictionaryWithDictionary:[JSONString asJSONObject]];
    JSONDictionary[@"beepOnResult"] = @(YES);
    JSONDictionary[@"vibrateOnResult"] = @(YES);
    ALScanFeedbackConfig *scanFeedbackConfig = [ALScanFeedbackConfig withJSONDictionary:JSONDictionary];

    // assemble and create the scan view plugin config
    ALScanViewPluginConfig *scanViewPluginConfig = [[ALScanViewPluginConfig alloc] initWithPluginConfig:pluginConfig
                                                                                           cutoutConfig:cutoutConfig
                                                                                     scanFeedbackConfig:scanFeedbackConfig
                                                                                                  error:&error];

    // Create the scan view plugin
    self.scanViewPlugin = [[ALScanViewPlugin alloc] initWithConfig:scanViewPluginConfig error:&error];

    // Note that you don't need to instantiate an ALScanPlugin yourself --
    // the ScanViewPlugin already creates this, and you simply access its scanPlugin property.
    self.scanViewPlugin.scanPlugin.delegate = self;

    // create the camera config: no customization was done, so was left as it is
    ALCameraConfig *cameraConfig = [ALCameraConfig defaultCameraConfig];

    // configure and create the flash config
    JSONString = [[ALFlashConfig defaultFlashConfig] asJSONString];
    JSONDictionary = [NSMutableDictionary dictionaryWithDictionary:[JSONString asJSONObject]];
    JSONDictionary[@"alignment"] = @"bottom_right";
    ALFlashConfig *flashConfig = [ALFlashConfig withJSONDictionary:JSONDictionary];

    // note that you can pass null to either config here and the config defaults would be used.
    ALScanViewConfig *scanViewConfig = [[ALScanViewConfig alloc] initWithCameraConfig:cameraConfig
                                                                          flashConfig:flashConfig];

    self.scanView = [[ALScanView alloc] initWithFrame:CGRectZero
                                       scanViewPlugin:scanViewPlugin
                                       scanViewConfig:scanViewConfig
                                                error:&error];

    // Next Steps:
    // - install the scan view into the view hierarchy
    // - implement ALScanPluginDelegate for this view controller
    // - start the scan view camera
    // - start the scan view plugin
}

From the code example above, you can see how config objects can be created either through memberwise initialization (see: ALCutoutConfig) or with a JSON dictionary (for instance, ALScanFeedbackConfig). Each approach can be useful in their own way.

Memberwise initialization allows you to deterministically and completely define a config object, but it requires you to provide a value for every property in the config class.

The JSON approach shown above, on the other hand, is more flexible in allowing you to provide only non-default values for your config object, but you trade away a measure of type safety in using it.

Changing the config in real time

Your work flow may require you to update the scan config while leaving the scan view camera running. To take an example, for instance, you may have a UI switch over the scan view to toggle the cutout width-height ratio (eg 3:1, 2:1, and so on), and it was tapped by the user.

In such a case, it is important to remember that once a component has been created, it can no longer be modified. For example, changing the scan mode of a plugin is not possible:

  • Swift

  • Objective-C

self.scanViewPlugin.scanPlugin.pluginConfig.meterConfig?.scanMode = .dialMeter()
self.scanViewPlugin.scanPlugin.pluginConfig.meterConfig.scanMode = ALMeterConfigScanMode.dialMeter;

Instead, one should recreate the ScanViewPlugin (including ScanPlugin) with the updated configuration, and then either replace the old scan view in the view hierarchy with a new one made with the above components, or (to avoid the display momentarily showing a black background in the process) reload the existing scan view using its -setScanViewPlugin:error: method.

You can use the approach discussed in Building components programmatically as a starting point. Structuring how you assemble the components would go a long way towards making this manageable.

Below is an example of how you might for instance change the meter scan mode dynamically:

  • Swift

  • Objective-C

class func defaultScanViewPluginConfig() -> ALScanViewPluginConfig {
    // here we use a JSON-based config as a starting point. We will be introducing changes to it later.
    let configStr: String = type(of: self).defaultJSONConfigStr();
    return try! ALScanViewPluginConfig(jsonDictionary: configStr.asJSONObject() ?? [:])
}

class func defaultScanViewConfig() -> ALScanViewConfig {
    let configStr = type(of: self).defaultJSONConfigStr();
    return try! ALScanViewConfig.init(jsonDictionary: configStr.asJSONObject() ?? [:])
}

class func scanViewPlugin(meterScanMode: ALMeterConfigScanMode) -> ALScanViewPlugin {
    // all config properties that can change during runtime are confined within this method.
    // In this case, we want to have a scan view plugin created for a different meter scan mode than
    // the one currently running, based on for instance, the selection of the user.
    // The result is an ALScanViewPlugin that can be given to a new or existing ALScanView to
    // effect the scan mode change.
    let scanViewPluginConfig = type(of: self).defaultScanViewPluginConfig()
    let meterConfig = scanViewPluginConfig.pluginConfig.meterConfig
    meterConfig?.scanMode = meterScanMode
    return try! ALScanViewPlugin(config: scanViewPluginConfig)
    // handle any errors as needed.
}

func loadScanView(withMeterScanMode newScanMode: ALMeterConfigScanMode) {
    let newScanViewPlugin = type(of: self).scanViewPlugin(meterScanMode: newScanMode)
    try! self.scanView.setScanViewPlugin(newScanViewPlugin)
    // handle any errors as needed.
}
+ (ALScanViewPluginConfig *)defaultScanViewPluginConfig {
// here we use a JSON-based config as a starting point. We will be introducing changes to it later.
    NSString *configStr = [self.class defaultJSONConfigStr];
    return [[ALScanViewPluginConfig alloc] initWithJSONDictionary:[configStr asJSONObject] error:nil];
}

+ (ALScanViewConfig *)defaultScanViewConfig {
    NSString *configStr = [self.class defaultJSONConfigStr];
    return [[ALScanViewConfig alloc] initWithJSONDictionary:[configStr asJSONObject] error:nil];
}

+ (ALScanViewPlugin *)scanViewPluginWithMeterScanMode:(ALMeterConfigScanMode *)scanMode {
    // all config properties that can change during runtime are confined within this method.
    // In this case, we want to have a scan view plugin created for a different meter scan mode than
    // the one currently running, based on for instance, the selection of the user.
    // The result is an ALScanViewPlugin that can be given to a new or existing ALScanView to
    // effect the scan mode change.
    ALScanViewPluginConfig *scanViewPluginConfig = [self.class defaultScanViewPluginConfig];
    ALMeterConfig *meterConfig = scanViewPluginConfig.pluginConfig.meterConfig;

    meterConfig.scanMode = scanMode;

    NSError *error;
    ALScanViewPlugin *ret = [[ALScanViewPlugin alloc] initWithConfig:scanViewPluginConfig error:&error];

    // check and handle any errors
    return ret;
}

- (void)loadScanViewWithMeterScanMode:(ALMeterConfigScanMode *)newScanMode {
ALScanViewPlugin *newScanViewPlugin = [self.class scanViewPluginWithMeterScanMode:newScanMode];
    NSError *error;
    [self.scanView setScanViewPlugin:newScanViewPlugin error:&error];
    // handle the error, if non-null
}

With this in place, you can now simply call -loadScanViewWithMeterScanMode: and update the scan process with the new meter mode whenever it is required.


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