Migrating to Anyline 53

Anyline 53 contains a number of important breaking changes. This page details the implications of these changes, and provides guidance on how to adapt your integration code.

For more detailed information about the classes, please refer to API Documentation.

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

Additional examples can be found on the latest release version of the Anyline Developer Examples on GitHub.

Get help

If there is anything you are missing, you require support, or want to provide us with feedback, please reach out to us via https://support.anyline.com, where you either may find helpful information in our Knowledge Base or you can open a Support Ticket for more specific inquiries.

In order to better assist you, please include any code of your Anyline integration and any ScanViewConfig you are using.

An overview of the changes in Anyline 53

If you are upgrading from a version earlier than 43, be sure to also read Migrating from pre-43.x.x!

1. Configs now have a well-defined structure

Since version 43, an Anyline config (ScanViewConfig) for a given use case has the following top-level JSON structure:

A JSON configuration file for a use case
{
    "cameraConfig": {

    },
    "flashConfig": {

    },
    "viewPluginConfig": {
        "pluginConfig": {

        },
        "cutoutConfig": {

        },
        "scanFeedbackConfig": {

        }
    }
}

The most important change to come with version 53 is that the config JSONs passed to the Anyline SDK now also need to conform to the ScanViewConfig schema, which describes through a set of constraints the way a valid JSON configuration – that the SDK can understand and safely consume – should be constructed.

Config validation is now being run whenever an Anyline component is created using any method call accepting JSON as a parameter. Passing in a config that fails the schema check would cause an exception to be thrown as well as the method returning a null value.

JSON validation exceptions include a message that can help in determining the location and the immediate cause of the error. The following is an example of an exception message:

Message from a config validation exception
Failed to match against any enum values in
'ROOT → viewPluginConfig → pluginConfig → meterConfig → scanMode' (+4 others)

If this message does not provide you with sufficiently-actionable information to fix the JSON config, for a more detailed explanation of the error you can also check the 'failure reason' value found on the underlying NSError.userInfo dictionary:

if let error = error as? NSError {
  errorReason = error.userInfo[NSLocalizedFailureReasonErrorKey]
}

To help with this transition, you can now use a static method from the AnylineSDK class called validateJSONConfigString to test out your JSON configs (note: ScanViewConfigs only):

do {
  try AnylineSDK.validateJSONConfigString(JSONStr)
} catch {
  print("Invalid Anyline Config! Reason: \(error.localizedDescription)")
}

Please consult the schema to build fully working configs for your Anyline scanning use cases.When in doubt, a good starting point would be the aforementioned developer examples code on GitHub.

2. ScanViewPlugin renamed to ViewPlugin

To keep the type names consistent internally within the SDK, we have renamed some classes, protocols, as well as their members. The main ScanView component previously named ScanViewPlugin is now called simply ViewPlugin.

Here is a summary of the changes:

Old name

New name

Description

ALScanViewPluginBase

ALViewPluginBase

protocol; defines common interface for both ALScanViewPlugin and ALViewPluginComposite

ALScanView.scanViewPlugin

ALScanView.viewPlugin

main ScanView component (uses above protocol as its type)

ALScanViewPluginConfig

ALViewPluginConfig

object used to initialize an ALScanViewPlugin

setScanViewPlugin()

setViewPlugin()

ALScanView method to update view plugins

The concrete class ALScanViewPlugin retains its name for this release.

3. Config types now share a common interface

The classes ALScanViewConfig, ALViewPluginConfig, ALViewPluginCompositeConfig, ALCameraConfig, ALFlashConfig, ALScanFeedbackConfig, ALCutoutConfig, and ALPluginConfig have been updated to adopt the ALJSONConfig protocol, which is defined as follows:

protocol ALJSONConfig {

  /* creates an object of that type with default values populated */
  static func `default`() -> Self

  /* initializes an object using a JSON string valid for that type */
  init(jsonString JSONString: String) throws

  /* initializes an object using a JSON dictionary valid for that type */
  init(jsonDictionary JSONDictionary: [AnyHashable : Any]) throws

  /* creates an object using a JSON string valid for that type */
  static func withJSONString(_ JSONString: String) throws -> Self

  /* initializes an object using a JSON dictionary valid for that type */
  static func withJSONDictionary(_ JSONDictionary: [AnyHashable : Any]) throws -> Self
}

