Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6168241
feat(GiniBankAPILibrary): add unsupportedQRCodeWarningEnabled flag to…
qnaveed87 Jun 17, 2026
861f895
feat(GiniCaptureSDK): add unsupportedQRCodeWarningEnabled to UserDefa…
qnaveed87 Jun 17, 2026
fdda0a3
feat(GiniBankSDK): passs unsupportedQRCodeWarningEnabled from backend…
qnaveed87 Jun 17, 2026
e34dc25
feat(GiniCaptureSDK): add pauseQRDetection and resumeQRDetection to C…
qnaveed87 Jun 17, 2026
6c33e25
feat(GiniCaptureSDK): implement pauseQRDetection and resumeQRDetectio…
qnaveed87 Jun 17, 2026
b4d8e16
feat(GiniCaptureSDK): add unsupported QR code alert and user action h…
qnaveed87 Jun 17, 2026
bceb1de
feat(GiniCaptureSDK): gate unsupported QR code alert behind backend f…
qnaveed87 Jun 17, 2026
2970d07
feat(GiniCaptureSDK): Update access modifier fro camera protocol
qnaveed87 Jun 18, 2026
f8f360a
feat(BankSDK): Code indentation
qnaveed87 Jun 18, 2026
aa4f9f6
fix(GiniCaptureSDK): snapshot unsupported QR code flag once per session
qnaveed87 Jun 19, 2026
754b5b5
fix(GiniBankSDK): Added localized strings
qnaveed87 Jun 19, 2026
4fdb092
feat(GiniBankSDK): Update strings via localized string
qnaveed87 Jun 19, 2026
1fe374b
feat(GiniBankAPILibrary): add unsupportedQRCodeWarningEnabled to clie…
qnaveed87 Jun 19, 2026
9295a09
test(GiniBankAPILibrary): add unsupportedQRCodeWarningEnabled coverag…
qnaveed87 Jun 19, 2026
25249e1
fix(GiniBankSDK): Fix CameraMock conformance to CameraProtocol
qnaveed87 Jun 22, 2026
8f2d97f
fix(GiniCaptureSDK): Fix CameraMock conformance to CameraProtocol
qnaveed87 Jun 22, 2026
bc2f62c
refactor(GiniBankAPILibrary): remove default value from unsupportedQR…
qnaveed87 Jun 22, 2026
d39e992
refactor(GiniCaptureSDK): restrict camera property to private(set) in…
qnaveed87 Jun 22, 2026
bebc167
test(GiniCaptureSDK): add coverage for pauseQRDetection and resumeQRD…
qnaveed87 Jun 22, 2026
156de27
test(GiniBankSDK): pass unsupportedQRCodeWarningEnabled to ClientConf…
qnaveed87 Jun 22, 2026
46a171e
test(GiniCaptureSDK): remove flaky QR detection tests that rely on se…
qnaveed87 Jun 22, 2026
cf9f57e
test(GiniCaptureSDK): add private(set) qrMetadataOutput with test-onl…
qnaveed87 Jun 22, 2026
17f68bc
test(GiniCaptureSDK): cover qrMetadataOutput assignment via setupQRSc…
qnaveed87 Jun 23, 2026
5f41f49
refactor(GiniCaptureSDK): remove force unwrap of hideQRCodeTask in sh…
qnaveed87 Jun 24, 2026
fa65534
fix(GiniCaptureSDK): guard against duplicate unsupported QR alert sid…
qnaveed87 Jun 24, 2026
996b596
test(GiniCaptureSDK): cover [.qr] assignment in resumeQRDetection via…
qnaveed87 Jun 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public struct ClientConfiguration: Codable {
public let savePhotosLocallyEnabled: Bool
public let alreadyPaidHintEnabled: Bool
public let paymentDueHintEnabled: Bool
public let unsupportedQRCodeWarningEnabled: Bool

/**
Creates a new `ClientConfiguration` instance.
Expand All @@ -43,6 +44,7 @@ public struct ClientConfiguration: Codable {
- savePhotosLocallyEnabled: A flag indicating whether saving photos locally is enabled.
- alreadyPaidHintEnabled: A flag indicating whether hints for already paid invoices are enabled.
- paymentDueHintEnabled: A flag indicating whether hints for upcoming payment due date is enabled.
- unsupportedQRCodeWarningEnabled: A flag indicating whether the unsupported QR code warning alert is enabled.
*/
public init(clientID: String,
userJourneyAnalyticsEnabled: Bool,
Expand All @@ -54,7 +56,8 @@ public struct ClientConfiguration: Codable {
eInvoiceEnabled: Bool,
savePhotosLocallyEnabled: Bool,
alreadyPaidHintEnabled: Bool,
paymentDueHintEnabled: Bool) {
paymentDueHintEnabled: Bool,
unsupportedQRCodeWarningEnabled: Bool) {
self.clientID = clientID
self.userJourneyAnalyticsEnabled = userJourneyAnalyticsEnabled
self.skontoEnabled = skontoEnabled
Expand All @@ -66,5 +69,6 @@ public struct ClientConfiguration: Codable {
self.savePhotosLocallyEnabled = savePhotosLocallyEnabled
self.alreadyPaidHintEnabled = alreadyPaidHintEnabled
self.paymentDueHintEnabled = paymentDueHintEnabled
self.unsupportedQRCodeWarningEnabled = unsupportedQRCodeWarningEnabled
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ struct ClientConfigurationTests {
eInvoiceEnabled: true,
savePhotosLocallyEnabled: true,
alreadyPaidHintEnabled: true,
paymentDueHintEnabled: true)
paymentDueHintEnabled: true,
unsupportedQRCodeWarningEnabled: true)

#expect(config.clientID == testClientID, "Expected clientID to be \(testClientID)")
#expect(config.userJourneyAnalyticsEnabled, "Expected userJourneyAnalyticsEnabled to be true")
Expand All @@ -38,6 +39,7 @@ struct ClientConfigurationTests {
#expect(config.eInvoiceEnabled, "Expected eInvoiceEnabled to be true")
#expect(config.alreadyPaidHintEnabled, "Expected alreadyPaidHintEnabled to be true")
#expect(config.savePhotosLocallyEnabled, "Expected savePhotosLocallyEnabled to be true")
#expect(config.unsupportedQRCodeWarningEnabled, "Expected unsupportedQRCodeWarningEnabled to be true")
}

@Test("Initialization with all flags disabled")
Expand All @@ -52,9 +54,8 @@ struct ClientConfigurationTests {
eInvoiceEnabled: false,
savePhotosLocallyEnabled: false,
alreadyPaidHintEnabled: false,
paymentDueHintEnabled: false)


paymentDueHintEnabled: false,
unsupportedQRCodeWarningEnabled: false)

#expect(config.clientID == testClientID, "Expected clientID to be \(testClientID)")
#expect(!config.userJourneyAnalyticsEnabled, "Expected userJourneyAnalyticsEnabled to be false")
Expand All @@ -66,6 +67,7 @@ struct ClientConfigurationTests {
#expect(!config.eInvoiceEnabled, "Expected eInvoiceEnabled to be false")
#expect(!config.alreadyPaidHintEnabled, "Expected alreadyPaidHintEnabled to be false")
#expect(!config.savePhotosLocallyEnabled, "Expected savePhotosLocallyEnabled to be false")
#expect(!config.unsupportedQRCodeWarningEnabled, "Expected unsupportedQRCodeWarningEnabled to be false")
}

// MARK: - JSON Decoding Tests
Expand All @@ -87,6 +89,7 @@ struct ClientConfigurationTests {
#expect(!config.eInvoiceEnabled, "Expected eInvoiceEnabled to be false from JSON")
#expect(!config.alreadyPaidHintEnabled, "Expected alreadyPaidHintEnabled to be false from JSON")
#expect(!config.savePhotosLocallyEnabled, "Expected savePhotosLocallyEnabled to be false from JSON")
#expect(!config.unsupportedQRCodeWarningEnabled, "Expected unsupportedQRCodeWarningEnabled to be false from JSON")
}

@Test("Decoding fails when missing required clientID field")
Expand All @@ -113,7 +116,8 @@ struct ClientConfigurationTests {
eInvoiceEnabled: true,
savePhotosLocallyEnabled: true,
alreadyPaidHintEnabled: true,
paymentDueHintEnabled: true)
paymentDueHintEnabled: true,
unsupportedQRCodeWarningEnabled: true)

let encoder = JSONEncoder()

Expand All @@ -140,6 +144,8 @@ struct ClientConfigurationTests {
"Expected alreadyPaidHintEnabled to be preserved")
#expect(decodedConfig.savePhotosLocallyEnabled == config.savePhotosLocallyEnabled,
"Expected savePhotosLocallyEnabled to be preserved")
#expect(decodedConfig.unsupportedQRCodeWarningEnabled == config.unsupportedQRCodeWarningEnabled,
"Expected unsupportedQRCodeWarningEnabled to be preserved")
}

