Skip to content

Commit

Permalink
Release version 3.11.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Team Mobile Schorsch committed Nov 26, 2024
1 parent fc825d5 commit 693b559
Show file tree
Hide file tree
Showing 29 changed files with 283 additions and 134 deletions.
2 changes: 1 addition & 1 deletion .jazzy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ xcodebuild_arguments:
- "-scheme"
- GiniCaptureSDK
- "-destination"
- platform=iOS Simulator,OS=17.2,name=iPhone 14
- platform=iOS Simulator,OS=17.5,name=iPhone 15
author: Gini GmbH
author_url: https://gini.net
module: GiniCaptureSDK
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ let package = Package(
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(name: "GiniBankAPILibrary", url: "https://github.com/gini/bank-api-library-ios.git", .exact("3.3.1"))
.package(name: "GiniBankAPILibrary", url: "https://github.com/gini/bank-api-library-ios.git", .exact("3.4.0"))
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
Expand Down
82 changes: 42 additions & 40 deletions Sources/GiniCaptureSDK/Core/Custom views/GiniBarButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,56 +126,58 @@ public final class GiniBarButton {
}

private func setupContent(basedOnType type: BarButtonType) {
var buttonTitle: String?
var icon: UIImage?

switch type {
case .cancel:
buttonTitle = NSLocalizedStringPreferredFormat("ginicapture.navigationbar.analysis.back",
comment: "Cancel")
icon = UIImageNamedPreferred(named: "barButton_cancel")
case .help:
buttonTitle = NSLocalizedStringPreferredFormat("ginicapture.navigationbar.camera.help",
comment: "Help")
icon = UIImageNamedPreferred(named: "barButton_help")
case .back(title: let title):
buttonTitle = title
let backString = NSLocalizedStringPreferredFormat("ginicapture.navigationbar.accessibility.back",
comment: "Back")
if title.trimmingCharacters(in: .whitespaces).isNotEmpty {
stackView.accessibilityValue = "\(title) \(backString)"
} else {
stackView.accessibilityValue = backString
}
icon = UIImageNamedPreferred(named: "barButton_back")
case .done:
buttonTitle = NSLocalizedStringPreferredFormat("ginicapture.imagepicker.openbutton",
comment: "Done")
icon = UIImageNamedPreferred(named: "barButton_done")
case .skip:
buttonTitle = NSLocalizedStringPreferredFormat("ginicapture.onboarding.skip",
comment: "Skip button")
}

let buttonTitleIsEmpty = buttonTitle == nil || buttonTitle!.isEmpty

// If there is no image nor any text for the button, the app will crash in debug mode.
if buttonTitleIsEmpty && icon == nil {
assertionFailure("You need to provide at least a valid string or an icon" +
" for the navigation bar button of type: \(type)")
let (buttonTitle, icon) = titleAndIcon(for: type)

// Ensure there’s at least a title or icon to avoid a crash
guard buttonTitle != nil || icon != nil else {
assertionFailure("""
You need to provide at least a valid string or an icon \
for the navigation bar button of type: \(type)
""")
return
}

imageView.image = icon?.tintedImageWithColor(.GiniCapture.accent1)

// Set up title if available and accessibility value for the stackview
if let buttonTitle = buttonTitle {
titleLabel.attributedText = NSAttributedString(string: buttonTitle,
attributes: textAttributes())
if stackView.accessibilityValue == nil {
titleLabel.attributedText = NSAttributedString(string: buttonTitle, attributes: textAttributes())

// Add accessibility value with backString only for the back button when title is not equal to backString
if case .back = type {
let backString = NSLocalizedStringPreferredFormat("ginicapture.navigationbar.accessibility.back",
comment: "Back")
stackView.accessibilityValue = (buttonTitle == backString) ? buttonTitle :
"\(buttonTitle) \(backString)"
} else {
stackView.accessibilityValue = buttonTitle
}
}
}

private func titleAndIcon(for type: BarButtonType) -> (String?, UIImage?) {
switch type {
case .cancel:
return (NSLocalizedStringPreferredFormat("ginicapture.navigationbar.analysis.back",
comment: "Cancel"),
UIImageNamedPreferred(named: "barButton_cancel"))
case .help:
return (NSLocalizedStringPreferredFormat("ginicapture.navigationbar.camera.help",
comment: "Help"),
UIImageNamedPreferred(named: "barButton_help"))
case .back(let title):
return (title, UIImageNamedPreferred(named: "barButton_back"))
case .done:
return (NSLocalizedStringPreferredFormat("ginicapture.imagepicker.openbutton",
comment: "Done"),
UIImageNamedPreferred(named: "barButton_done"))
case .skip:
return (NSLocalizedStringPreferredFormat("ginicapture.onboarding.skip",
comment: "Skip button"),
nil)
}
}

private func textAttributes() -> [NSAttributedString.Key: Any] {
var attributes: [NSAttributedString.Key: Any]
let buttonFont = configuration.textStyleFonts[.body]
Expand Down
1 change: 1 addition & 0 deletions Sources/GiniCaptureSDK/Core/Extensions/Data.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ extension Data {
0x47: "image/gif",
0x49: "image/tiff",
0x4D: "image/tiff",
0x52: "image/webp",
0x25: "application/pdf",
0xD0: "application/vnd",
0x46: "text/plain"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//
// GiniBankDocument+Convenience.swift
//
// Copyright © 2024 Gini GmbH. All rights reserved.
//

import GiniBankAPILibrary
import UIKit

extension Document.UploadMetadata {
convenience init(
interfaceOrientation: UIInterfaceOrientation,
documentSource: DocumentSource,
importMethod: DocumentImportMethod?
) {
self.init(
giniCaptureVersion: GiniCaptureSDKVersion,
deviceOrientation: interfaceOrientation.isLandscape ? "landscape" : "portrait",
source: documentSource.value,
importMethod: importMethod?.rawValue ?? "",
entryPoint: GiniConfiguration.shared.entryPoint.stringValue,
osVersion: UIDevice.current.systemVersion
)
}

convenience init(
deviceOrientation: UIDeviceOrientation,
documentSource: DocumentSource,
importMethod: DocumentImportMethod?
) {
self.init(
giniCaptureVersion: GiniCaptureSDKVersion,
deviceOrientation: deviceOrientation.isLandscape ? "landscape" : "portrait",
source: documentSource.value,
importMethod: importMethod?.rawValue ?? "",
entryPoint: GiniConfiguration.shared.entryPoint.stringValue,
osVersion: UIDevice.current.systemVersion
)
}
}
7 changes: 7 additions & 0 deletions Sources/GiniCaptureSDK/Core/GiniConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,13 @@ import GiniBankAPILibrary
case button
// Must be used when the user launches the SDK from a text field.
case field

var stringValue: String {
switch self {
case .button: "button"
case .field: "field"
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ fileprivate extension GiniCaptureDocumentValidator {
if document.extractedParameters[QRCodesExtractor.epsCodeUrlKey] == nil {
throw DocumentValidationError.qrCodeFormatNotValid
}
case .some(.giniQRCode):
if document.extractedParameters[QRCodesExtractor.giniCodeUrlKey] == nil {
throw DocumentValidationError.qrCodeFormatNotValid
}
case .none:
throw DocumentValidationError.qrCodeFormatNotValid
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import UIKit
import ImageIO
import AVFoundation
import MobileCoreServices
import GiniBankAPILibrary

typealias MetaInformation = NSDictionary

Expand Down Expand Up @@ -92,13 +93,7 @@ final class ImageMetaInformationManager {
// user comment fields
let userCommentRotation = "RotDeltaDeg"
let userCommentContentId = "ContentId"
let userCommentPlatform = "Platform"
let userCommentOSVer = "OSVer"
let userCommentGiniVersionVer = "GiniCaptureVer"
let userCommentDeviceOrientation = "DeviceOrientation"
let userCommentSource = "Source"
let userCommentImportMethod = "ImportMethod"
let userCommentEntryPoint = "EntryPoint"

var imageData: Data?
var metaInformation: MetaInformation?
Expand Down Expand Up @@ -232,43 +227,24 @@ final class ImageMetaInformationManager {
return nil
}

fileprivate func entryFieldString(_ entryPoint: GiniConfiguration.GiniEntryPoint) -> String {
switch entryPoint {
case .button:
return "button"
case .field:
return "field"
}
}

fileprivate func userComment(rotationDegrees: Int? = nil) -> String {
let platform = "iOS"
let osVersion = UIDevice.current.systemVersion
let GiniCaptureVersion = GiniCapture.versionString
let uuid = imageUUID()
let entryPoint = entryFieldString(GiniConfiguration.shared.entryPoint)
var comment = "\(userCommentPlatform)=\(platform)," +
"\(userCommentOSVer)=\(osVersion)," +
"\(userCommentGiniVersionVer)=\(GiniCaptureVersion)," +
"\(userCommentContentId)=\(uuid)," +
"\(userCommentSource)=\(imageSource.value)," +
"\(userCommentEntryPoint)=\(entryPoint)"

if let imageImportMethod = imageImportMethod, imageSource.value != DocumentSource.camera.value {
comment += ",\(userCommentImportMethod)=\(imageImportMethod.rawValue)"
}

var rotationNorm = ""
if let rotationDegrees = rotationDegrees {
// normalize the rotation to 0-360
let rotation = imageRotationDeltaDegrees() + rotationDegrees
let rotationNorm = normalizedDegrees(imageRotation: rotation)
comment += ",\(userCommentRotation)=\(rotationNorm)"
}

if let deviceOrientationOnCapture = deviceOrientationOnCapture {
comment += ",\(userCommentDeviceOrientation)=\(deviceOrientationOnCapture)"
rotationNorm = "\(normalizedDegrees(imageRotation: imageRotationDeltaDegrees() + rotationDegrees))"
}
return comment
let importMethod = imageSource.value != DocumentSource.camera.value
? imageImportMethod?.rawValue ?? ""
: ""
return Document.UploadMetadata.constructComment(
osVersion: UIDevice.current.systemVersion,
giniVersion: GiniCapture.versionString,
contentId: imageUUID(),
source: imageSource.value,
entryPoint: GiniConfiguration.shared.entryPoint.stringValue,
importMethod: importMethod,
deviceOrientation: deviceOrientationOnCapture ?? "",
rotation: rotationNorm
)
}

fileprivate func imageUUID() -> String {
Expand Down
19 changes: 18 additions & 1 deletion Sources/GiniCaptureSDK/Core/Helpers/QRCodesExtractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,27 @@ public enum QRCodesFormat {
case epc06912
case eps4mobile
case bezahl
case giniQRCode

var prefixURL: String {
switch self {
case .epc06912:
return "BCD"
case .eps4mobile:
return "epspayment://"
case .bezahl:
return "bank://"
case .giniQRCode:
return "https://pay.gini.net/"
}
}
}

public final class QRCodesExtractor {

public static let epsCodeUrlKey = "epsPaymentQRCodeUrl"

public static let giniCodeUrlKey = "giniPaymentQRCodeUrl"

class func extractParameters(from string: String, withFormat qrCodeFormat: QRCodesFormat?) -> [String: String] {
switch qrCodeFormat {
case .some(.bezahl):
Expand All @@ -25,6 +40,8 @@ public final class QRCodesExtractor {
return extractParameters(fromEPC06912CodeString: string)
case .some(.eps4mobile):
return [epsCodeUrlKey: string]
case .some(.giniQRCode):
return [giniCodeUrlKey: string]
case .none:
return [:]
}
Expand Down
15 changes: 13 additions & 2 deletions Sources/GiniCaptureSDK/Core/Models/GiniCaptureDocument.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// Copyright © 2017 Gini GmbH. All rights reserved.
//

import GiniBankAPILibrary
import UIKit

/**
Expand All @@ -14,6 +15,7 @@ import UIKit

@objc public protocol GiniCaptureDocument: AnyObject {
var type: GiniCaptureDocumentType { get }
var uploadMetadata: Document.UploadMetadata? { get }
var data: Data { get }
var id: String { get }
var previewImage: UIImage? { get }
Expand Down Expand Up @@ -74,12 +76,13 @@ public class GiniCaptureDocumentBuilder: NSObject {
*/
public func build(with data: Data, fileName: String?) -> GiniCaptureDocument? {
if data.isPDF {
return GiniPDFDocument(data: data, fileName: fileName)
return GiniPDFDocument(data: data, fileName: fileName, uploadMetadata: generateUploadMetadata())
} else if data.isImage {
return GiniImageDocument(data: data,
imageSource: documentSource,
imageImportMethod: importMethod,
deviceOrientation: deviceOrientation)
deviceOrientation: deviceOrientation,
uploadMetadata: generateUploadMetadata())
}
return nil
}
Expand All @@ -102,6 +105,14 @@ public class GiniCaptureDocumentBuilder: NSObject {
completion(self.build(with: data, fileName: openURL.lastPathComponent))
}
}

public func generateUploadMetadata() -> Document.UploadMetadata {
return Document.UploadMetadata(
interfaceOrientation: deviceOrientation ?? .portrait,
documentSource: documentSource,
importMethod: importMethod
)
}
}

private extension GiniCaptureDocumentBuilder {
Expand Down
7 changes: 5 additions & 2 deletions Sources/GiniCaptureSDK/Core/Models/GiniImageDocument.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// Copyright © 2017 Gini GmbH. All rights reserved.
//

import GiniBankAPILibrary
import UIKit
import MobileCoreServices

Expand All @@ -22,6 +23,7 @@ final public class GiniImageDocument: NSObject, GiniCaptureDocument {
public var previewImage: UIImage?
public var isReviewable: Bool
public var isImported: Bool
public var uploadMetadata: Document.UploadMetadata?
public var rotationDelta: Int { // Should be normalized to be in [0, 360)
return self.metaInformationManager.imageRotationDeltaDegrees()
}
Expand All @@ -43,11 +45,12 @@ final public class GiniImageDocument: NSObject, GiniCaptureDocument {
processedImageData: Data? = nil,
imageSource: DocumentSource,
imageImportMethod: DocumentImportMethod? = nil,
deviceOrientation: UIInterfaceOrientation? = nil) {
deviceOrientation: UIInterfaceOrientation? = nil,
uploadMetadata: Document.UploadMetadata? = nil) {
self.previewImage = UIImage(data: processedImageData ?? data)
self.isReviewable = true
self.id = UUID().uuidString

self.uploadMetadata = uploadMetadata
switch imageSource {
case .appName(name: _):
isFromOtherApp = true
Expand Down
Loading

0 comments on commit 693b559

Please sign in to comment.