The SmartScannerSDK
library requires iOS 14.0 or later and Swift 5.0 or later.
Note Apple platforms other than iOS are not currently supported.
The Swift Package Manager is a tool for automating the distribution of Swift code and is integrated into the swift
compiler.
Once you have your Swift package set up, adding the SmartScannerSDK
as a dependency is as easy as adding it to the dependencies
value of your Package.swift
.
dependencies: [
.package(
url: "https://github.com/securingsam/SmartScanner-iOS-Spec",
.upToNextMajor(from: "1.0.0")
)
]
If you prefer not to use dependency managers, you can integrate the SmartScannerSDSK
into your project manually.
Download the XCFramework file and copy it to your Xcode project.
For a more detailed list of all of the available SDK API methods and types, please refer to the API reference.
A full example ViewModel
class file can be found here.
Importing the library in your Swift code is pretty straightforward
import SmartScannerSDK
It is important to know that this variation will work for compiling and running on physical devices only. In case an iPhone Simulator support is required, please use the following import code snippet:
#if targetEnvironment(simulator)
import SmartScannerSDKSimulator
#else
import SmartScannerSDK
#endif
This will ensure to import the simulator variation of the library when the Xcode target device is an iOS simulator.
Note The simulator variation will compile and run, but the scanner will not work on the iPhone simulator in any way.
The SmartScannerSDK
library requires a SAM vendor authentication token in order to verify your identity.
Setting up occurs anywhere, as long as it happens before using any of the SmartScannerSDK
's features. Interacting with any other API methods beforehand will result in 401
errors.
SmartScanner.getInstance().setup(token: "yoursamvendortoken")
The Scan
instance is used in order to communicate with the SmartScannerSDK
.
let scanner: Scan = SmartScanner.getInstance().getScanInstance()
In order to start scanning, call the .scan(params:)
method from the Scan
instance.
This method returns a Combined AnyPublisher
and provides a ScanResult
object which contains the data that was collected by the scanner.
let config = ScanConfig()
scanner.scan(params: config)
.receive(on: RunLoop.main)
.sink { completion in
switch completion {
case .finished:
print("Completed!")
case .failure(let error):
// Scan process failed. Print the error
print(error.description)
}
} receiveValue: { result in
// On success, print the list of the detected devices.
guard
let result,
let devices = result.devices
else {
print("No devices found")
return
}
print("Devices: \(devices)")
}
.store(in: &cancellables)
The Scan
instances exposes an observer instance called .observeProgress()
that allows you to observe the progress of the scan (progress values ranging from 0 - 100).
The .observeProgress()
method returns a new Combine AnyPublisher
with a value of type Int
that progress values can be read from.
scanner
.observeProgress()
.receive(on: RunLoop.main)
.sink { value in
print("Progress: \(value)")
}
.store(in: &cancellables)
As it is better to get devices when they are detected, The Scan
instance also exposes an .observeDevices()
method that returns an array of Device
items.
This allows you to retrieve a list of detected devices as they are detected.
scanner
.observeDevices()
.receive(on: RunLoop.main)
.sink(receiveCompletion: { error in
print("The scanner encountered an error: \(error)")
}, receiveValue: { devices in
print("Detected devices: \(devices)")
})
.store(in: &cancellables)
In order to stop the currently active scan, you can use the .killScan()
method built into the Scan
instance.
scanner.killScan()
The scan
method accepts a ScanConfig
object.
The ScanConfig
object allows the SDK client to control the scanner's configurations. For example, the following code snippet starts a scan that is using only PING
to detect devices in the network and sets the scan to last only 2 minutes.
All scanner types can be found within the SamScannerType
enum.
Note The default configurations are considered to be optimized for the best scan results.
let config = ScanConfig(
allowedScannerTypes: [.PING],
scanTimeInSeconds: 2000
)
scanner.scan(params: config)
.receive(on: RunLoop.main)
.sink { ... }
.store(in: &cancellables)
In case the authentication token provided in the initialization and authentication phase is expired during the scan process, the scan process will automatically stop and return a SamSDKError
instance with code of .UNAUTHORIZED
(401).
In that scenario a new/refreshed token must be provided to the SDK by executing the .setup(token:)
function again.
func startScan() {
let config = ScanConfig()
scanner.scan(params: config)
.receive(on: RunLoop.main)
.sink { [weak self] completion in
switch completion {
case .finished:
print("Finished!")
case .failure(let error):
/// Treat an `.UNAUTHORIZED` type error
if error == .UNAUTHORIZED {
self?.handle401UnauthorizedErrorAndRescan()
return
}
print(error.description)
}
} receiveValue: { result in
// On success, print the list of deteceted devices
guard
let result,
let devices = result.devices
else {
print("No devices found")
return
}
print("Devices: \(devices)")
}
.store(in: &cancellables)
}
private func handle401UnauthorizedErrorAndRescan() {
setupScanner(token: refreshToken())
startScan()
}
private func refreshToken() -> String {
// Refresh the token and return a new valid one
}