// MARK: - Property Combinations Tests
Expand All @@ -156,7 +162,8 @@ struct ClientConfigurationTests {
eInvoiceEnabled: false,
savePhotosLocallyEnabled: false,
alreadyPaidHintEnabled: true,
paymentDueHintEnabled: true)
paymentDueHintEnabled: true,
unsupportedQRCodeWarningEnabled: true)

#expect(config.userJourneyAnalyticsEnabled, "Expected userJourneyAnalyticsEnabled to be true")
#expect(!config.skontoEnabled, "Expected skontoEnabled to be false")
Expand All @@ -167,5 +174,6 @@ struct ClientConfigurationTests {
#expect(!config.eInvoiceEnabled, "Expected eInvoiceEnabled to be false")
#expect(config.alreadyPaidHintEnabled, "Expected alreadyPaidHintEnabled to be true")
#expect(!config.savePhotosLocallyEnabled, "Expected savePhotosLocallyEnabled to be false")
#expect(config.unsupportedQRCodeWarningEnabled, "Expected unsupportedQRCodeWarningEnabled to be true")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@
"eInvoiceEnabled": false,
"savePhotosLocallyEnabled": false,
"alreadyPaidHintEnabled": false,
"paymentDueHintEnabled": false
"paymentDueHintEnabled": false,
"unsupportedQRCodeWarningEnabled": false
}
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,8 @@ open class GiniBankNetworkingScreenApiCoordinator: GiniScreenAPICoordinator, Gin
GiniCaptureUserDefaultsStorage.qrCodeEducationEnabled = configuration.qrCodeEducationEnabled
GiniCaptureUserDefaultsStorage.eInvoiceEnabled = configuration.eInvoiceEnabled
GiniCaptureUserDefaultsStorage.savePhotosLocallyEnabled = configuration.savePhotosLocallyEnabled
GiniCaptureUserDefaultsStorage.unsupportedQRCodeWarningEnabled =
configuration.unsupportedQRCodeWarningEnabled
self.initializeAnalytics(with: configuration)
}
case .failure(let error):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ final class CameraMock: CameraProtocol {
// This method will remain empty; no implementation is needed.
}

