Migrating to Anyline 43.0.0

We have completed a major update to the Anyline classes in version 43. If you are coming from a previous Anyline version, it is helpful to go over some of the more significant changes in this page. For more detailed information about the classes, please head over to API Documentation.

It is also worth having a look at Plugin Configuration for more specific implementation guidance.

Video Tutorial

Here, you will be able to watch our V43 iOS SDK Upgrade Tutorial Video:

New way to initialize the scan plugin

ScanPlugins, ScanViewPlugins, etc., have seen changes in the ways in which they are initialized with Anyline 43. With earlier versions, in order to implement a scan use case, you would initialize the following components: 1) a ScanView, 2) a ScanViewPlugin, and 3) a ScanPlugin, and with the latter two components, concrete instances specific to your scanning use case should be chosen, such as for instance ALOCRScanPlugin and ALOCRScanViewPlugin:

Universal Serial Number use case with the previous Anyline version
ALOCRConfig *config = [self getOCRConfig];

NSError *error = nil;
ALOCRScanPlugin *serialNumberScanPlugin = [[ALOCRScanPlugin alloc] initWithPluginID:@"ANYLINE_OCR"
                                                                           delegate:self
                                                                          ocrConfig:config
                                                                              error:&error];

ALScanViewPluginConfig *scanViewPluginConfig = [self getScanViewPluginConfig];

serialNumberScanViewPlugin = [[ALOCRScanViewPlugin alloc] initWithScanPlugin:self.serialNumberScanPlugin
                                                        scanViewPluginConfig:scanViewPluginConfig];
[serialNumberScanViewPlugin addScanViewPluginDelegate:self];

self.scanView = [[ALScanView alloc] initWithFrame:frame
                                   scanViewPlugin:self.serialNumberScanViewPlugin];

With Anyline 43, it now becomes possible to only initialize an ALScanView and an ALScanViewPlugin (from the latter a suitable ALScanPlugin will also be automatically initialized), by passing in configuration objects targeting the scanning use case you intend to use.

Note that unlike with previous versions, ALScanViewPlugin and ALScanPlugin are the concrete classes to use in your application code – rather than say, ALOCRScanViewPlugin or ALOCRScanPlugin.

In addition, when initializing a ScanView, a scan view config object is required along with a scan view plugin.

  • Swift

  • Objective-C

let serialNrConfigJSONStr = self.getSerialNumberConfigJSONString()

// The SDK provides a number of NSString extension methods like this one to deserialize a
// potential JSON object(s) in an NSString (for more info, see ALJSONExtras extension in ALJSONUtilities.h)
guard let configJSONDict = serialNrConfigJSONStr.asJSONObject() else {
    // handle the error
    return;
}

do {
    let scanViewPlugin = try ALScanViewPlugin(jsonDictionary: configJSONDict)
} catch {
    // handle `error`, if necessary
}

// Notice here that `scanPlugin` is already a part of the ScanViewPlugin that had been created
scanViewPlugin.scanPlugin.delegate = self
let scanView = ALScanView(frame: frame, scanViewPlugin: scanViewPlugin)
view.addSubview(scanView)
scanView.startCamera()
NSString *serialNrConfigJSONStr = [self getSerialNumberConfigJSONString];

// The SDK provides a number of NSString extension methods like this one to deserialize a
// potential JSON object(s) in an NSString (for more info, see ALJSONExtras extension in ALJSONUtilities.h)
NSDictionary *configJSONDict = [serialNrConfigJSONStr asJSONObject];

ALScanViewPlugin *scanViewPlugin = [[ALScanViewPlugin alloc] initWithJSONDictionary:configJSONDict
                                                                              error:&error];
// handle `error`, if necessary

// Notice here that `scanPlugin` is already a part of the ScanViewPlugin that had been created
scanViewPlugin.scanPlugin.delegate = self;

ALScanView *scanView = [[ALScanView alloc] initWithFrame:frame
                                          scanViewPlugin:scanViewPlugin
                                                   error:&error];

[self.view add:scanView];

[self.scanView startCamera];

As you can see here, ALScanViewPlugin is the only scan view plugin class used, with the particulars for serial number scanning being completely defined within the configuration object defined here in JSON.

As discussed in Building Plugins Programmatically, it is possible to still create and structure Anyline components without JSON configs, however, our recommendation is to initialize components using properly-formatted JSON config files.

An ALScanView can also be initialized with an optional ALScanViewConfig parameter:

  • Swift

  • Objective-C

try ALScanView(frame: frame, scanViewPlugin: anyScanViewPlugin, scanViewConfig: anyScanViewConfig)
[[ALScanView alloc] initWithFrame:anyFrame scanViewPlugin:anyScanViewPlugin scanViewConfig:anyScanViewConfig error:&error]

After initializing an ALScanViewPlugin, modifying the attributes of its configuration object during runtime will not affect how the plugin behaves. Indeed, most config properties are now read-only.

To achieve the desired outcome, you should instead fully recreate a new ALScanViewPlugin instance with the updates to your config, and either attach it to the ScanView through its method -setScanViewPlugin:error:, or with a new ScanView instance via its initializer.