This makes it possible to dynamically construct config objects using a variety of methods, via JSON strings or dictionaries, or with concrete config implementations. An example:

Constructing a ScanView using a custom ScanViewConfig
// a config JSON string (escaped) for meter scanning
let scanViewConfigJSONStr = """
  {\"viewPluginConfig\":{\"pluginConfig\":{\"id\":\"meter_auto\",\"meterConfig\":{\"scanMode\":\"auto_analog_digital_meter\"},\"cancelOnResult\":true},\"cutoutConfig\":{\"maxWidthPercent\":\"80%\",\"ratioFromSize\":{\"width\":3,\"height\":1}},\"scanFeedbackConfig\":{}}}
"""

// create the initial scan view config
let scanViewConfig = try ALScanViewConfig.withJSONString(scanViewConfigJSONStr)

// add a camera config to the scan view config using a separate string
scanViewConfig.cameraConfig = try .withJSONString("{\"captureResolution\":\"1080p\",\"pictureResolution\":\"1080p\"}")

// add a flash config with a slight modification from the default
let flashConfig: ALFlashConfig = .default()
flashConfig.alignment = ALFlashConfigAlignment.bottomRight()
scanViewConfig.flashConfig = flashConfig

// create the scan view
let scanView = try ALScanView(config: scanViewConfig)

In addition, the above config objects now also adopt the ALJSONStringRepresentable protocol, making it straightforward to create JSON string representations from said config objects:

protocol ALJSONStringRepresentable {
    // throwing versions
    func toJSONStringPretty(_ isPretty: Bool) throws -> String
    func toJSONString() throws -> String

    // non-throwing versions
    func asJSONStringPretty(_ isPretty: Bool) -> String
    func asJSONString() -> String
}

You can then print out the JSON string representation of said config objects like so:

// display the JSON string for the configs
let prettyFlashConfigString = try flashConfig.toJSONStringPretty(true)
print("the flash config: \(prettyFlashConfigString)")

It is also important to note that all methods to create or initialize a config are now capable of throwing errors, and would require you to handle them appropriately (through a try-catch pattern):

do {
  self.scanView = try ALScanView(frame: frame, config: config)
} catch {
  print("unable to create scan view. Reason: \(error.localizedDescription)")
}

Lastly, initializers that previously accept sub-configs have been removed in this release. For instance, this method to initialize an ALScanViewPluginConfig is no longer available:

ALScanViewPluginConfig(pluginConfig: ALPluginConfig,
                       cutoutConfig: ALCutoutConfig,
                 scanFeedbackConfig: ALScanFeedbackConfig)

Instead, you should use the methods provided by ALJSONConfig, as discussed above.

4. ScanViews can now be initialized with a ScanViewConfig

Prior to version 53, ScanViews are typically initialized from existing ScanViewPlugins (or with an ALScanViewPluginFactory).

With the latest release, it will now be possible to create a ScanView using a ScanViewConfig (which can be constructed entirely via JSON):

Constructing a ScanView with a ScanViewConfig
let scanViewConfig = try ALScanViewConfig.withJSONString(ConfigJSONs.meterScanViewConfig)

// create a ScanView
scanView = try ALScanView(frame: frame, scanViewConfig: scanViewConfig)
view.addSubview(self.scanView)

// assign self as ALScanPluginDelegate
if let scanViewPlugin = scanView.viewPlugin as? ALScanViewPlugin {
    scanViewPlugin.scanPlugin.delegate = self
}

self.scanView.startCamera()
try self.scanView.startScanning()

Another possibility is through the help of new class ALScanViewFactory:

Constructing a ScanView using ALScanViewFactory
scanView = ALScanViewFactory.withJSONString(ConfigJSONs.meterScanViewConfig,
                                            delegate: self)

Pass the same config string as that used to create the ALScanViewConfig object in the previous example.

What the changes mean for your Anyline integration code

1. Start by renaming instances of 'ScanViewPlugin' to 'ViewPlugin'

The first build of your existing code after upgrading to Anyline version 53 will likely result in a substantial number of errors.

You will first need to attempt to fix those errors by renaming all (problematic) instances of ScanViewPlugin to ViewPlugin. Please check this section for an overview of those changes.

2. Take advantage of error handling