func setup(completion: ((CameraError?) -> Void)) {
func setup(completion: @escaping ((CameraError?) -> Void)) {
switch state {
case .authorized:
completion(nil)
Expand All @@ -76,4 +76,12 @@ final class CameraMock: CameraProtocol {
func stop() {
// This method will remain empty; no implementation is needed.
}

func pauseQRDetection() {
// This method will remain empty; no implementation is needed.
}

func resumeQRDetection() {
// This method will remain empty; no implementation is needed.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ extension ClientConfiguration {
eInvoiceEnabled: false,
savePhotosLocallyEnabled: false,
alreadyPaidHintEnabled: alreadyPaidHintEnabled,
paymentDueHintEnabled: paymentDueHintEnabled)
paymentDueHintEnabled: paymentDueHintEnabled,
unsupportedQRCodeWarningEnabled: false)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ protocol CameraProtocol: AnyObject {
func setupQRScanningOutput(completion: @escaping ((CameraError?) -> Void))
func start()
func stop()
func pauseQRDetection()
func resumeQRDetection()
Comment thread
qnaveed87 marked this conversation as resolved.

// IBAN detection
func startOCR()
Expand Down Expand Up @@ -74,6 +76,9 @@ final class Camera: NSObject, CameraProtocol {

fileprivate let application: UIApplication

// QR detection
private(set) var qrMetadataOutput: AVCaptureMetadataOutput?

// IBAN detection
private var request: VNImageBasedRequest?
private var textOrientation = CGImagePropertyOrientation.up
Expand Down Expand Up @@ -272,8 +277,30 @@ final class Camera: NSObject, CameraProtocol {
if qrOutput.availableMetadataObjectTypes.contains(.qr) {
qrOutput.metadataObjectTypes = [.qr]
}
qrMetadataOutput = qrOutput
session.commitConfiguration()
}

func pauseQRDetection() {
sessionQueue.async { [weak self] in
self?.qrMetadataOutput?.metadataObjectTypes = []
}
}

func resumeQRDetection() {
sessionQueue.async { [weak self] in
guard let self = self,
let output = self.qrMetadataOutput,
output.availableMetadataObjectTypes.contains(.qr) else { return }
output.metadataObjectTypes = [.qr]
}
}

// Intended for unit tests only — injects a metadata output without going
// through full session setup, which is unstable on CI simulators.
func setQRMetadataOutputForTesting(_ output: AVCaptureMetadataOutput?) {
qrMetadataOutput = output
}
}

// MARK: - Fileprivate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ final class CameraViewController: UIViewController {
private var isPresentedOnScreen = false

private var isValidIBANDetected: Bool = false
// Snapshot of the backend flag taken on the first invalid QR scan.
// Stays fixed for the session so all repeated scans show the same feedback type.
private var sessionUnsupportedQRCodeWarningEnabled: Bool?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion]: Looks like one boolean should be enough like
private var sessionUnsupportedQRCodeWarningEnabled = false

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Second bool is for manage the session to have 1 type of alert within same session.
Here is the AC fro the ticket

`Given the user already triggered an invalid QR-code warning earlier in the same SDK session, when the user scans a second (or subsequent) invalid QR-code, the SDK shows the same warning type as the first time.

This holds even if the configuration endpoint response resolves in between and changes the flag value (e.g. unsupportedQRCodeWarningEnabled becomes true).

Example: On first install, no ClientConfiguration is stored, so the default applies. The user scans an invalid QR-code before the endpoint responds and sees the old yellow warning. While they go back, the endpoint resolves with unsupportedQRCodeWarningEnabled = true. The user scans a second invalid QR-code and still sees the old yellow warning, not the new popup. The new popup only appears from the next SDK session onward.`

private var isWarningFlagSnapshotted = false
// Analytics
private var invalidQRCodeOverlayFirstAppearance: Bool = true
private var ibanOverlayFirstAppearance: Bool = true
Expand Down Expand Up @@ -589,24 +593,39 @@ final class CameraViewController: UIViewController {
resetQRCodeTask?.cancel()
detectedQRCodeDocument = document

hideQRCodeTask = DispatchWorkItem(block: {
self.resetQRCodeScanning(isValid: isValid)

if let QRDocument = self.detectedQRCodeDocument {
if isValid {
if isValid {
let task = DispatchWorkItem(block: {
self.resetQRCodeScanning(isValid: true)
if let QRDocument = self.detectedQRCodeDocument {
self.didPick(QRDocument)
}
}
})

if isValid {
})
hideQRCodeTask = task
showValidQRCodeFeedback()
DispatchQueue.main.asyncAfter(deadline: .now() + Constants.hideQRCodeDelay, execute: task)
} else {
if !isValidIBANDetected {
showInvalidQRCodeFeedback()
// Snapshot the flag once so the feedback type stays consistent across
// repeated scans in the same session. A separate boolean guards the snapshot
// because assigning nil to a Bool? still leaves it nil, making a nil-check
// unreliable as a "has snapshotted" gate.
Comment thread
qnaveed87 marked this conversation as resolved.
if !isWarningFlagSnapshotted {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we keep 1 boolean

Suggested change
if !isWarningFlagSnapshotted {
if !sessionUnsupportedQRCodeWarningEnabled {
sessionUnsupportedQRCodeWarningEnabled =
GiniCaptureUserDefaultsStorage.unsupportedQRCodeWarningEnabled == true
}
if sessionUnsupportedQRCodeWarningEnabled {
showUnsupportedQRCodeAlert()
} else {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented as per the AC - 7

sessionUnsupportedQRCodeWarningEnabled = GiniCaptureUserDefaultsStorage.unsupportedQRCodeWarningEnabled
isWarningFlagSnapshotted = true
}

if sessionUnsupportedQRCodeWarningEnabled == true {
showUnsupportedQRCodeAlert()
} else {
showInvalidQRCodeFeedback()
let task = DispatchWorkItem(block: {
self.resetQRCodeScanning(isValid: false)
})
hideQRCodeTask = task
DispatchQueue.main.asyncAfter(deadline: .now() + Constants.hideQRCodeDelay, execute: task)
}
}
}
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5, execute: hideQRCodeTask!)
}

private func showValidQRCodeFeedback() {
Expand Down Expand Up @@ -651,6 +670,43 @@ final class CameraViewController: UIViewController {
qrCodeOverLay.configureQrCodeOverlay(withCorrectQrCode: false)
}

private func showUnsupportedQRCodeAlert() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Clarification]: pauseQRDetection() schedules its work asynchronously on sessionQueue, but the QR delegate fires per-frame and fans out via DispatchQueue.main.async for each frame. By the time the pause actually takes effect, several showUnsupportedQRCodeAlert() calls are already queued on the main thread-each one firing sendGiniAnalyticsEventForInvalidQRCode() and playVoiceOverMessage(). UIKit silently swallows the duplicate present() calls so there's no crash, but analytics and voice-over fire N times. Introducing `guard in suggestion below can be a fix.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not able to re produce issue with voice over but added guard so that it wont happen fro any edge case

// Skip duplicate side-effects when the alert is already on screen and more frames
// are still queued before pauseQRDetection takes effect on the session queue.
guard presentedViewController == nil else { return }

let generator = UINotificationFeedbackGenerator()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let generator = UINotificationFeedbackGenerator()
guard !isUnsupportedQRAlertPresented else { return }
isUnsupportedQRAlertPresented = true
let generator = UINotificationFeedbackGenerator()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented as per the AC - 7

generator.notificationOccurred(.warning)

cameraPreviewViewController.camera.pauseQRDetection()

sendGiniAnalyticsEventForInvalidQRCode()
playVoiceOverMessage(success: false)

let alert = UIAlertController(title: Strings.unsupportedQRAlertTitle,
message: nil,
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: Strings.scanAnotherQRCode, style: .default) { [weak self] _ in
self?.handleScanAnotherQRCode()
})
alert.addAction(UIAlertAction(title: Strings.takePhotoOfDocument, style: .default) { [weak self] _ in
self?.handleTakePhotoOfDocument()
})

present(alert, animated: true)
}

private func handleScanAnotherQRCode() {
cameraPreviewViewController.camera.resumeQRDetection()
detectedQRCodeDocument = nil
}

private func handleTakePhotoOfDocument() {
// QR detection stays paused — resuming here would immediately re-trigger the alert
cameraPreviewViewController.cameraFrameView.isHidden = false
detectedQRCodeDocument = nil
}

private func isAccessibilityLargeTextEnabled() -> Bool {
let contentSizeCategory = UIApplication.shared.preferredContentSizeCategory
return contentSizeCategory.isAccessibilityCategory
Expand Down Expand Up @@ -767,6 +823,7 @@ private extension CameraViewController {
static let switcherPadding: CGFloat = 8
static let phoneSwitcherSize: CGSize = CGSize(width: 124, height: 40)
static let tableSwitcherSize: CGSize = CGSize(width: 40, height: 124)
static let hideQRCodeDelay: TimeInterval = 1.5
}

private struct Strings {
Expand All @@ -778,6 +835,21 @@ private extension CameraViewController {
comment: "Info label")
static let cameraTitle = NSLocalizedStringPreferredFormat("ginicapture.navigationbar.camera.title",
comment: "Camera title")

static let unsupportedQRAlertTitleKey = "ginicapture.QRscanning.alert.title"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion]: Wrapping all keys in the constants looks like overhead... was is need for lint?
We used before static let unsupportedQRAlertTitle = NSLocalizedStringPreferredFormat( "ginicapture.QRscanning.alert.title", comment: "Unsupported QR code alert title")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. If we keep everything in a single line, SwiftLint raises a warning. We also follow the same pattern in PaymentDueHint

static let unsupportedQRAlertTitleComment = "Unsupported QR code alert title"
static let unsupportedQRAlertTitle = NSLocalizedStringPreferredFormat(unsupportedQRAlertTitleKey,
comment: unsupportedQRAlertTitleComment)

static let scanAnotherQRCodeKey = "ginicapture.QRscanning.alert.scanAnother"
static let scanAnotherQRCodeComment = "Scan another QR code button"
static let scanAnotherQRCode = NSLocalizedStringPreferredFormat(scanAnotherQRCodeKey,
comment: scanAnotherQRCodeComment)

static let takePhotoOfDocumentKey = "ginicapture.QRscanning.alert.takePhoto"
static let takePhotoOfDocumentComment = "Take photo of document button"
static let takePhotoOfDocument = NSLocalizedStringPreferredFormat(takePhotoOfDocumentKey,
comment: takePhotoOfDocumentComment)
}
}
// swiftlint:enable type_body_length
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ final class CameraPreviewViewController: UIViewController {
private var notAuthorizedView: UIView?
private let giniConfiguration: GiniConfiguration
private typealias FocusIndicator = UIImageView
private var camera: CameraProtocol
private(set) var camera: CameraProtocol
private var defaultImageView: UIImageView?
private var focusIndicatorImageView: UIImageView?

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ public struct GiniCaptureUserDefaultsStorage {
defaultValue: nil)
public static var savePhotosLocallyEnabled: Bool?

// Configuration flag for the unsupported QR code warning alert
@GiniUserDefault("ginicapture.defaults.clientConfigurations.unsupportedQRCodeWarningEnabled",
defaultValue: nil)
public static var unsupportedQRCodeWarningEnabled: Bool?

// User preference for the Save photos locally feature
@GiniUserDefault("ginicapture.defaults.userSettings.savePhotosSwitchOn",
defaultValue: nil)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,9 @@
"ginicapture.QRscanning.correct" = "QR-Code erkannt";
"ginicapture.QRscanning.incorrect.title" = "Unbekannter QR-Code";
"ginicapture.QRscanning.incorrect.description" = "Dieser Code enthält keine Zahlungsinformationen. Bitte anderen QR-Code oder Rechnung abfotografieren.";
"ginicapture.QRscanning.alert.title" = "Dieser QR-Code ist kein Zahlungs-QR-Code";
"ginicapture.QRscanning.alert.scanAnother" = "Anderen QR-Code scannen";
"ginicapture.QRscanning.alert.takePhoto" = "Dokument fotografieren";
"ginicapture.QRscanning.loading" = "Rechnung wird übermittelt";
"ginicapture.QRscanning.education.loading.captureTip" = "Fotografieren Sie direkt ein Zahlungsformular oder eine Rechnung - auch ohne QR-Code";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,9 @@
"ginicapture.QRscanning.correct" = "QR code detected";
"ginicapture.QRscanning.incorrect.title" = "Unknown QR code";
"ginicapture.QRscanning.incorrect.description" = "This code does not contain any payment details. Please use another QR code or take an image of your invoice.";
"ginicapture.QRscanning.alert.title" = "This is not a payment QR code";
"ginicapture.QRscanning.alert.scanAnother" = "Scan another QR code";
"ginicapture.QRscanning.alert.takePhoto" = "Take photo of document";
Comment thread
qnaveed87 marked this conversation as resolved.
"ginicapture.QRscanning.loading" = "Retrieving invoice";
"ginicapture.QRscanning.education.loading.captureTip" = "You can take photos of receipts or remittance slips – even without a QR code";

Expand Down
Loading
Loading