Composite Plugins

Nesting composite plugins is currently not supported in this version of Anyline.

The ALPluginComposite class, which replaces the old plugin composite type ALAbstractScanViewPluginComposite, is now (along with ALScanViewPlugin) a concrete implementation of the ALScanViewPluginBase protocol.

ALPluginComposite is now used for both parallel and sequential scanning modes, replacing the concrete classes (ALParallelScanViewPluginComposite and ALSerialScanViewPluginComposite) from the previous version.

We’ve also added a ALScanViewPluginFactory helper class, with a method to conveniently initialize either a composite or a non-composite scan view plugin based on the supplied JSON configuration object:

  • Swift

  • Objective-C

let scanViewPlugin: ALScanViewPluginBase = ALScanViewPluginFactory.withJSONDictionary(self.getJSONConfigDict())
if scanViewPlugin is ALScanViewPlugin {
    // treat scanViewPlugin as an ALScanViewPlugin
} else if scanViewPlugin is ALViewPluginComposite {
    // treat scanViewPlugin as an ALViewPluginComposite
}
NSObject<ALScanViewPluginBase> *scanViewPlugin = [ALScanViewPluginFactory withJSONDictionary:[self getJSONConfigDict]];
if ([scanViewPlugin isKindOfClass:[ALScanViewPlugin class]]) {
  // treat scanViewPlugin as an ALScanViewPlugin
} else if ([scanViewPlugin isKindOfClass:[ALViewPluginComposite class]]) {
  // treat scanViewPlugin as an ALViewPluginComposite
}

ALPluginComposite comes with a new delegate type called ALPluginCompositeDelegate, which includes the method -viewPluginComposite:allResultsReceived: that is called when results from all children plugins have been successfully scanned. You may still subscribe to child scan plugin events individually (ie setting their delegates) by accessing them through the children property:

  • Swift

  • Objective-C

// Here we think `viewPlugin` may be a composite based on the JSON config provided
let viewPlugin: ALScanViewPluginBase = ALScanViewPluginFactory.withJSONDictionary(JSONConfigDict)

// subscribe to children plugin callback events
for child in viewPlugin.children {
    if child is ALScanViewPlugin {
        (child as! ALScanViewPlugin).scanPlugin.delegate = self
    }
}
// Here we think `viewPlugin` may be a composite based on the JSON config provided
NSObject<ALScanViewPluginBase> *viewPlugin = [ALScanViewPluginFactory withJSONDictionary:JSONConfigDict];

// subscribe to children plugin callback events
for (NSObject<ALScanViewPluginBase> *child in viewPlugin.children) {
    if ([child isKindOfClass:ALScanViewPlugin.class]) {
        ((ALScanViewPlugin *)child).scanPlugin.delegate = self;
    }
}

New JSON config file structure

There are changes as well with the way Anyline structures parses JSON config files starting with version 43. Now, any top-level config key name ends with Config (for instance, scanFeedBackConfig, flashConfig, etc).

A JSON configuration file for a use case
{
    "cameraConfig": {
        ...
    },
    "flashConfig": {
        ...
    },
    "viewPluginConfig": {
        "pluginConfig": {
            ...
        },
        "cutoutConfig": {
            ...
        },
        "scanFeedbackConfig": {
            ...
        }
    }
}
Keys in the config JSON are case-sensitive. Do not write, say, scanfeedbackConfig (lowercase 'F') where scanFeedbackConfig is required.

Here’s a direct comparison of the same JSON configuration in the old vs. new style:

Old JSON

New JSON

{
  "camera": { (1)
    "captureResolution": "1080p",
    "pictureResolution": "1080p"
  },
  "flash": { (2)
    "mode": "manual",
    "alignment": "top_right"
  },
  "viewPlugin": { (3)
    "plugin": { (4)
      "id": "METER_PLUGIN",
      "meterPlugin": {
        "scanMode": "AUTO_ANALOG_DIGITAL_METER"
      }
    },
    "cutoutConfig" : {
      "style" : "rect",
      "alignment" : "top",
      "strokeWidth" : 2,
      "strokeColor" : "FFFFFF",
      "cornerRadius" : 4,
      "outerColor" : "000000",
      "outerAlpha" : 0.5,
      "feedbackStrokeColor" : "0099FF",
      "offset": {
        "x": 0,
        "y": 120
      }
    },
    "scanFeedback": { (5)
      "style": "CONTOUR_RECT",
      "strokeColor": "0099FF",
      "strokeWidth": 2,
      "fillColor": "220099FF",
      "cornerRadius": 2,
      "redrawTimeout": 200,
      "animationDuration": 75,
      "blinkOnResult": true,
      "beepOnResult": true,
      "vibrateOnResult": true
    },
    "cancelOnResult": true (6)
  }
}
{
  "cameraConfig": { (7)
    "captureResolution": "1080p",
    "pictureResolution": "1080p"
  },
  "flashConfig": { (8)
    "mode": "manual",
    "alignment": "top_right"
  },
  "viewPluginConfig": { (9)
    "pluginConfig": {
      "id": "METER_PLUGIN",
      "meterConfig": {
        "scanMode": "auto_analog_digital_meter"
      },
      "cancelOnResult": true (10)
    },
    "cutoutConfig" : {
      "style" : "rect",
      "alignment" : "top",
      "strokeWidth" : 2,
      "strokeColor" : "FFFFFF",
      "cornerRadius" : 4,
      "outerColor" : "000000",
      "outerAlpha" : 0.5,
      "feedbackStrokeColor" : "0099FF",
      "offset": {
        "x": 0,
        "y": 120
      }
    },
    "scanFeedbackConfig": { (11)
      "style": "CONTOUR_RECT",
      "strokeColor": "0099FF",
      "strokeWidth": 2,
      "fillColor": "220099FF",
      "cornerRadius": 2,
      "redrawTimeout": 200,
      "animationDuration": 75,
      "blinkOnResult": true,
      "beepOnResult": true,
      "vibrateOnResult": true
    }
  }
}
1 camera is now called cameraConfig (see 7.)
2 flash is now called flashConfig (see 8.)
3 viewPlugin is now called viewPluginConfig (see 9.)
4 plugin is now called pluginConfig and no longer contains a specific 'plugin', but instead, an XXXConfig object specific to the scanning use case (in this case a meterConfig)
5 scanFeedback is now called scanFeedbackConfig (see 11.)
6 cancelOnResult is not part of the viewPlugin anymore, instead it is part of the pluginConfig (see 10.)