In previous versions, when calling methods to construct Anyline components or config objects, chances are that you do not take any possible errors into account. These methods have been changed to make error handling fully explicit in the new version.

With the new schema validation in place, catching and processing the errors have become more important as it will help you quickly determine and fix any issues with your configs.

Fix these build issues by adding try to methods and handle the error as you find appropriate.

do {
  let scanViewConfig = try ALScanViewConfig(withJSONString: configString)
  // ...
} catch {
  print("Error constructing ALScanViewConfig: \(error.localizedDescription)")
}

3. Use the right JSON for the type of object being constructed

The most frequent cause of suddenly getting validation failures with your configs is mistaking a ScanViewConfig object for a ViewPluginConfig object, i.e. passing a JSON meant for a ALViewPluginConfig constructor to an ALScanViewConfig constructor.

In such cases, the error message usually come in this form:

Missing required property 'viewPluginConfig' in 'ROOT' (+4 others)

When constructing an ALJSONConfig object (or component based from said object) with a JSON string or dictionary, it is important to ensure that you are passing in the correct JSON object appropriate for that level of the config hierarchy.

As an example, consider the classes ALScanViewConfig, and ALViewPluginConfig, which is a member of ALScanViewConfig named viewPluginConfig. The following JSON will validate for an initializer of ALScanViewConfig, but not for ALViewPluginConfig:

A valid JSON for ALScanViewConfig
{
    "viewPluginConfig": {
        "pluginConfig": {
            "id": "meter_auto",
            "meterConfig": {
                "scanMode": "analog_digital_meter"
            },
            "cancelOnResult": true
        },
        "cutoutConfig": {
            "maxWidthPercent": "80%",
            "ratioFromSize": {
                "width": 3,
                "height": 1
            }
        },
        "scanFeedbackConfig": {}
    }
}

To construct an ALViewPluginConfig, the following JSON would be appropriate:

A valid JSON for ALViewPluginConfig
{
    "pluginConfig": {
        "id": "meter_auto",
        "meterConfig": {
            "scanMode": "analog_digital_meter"
        },
        "cancelOnResult": true
    },
    "cutoutConfig": {
        "maxWidthPercent": "80%",
        "ratioFromSize": {
            "width": 3,
            "height": 1
        }
    },
    "scanFeedbackConfig": {}
}

When in doubt, check the ScanViewConfig schema.

4. Use only ALScanView when you can

In previous versions you would first create a scan view plugin, and then followed by the scan view, which is done by passing the scan view plugin to the ALScanView initializer, and then assigning any delegates required:

let viewPlugin = try ALScanViewPlugin(JSONDictionary: JSONConfigDict)
scanViewPlugin.delegate = self

let scanView = try ALScanView(frame: frame, scanViewPlugin: viewPlugin)
scanView.startCamera()

// ...
try? viewPlugin.startScanning()

This approach is still available, though with some minor changes we have noted in the previous section.

However, as of version 53, we are now recommending you build the ScanView through the ALScanViewFactory with an appropriate config JSON string (or dictionary):

// the delegate is of type ALScanViewPluginDelegate
scanView = try ALScanViewFactory.withJSONString(JSONStr, delegate: self)

Another possibility is to create the ScanView through the ALScanView initializer that accepts an ALScanViewConfig parameter.

In addition, methods were added to ALScanView to provide a way to directly start and stop the scanning process, without having to do it through its associated view plugin object:

try scanView.startScanning()
try scanView.stopScanning()

However, you can continue to call startScanning on an ALScanViewPlugin object, if you prefer.

Migrating from pre-43.x.x

If you are upgrading from an older version of Anyline (e.g. 42 or earlier), please note some important changes in the config structure.

Refer to the following table for a brief comparison:

Test your JSON configs using the AnylineSDK static method validateJSONConfigString(JSONStr) to be sure you are passing a config that works with the current release.

Old JSON (pre-43)

New JSON (43 and later)

{
  "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.


Much will have changed with the programmatic interface of Anyline components for the current release. The important thing to maintain when performing a code migration is to have a ScanView constructed using a valid JSON config object, through one of the two methods provided in this section, for instance.

For additional information on the changes moving from pre-43 to 43.x.x and higher, read the Migration Guide for recent releases, such as: Migrating to Anyline 43.0.0 (note: some details may be outdated).