Please refer to Plugin Configuration for more details.

There is a slight change in cutoutConfig on how the cropPadding property works. cropPadding now only takes positive values, so for example if you use a value of 5 for the horizontal padding, it will now crop off 5 pixels from each side of the cutout.

New delegate methods

We’ve also made some changes to the Objective-C delegates to ALScanPlugin, ALScanViewPlugin, and ALScanView, standardizing their interface and eliminating the need to implement delegate protocols for different use cases.

Delegates such as ALOCRScanPluginDelegate, ALIDPluginDelegate, and so on, no longer exist in the new version and have now been replaced by a single protocol: ALScanPluginDelegate.

Old delegates
- (void)anylineOCRScanPlugin:(ALOCRScanPlugin *)anylineOCRScanPlugin
               didFindResult:(ALOCRResult *)result {
  // ...
}

- (void)anylineLicensePlateScanPlugin:(ALLicensePlateScanPlugin *)anylineLicensePlateScanPlugin
                        didFindResult:(ALLicensePlateResult *)result {
  // ...
}
  • Swift

  • Objective-C

func scanPlugin(_ scanPlugin: ALScanPlugin, resultReceived scanResult: ALScanResult) {
    // (access license plate results or OCR results in the same method)
}
- (void)scanPlugin:(ALScanPlugin *)scanPlugin
    resultReceived:(ALScanResult *)scanResult {
  // (access license plate results or OCR results in the same method)
}

To round it up, here are the other methods from ALScanPluginDelegate that provide additional information about a scanning session (all methods are optional):

  • Swift

  • Objective-C

protocol ALScanPluginDelegate {

    func scanPlugin(_ scanPlugin: ALScanPlugin, resultReceived scanResult: ALScanResult)

    func scanPlugin(_ scanPlugin: ALScanPlugin, errorReceived event: ALEvent)

    func scanPlugin(_ scanPlugin: ALScanPlugin, scanRunSkipped event: ALEvent)

    func scanPlugin(_ scanPlugin: ALScanPlugin, scanInfoReceived event: ALEvent)

    func scanPlugin(_ scanPlugin: ALScanPlugin, visualFeedbackReceived event: ALEvent)
}
@protocol ALScanPluginDelegate <NSObject>
@optional

- (void)scanPlugin:(ALScanPlugin *)scanPlugin errorReceived:(ALEvent *)event;

- (void)scanPlugin:(ALScanPlugin *)scanPlugin visualFeedbackReceived:(ALEvent *)event;

- (void)scanPlugin:(ALScanPlugin *)scanPlugin scanInfoReceived:(ALEvent *)event;

- (void)scanPlugin:(ALScanPlugin *)scanPlugin scanRunSkipped:(ALEvent *)event;

@end

In each method, the ALEvent parameter can be examined by using the JSONObject (or JSONStr) property.

A new result type

ALScanResult is now structured in a way in that result types of use cases we support are now strongly-typed and are defined as part of the result object.

This means that for example if you are scanning OCRs, then an ocrResult will be a non-null property that you can expect to find the scan result within ALScanResult.

  • Swift

  • Objective-C

func scanPlugin(_ scanPlugin: ALScanPlugin, resultReceived scanResult: ALScanResult) {
    // the scanned OCR result
    let ocrString = scanResult.pluginResult.ocrResult?.text
}
- (void)scanPlugin:(ALScanPlugin *)scanPlugin resultReceived:(ALScanResult *)scanResult {
    // the scanned OCR result
    NSString *ocrString = scanResult.pluginResult.ocrResult.text;
}

Check the API Documentation section for more result types.

👉 Have a look at the Scanning Capabilities section for more specific implementation details. 👈


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