From 9456ed3f0aa20e0233ae41950c77685d62b6abf1 Mon Sep 17 00:00:00 2001 From: Dima Khludkov <57712402+dmytrokhl@users.noreply.github.com> Date: Thu, 18 Feb 2021 06:14:49 +0200 Subject: [PATCH 1/5] Check TR status. (#62) --- Sources/VGSShowSDK/Core/APIClient/APIClient.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/VGSShowSDK/Core/APIClient/APIClient.swift b/Sources/VGSShowSDK/Core/APIClient/APIClient.swift index 06c78465..874d4865 100644 --- a/Sources/VGSShowSDK/Core/APIClient/APIClient.swift +++ b/Sources/VGSShowSDK/Core/APIClient/APIClient.swift @@ -28,9 +28,10 @@ internal class APIClient { let source = VGSAnalyticsClient.Constants.Metadata.source let medium = VGSAnalyticsClient.Constants.Metadata.medium + let trStatus = VGSAnalyticsClient.shared.shouldCollectAnalytics ? "default" : "none" return [ - "vgs-client": "source=\(source)&medium=\(medium)&content=\(Utils.vgsShowVersion)&osVersion=\(versionString)&vgsShowSessionId=\(VGSShowAnalyticsSession.shared.vgsShowSessionId)" + "vgs-client": "source=\(source)&medium=\(medium)&content=\(Utils.vgsShowVersion)&osVersion=\(versionString)&vgsShowSessionId=\(VGSShowAnalyticsSession.shared.vgsShowSessionId)&tr=\(trStatus)" ] }() From e0ec86f8ab14cb8c631deb7d7c67118cef7ad72e Mon Sep 17 00:00:00 2001 From: EugeneIOs <72962856+EugeneIOs@users.noreply.github.com> Date: Thu, 25 Feb 2021 11:19:40 +0200 Subject: [PATCH 2/5] Add Satellite integration --- .../VGSShowSDK/Core/APIClient/APIClient.swift | 49 +++- .../APIHostName/APICustomHostStatus.swift | 20 +- .../APIHostName/APIHostURLPolicy.swift | 33 ++- .../VGSShowSatelliteUtils.swift | 178 +++++++++++++ .../Core/Analytics/VGSAnalyticsClient.swift | 8 +- Sources/VGSShowSDK/Core/Show/VGSShow.swift | 10 +- .../Utils/Helpers/Loggers/VGSLogger.swift | 3 +- .../Masks/VGSShowRegexMaskTests.swift | 9 +- .../Masks/VGSShowSecureTextRangesTests.swift | 16 +- .../APIHostnameValidatorTests.swift | 8 +- .../VGSShow+SatelliteTests.swift | 110 ++++++++ .../VGSShowAPIClientSatelliteTests.swift | 110 ++++++++ .../VGSShowSatelliteUtilsTests.swift | 242 ++++++++++++++++++ .../VGSShowTests/VGSShowBaseTestCase.swift | 22 ++ .../VGSShowTests/VGSShowTests.swift | 6 +- .../Application/AppDelegate.swift | 33 +++ .../Controllers/CollectViewController.swift | 2 +- .../Controllers/ShowDemoViewController.swift | 9 - .../Helpers/UIViewController+Extensions.swift | 1 + VGSShowDemoApp/VGSShowDemoApp/Info.plist | 5 + .../Storyboards/Main.storyboard | 24 +- VGSShowSDK.xcodeproj/project.pbxproj | 36 +++ 22 files changed, 880 insertions(+), 54 deletions(-) create mode 100644 Sources/VGSShowSDK/Core/APIClient/SatelliteUtils/VGSShowSatelliteUtils.swift create mode 100644 Tests/VGSShowSDKTests/VGSShowTests/Satellite Tests/VGSShow+SatelliteTests.swift create mode 100644 Tests/VGSShowSDKTests/VGSShowTests/Satellite Tests/VGSShowAPIClientSatelliteTests.swift create mode 100644 Tests/VGSShowSDKTests/VGSShowTests/Satellite Tests/VGSShowSatelliteUtilsTests.swift create mode 100644 Tests/VGSShowSDKTests/VGSShowTests/VGSShowBaseTestCase.swift diff --git a/Sources/VGSShowSDK/Core/APIClient/APIClient.swift b/Sources/VGSShowSDK/Core/APIClient/APIClient.swift index 874d4865..4e52a851 100644 --- a/Sources/VGSShowSDK/Core/APIClient/APIClient.swift +++ b/Sources/VGSShowSDK/Core/APIClient/APIClient.swift @@ -7,20 +7,24 @@ import Foundation +/// Holds base API client logic. internal class APIClient { typealias RequestCompletion = ((_ response: APIRequestResult) -> Void)? // MARK: - Constants - enum Constants { + /// Constants. + internal enum Constants { static let validStatuses: Range = 200..<300 } // MARK: - Vars - var customHeader: VGSHTTPHeaders? + /// Custom headers. + internal var customHeader: VGSHTTPHeaders? + /// Default request headers with vgs client info. internal static let defaultHttpHeaders: VGSHTTPHeaders = { // Add Headers let version = ProcessInfo.processInfo.operatingSystemVersion @@ -38,9 +42,13 @@ internal class APIClient { /// URLSession object. internal let urlSession = URLSession(configuration: .ephemeral) + /// Vault ID. private let vaultId: String + + /// Vault URL. private let vaultUrl: URL? + /// Base URL depends on current api policy. internal var baseURL: URL? { return self.hostURLPolicy.url } @@ -51,11 +59,18 @@ internal class APIClient { /// Serial queue for syncing requests on resolving hostname flow. private let dataSyncQueue: DispatchQueue = .init(label: "iOS.VGSShowSDK.ResolveHostNameRequestsQueue") + /// Sync semaphore. private let syncSemaphore: DispatchSemaphore = .init(value: 1) // MARK: - Initialization - required init(tenantId: String, regionalEnvironment: String, hostname: String?) { + /// Initialization. + /// - Parameters: + /// - tenantId: `String` object, should be valid tenant id. + /// - regionalEnvironment: `String` object, should be valid environment. + /// - hostname: `String?` object, should be valid hostname or `nil`. + /// - satellitePort: `Int?` object, custom port for satellite configuration. **IMPORTANT! Use only with .sandbox environment!**. + required internal init(tenantId: String, regionalEnvironment: String, hostname: String?, satellitePort: Int?) { self.vaultUrl = VGSShow.generateVaultURL(tenantId: tenantId, regionalEnvironment: regionalEnvironment) self.vaultId = tenantId @@ -65,6 +80,26 @@ internal class APIClient { return } + // Check satellite port is *nil* for regular API flow. + guard satellitePort == nil else { + // Try to build satellite URL. + guard let port = satellitePort, let satelliteURL = VGSShowSatelliteUtils.buildSatelliteURL(with: regionalEnvironment, hostname: hostname, satellitePort: port) else { + + // Use vault URL as fallback if cannot resolve satellite flow. + self.hostURLPolicy = .vaultURL(validVaultURL) + return + } + + // Use satellite URL and return. + self.hostURLPolicy = .satelliteURL(satelliteURL) + + let message = "Satellite has been configured successfully! Satellite URL is: \(satelliteURL.absoluteString)" + let event = VGSLogEvent(level: .info, text: message) + VGSLogger.shared.forwardLogEvent(event) + + return + } + guard let hostnameToResolve = hostname, !hostnameToResolve.isEmpty else { if let name = hostname, name.isEmpty { @@ -85,7 +120,7 @@ internal class APIClient { // MARK: - Public - func sendRequestWithJSON(path: String, method: VGSHTTPMethod = .post, value: VGSJSONData?, completion block: RequestCompletion) { + internal func sendRequestWithJSON(path: String, method: VGSHTTPMethod = .post, value: VGSJSONData?, completion block: RequestCompletion) { let payload = VGSRequestPayloadBody.json(value) resolveURLForRequest(path: path, method: method, payload: payload, block: block) @@ -103,9 +138,15 @@ internal class APIClient { let url: URL? + let infoEventText = "API will start request with current URL policy: \(hostURLPolicy.description)" + let infoEvent = VGSLogEvent(level: .info, text: infoEventText) + VGSLogger.shared.forwardLogEvent(infoEvent) + switch hostURLPolicy { case .invalidVaultURL: url = nil + case .satelliteURL(let satelliteURL): + url = satelliteURL case .vaultURL(let vaultURL): url = vaultURL case .customHostURL(let status): diff --git a/Sources/VGSShowSDK/Core/APIClient/APIHostName/APICustomHostStatus.swift b/Sources/VGSShowSDK/Core/APIClient/APIHostName/APICustomHostStatus.swift index fb5fa9a1..f06cd678 100644 --- a/Sources/VGSShowSDK/Core/APIClient/APIHostName/APICustomHostStatus.swift +++ b/Sources/VGSShowSDK/Core/APIClient/APIHostName/APICustomHostStatus.swift @@ -31,7 +31,8 @@ internal enum APICustomHostStatus { */ case useDefaultVault(_ vaultURL: URL) - var url: URL? { + /// `URL` inferred from custom hostname status flow. + internal var url: URL? { switch self { case .isResolving: return nil @@ -42,3 +43,20 @@ internal enum APICustomHostStatus { } } } + +// MARK: - CustomStringConvertible + +extension APICustomHostStatus: CustomStringConvertible { + + /// Custom description. + var description: String { + switch self { + case .useDefaultVault(let url): + return ".useDefaultVault, default Vault url: \(url.absoluteString)" + case .resolved(let url): + return ".resolved, custom host url: \(url.absoluteString)" + case .isResolving(let hostnameToResolve): + return ".isResolving hostname in progress for \(hostnameToResolve)" + } + } +} diff --git a/Sources/VGSShowSDK/Core/APIClient/APIHostName/APIHostURLPolicy.swift b/Sources/VGSShowSDK/Core/APIClient/APIHostName/APIHostURLPolicy.swift index c01f7eb9..ab09816b 100644 --- a/Sources/VGSShowSDK/Core/APIClient/APIHostName/APIHostURLPolicy.swift +++ b/Sources/VGSShowSDK/Core/APIClient/APIHostName/APIHostURLPolicy.swift @@ -5,6 +5,7 @@ import Foundation +/// Defines API client policy for resolving URL. internal enum APIHostURLPolicy { /** @@ -28,7 +29,16 @@ internal enum APIHostURLPolicy { */ case invalidVaultURL - var url: URL? { + /** + Use satellite url for local testing. + + - Parameters: + - url: `URL` object, should be valid satellite URL for local testing. + */ + case satelliteURL(_ satelliteURL: URL) + + /// `URL?` inferred from current API policy flow. + internal var url: URL? { switch self { case .invalidVaultURL: return nil @@ -36,6 +46,27 @@ internal enum APIHostURLPolicy { return vaultURL case .customHostURL(let hostStatus): return hostStatus.url + case .satelliteURL(let satelliteURL): + return satelliteURL } } } + +// MARK: - CustomStringConvertible + +extension APIHostURLPolicy: CustomStringConvertible { + + /// Custom description. + var description: String { + switch self { + case .invalidVaultURL: + return "*.invalidVaultURL* - API url is *nil*, SDK has incorrect configuration." + case .vaultURL(let vaultURL): + return "*.vaultURL* - use regular flow, url: \(vaultURL.absoluteString)" + case .customHostURL(let status): + return "*.customHostURL* with status: \(status.description)" + case .satelliteURL(let satelliteURL): + return "*.satelliteURL* - url: \(satelliteURL.absoluteString)" + } + } +} diff --git a/Sources/VGSShowSDK/Core/APIClient/SatelliteUtils/VGSShowSatelliteUtils.swift b/Sources/VGSShowSDK/Core/APIClient/SatelliteUtils/VGSShowSatelliteUtils.swift new file mode 100644 index 00000000..6133c250 --- /dev/null +++ b/Sources/VGSShowSDK/Core/APIClient/SatelliteUtils/VGSShowSatelliteUtils.swift @@ -0,0 +1,178 @@ +// +// VGSShowSatelliteUtils.swift +// VGSShowSDK +// +// Created on 22.02.2021. +// Copyright © 2021 VGS. All rights reserved. +// + +import Foundation + +/// VGSShowSDK utils for satellite. +internal class VGSShowSatelliteUtils { + + /// Enable assertions on default, disable in unit tests. + internal static var isAssertionsEnabled = true + + /// Constants. + internal enum Constants { + + /// Valid port numbers. https://en.wikipedia.org/wiki/Port_(computer_networking)#Port_number + static let validPortNumbers: ClosedRange = 1...65535 + + /// Valid localhost prefix. + static let validLocalHostPrefix = "localhost" + + /// Valid localhost IP address prefix. + static let validLocalIPAddressPrefix = "192.168" + } + + /// Build URL for satellite. + /// - Parameters: + /// - environment: `String` object, only `sandbox` is valid. + /// - hostname: `String` object, should be valid hostname for satellite. + /// - satellitePort: `Int` object, should be valid port. + /// - Returns: `URL?`, satellite `URL` or `nil` for invalid configuration. + internal static func buildSatelliteURL(with environment: String, hostname: String?, satellitePort: Int) -> URL? { + + // Check satelliteHostname is set. + guard let satelliteHostname = hostname else { + let errorText = "SATELLITE CONFIGURATION ERROR! HOSTNAME NOT SET! SHOULD BE *http://localhost* or in local IP format http://192.168.X.X" + let event = VGSLogEvent(level: .warning, text: errorText, severityLevel: .error) + VGSLogger.shared.forwardLogEvent(event) + + forwardAssertion(with: errorText) + + return nil + } + + // Check environment == .sandbox, validate port. + guard isEnvironmentValidForSatellite(environment), isSatellitePortValid(satellitePort) else { + return nil + } + + // Check hostname is valid for satellite. + guard isSatelliteHostnameValid(satelliteHostname) else { + return nil + } + + // Cannot build hostname URL for satellite. + let errorText = "SATELLITE CONFIGURATION ERROR! HOSTNAME \(satelliteHostname) IS NOT VALID AND CANNOT BE NORMALIZED FOR SATELLITE! SHOULD BE *http://localhost* or *http://192.168.*" + let event = VGSLogEvent(level: .warning, text: errorText, severityLevel: .error) + + // Normalize hostname. + guard let normalizedHostname = satelliteHostname.normalizedHostname() else { + forwardAssertion(with: errorText) + VGSLogger.shared.forwardLogEvent(event) + return nil + } + + let infoText = "normalized hostname for satellite: \(normalizedHostname)" + let normalizeHostnameEvent = VGSLogEvent(level: .info, text: infoText) + VGSLogger.shared.forwardLogEvent(normalizeHostnameEvent) + + guard let url = URL(string: "http://" + normalizedHostname + ":\(satellitePort)") else { + + VGSLogger.shared.forwardLogEvent(event) + + forwardAssertion(with: errorText) + return nil + } + + return url + } + + /// Validate satellite hostname. Should be http://localhost or http://192.168 or 192.168.1.3 + /// Any other hostnames will be ignored for satellite. + /// - Parameter hostname: `String` object, hostname to validate. + /// - Returns: `true` if hostname is valid for satellite. + internal static func isSatelliteHostnameValid(_ hostname: String) -> Bool { + + let errorText = "SATELLITE CONFIGURATION ERROR! HOSTNAME \(hostname) IS INVALID FOR SATELLITE! SHOULD BE *http://localhost* or *192.168.*" + + // Normalize hostname. Create components to check hostname. + guard let normalizedHostName = hostname.normalizedHostname(), let components = URLComponents(string: normalizedHostName) else { + let event = VGSLogEvent(level: .warning, text: errorText, severityLevel: .error) + VGSLogger.shared.forwardLogEvent(event) + + forwardAssertion(with: errorText) + + return false + } + + var path: String + if let componentHost = components.host { + // Use hostname if component is url with scheme. + path = componentHost + } else { + // Use path if component has path only. + path = components.path + } + + if path == Constants.validLocalHostPrefix { + return true + } + + if path.hasPrefix(Constants.validLocalIPAddressPrefix) && verifyIPHostNameIsCorrect(path) { + return true + } + + let event = VGSLogEvent(level: .warning, text: errorText, severityLevel: .error) + VGSLogger.shared.forwardLogEvent(event) + + forwardAssertion(with: errorText) + + return false + } + + /// Return `true` if environment is valid for satellite (`.sandbox`). + /// - Parameter environment: `String` object, environment to check. + /// - Returns: `Bool` flag, `true` if `.sandbox`, otherwise `false`. + internal static func isEnvironmentValidForSatellite(_ environment: String) -> Bool { + guard environment == VGSEnvironment.sandbox.rawValue else { + let errorText = "CONFIGURATION ERROR! ENVIRONMENT *\(environment)* IS NOT *sandbox*! SATELLITE IS AVAILABLE ONLY FOR *sandbox*!" + let event = VGSLogEvent(level: .warning, text: errorText, severityLevel: .error) + VGSLogger.shared.forwardLogEvent(event) + + forwardAssertion(with: errorText) + + return false + } + + return true + } + + /// Return `true` if port is valid for satellite. + /// - Parameter port: `Int` object, should be valid port. + /// - Returns: `Bool` flag, `true` if port in range *1...65535*, otherwise `false`. + internal static func isSatellitePortValid(_ port: Int) -> Bool { + guard Constants.validPortNumbers.contains(port) else { + let errorText = "SATELLITE CONFIGURATION ERROR! PORT \(port) is invalid! Should be in range *1...65535*." + let event = VGSLogEvent(level: .warning, text: errorText, severityLevel: .error) + VGSLogger.shared.forwardLogEvent(event) + + forwardAssertion(with: errorText) + + return false + } + + return true + } + + /// Forward satellite assertions. + /// - Parameter assertionText: `String` object, assertion message. + internal static func forwardAssertion(with assertionText: String) { + if isAssertionsEnabled { + assertionFailure(assertionText) + } + } + + /// Verify ip-format hostname has only digits and *.*. + /// - Parameter hostname: `String` object to validate. + /// - Returns: `true` if valid ip format. + internal static func verifyIPHostNameIsCorrect(_ hostname: String) -> Bool { + let characterSet: Set = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "."] + + return Set(hostname).isSubset(of: characterSet) + } +} diff --git a/Sources/VGSShowSDK/Core/Analytics/VGSAnalyticsClient.swift b/Sources/VGSShowSDK/Core/Analytics/VGSAnalyticsClient.swift index 5e1170ee..7aed339b 100644 --- a/Sources/VGSShowSDK/Core/Analytics/VGSAnalyticsClient.swift +++ b/Sources/VGSShowSDK/Core/Analytics/VGSAnalyticsClient.swift @@ -78,12 +78,18 @@ public class VGSAnalyticsClient { "tnt": form.tenantId, "env": env ] - let data: [String: Any] + + var data: [String: Any] if let extraData = extraData { data = deepMerge(formDetails, extraData) } else { data = formDetails } + + if case .satelliteURL = form.apiClient.hostURLPolicy { + data["vgsSatellite"] = true + } + trackEvent(type, status: status, extraData: data) } diff --git a/Sources/VGSShowSDK/Core/Show/VGSShow.swift b/Sources/VGSShowSDK/Core/Show/VGSShow.swift index 81eccfba..4f8b2906 100644 --- a/Sources/VGSShowSDK/Core/Show/VGSShow.swift +++ b/Sources/VGSShowSDK/Core/Show/VGSShow.swift @@ -64,8 +64,9 @@ public final class VGSShow { /// - id: `String` object, your organization vault id. /// - environment: `String` object, your organization vault environment with data region.(e.g. "live", "live-eu1", "sanbox"). /// - hostname: `String` object. Custom Hostname, if not set, data will be sent to Vault Url. Default is `nil`. - public init(id: String, environment: String, hostname: String? = nil) { - self.apiClient = APIClient(tenantId: id, regionalEnvironment: environment, hostname: hostname) + /// - satellitePort: `Int?` object, custom port for satellite configuration. Default is `nil`. **IMPORTANT! Use only with .sandbox environment! Hostname should be specified for valid http://localhost (for simulator) or in local IP format http://192.168.X.X (for real device connected to the same local network as Mac)**. + public init(id: String, environment: String, hostname: String? = nil, satellitePort: Int? = nil) { + self.apiClient = APIClient(tenantId: id, regionalEnvironment: environment, hostname: hostname, satellitePort: satellitePort) self.tenantId = id self.regionalEnvironment = environment } @@ -77,9 +78,10 @@ public final class VGSShow { /// - environment: `VGSEnvironment` object, your organization vault environment. By default `.sandbox`. /// - dataRegion: `String` object, id of data storage region (e.g. "eu-123"). /// - hostname: `String` object. Custom Hostname, if not set, data will be sent to Vault Url. Default is `nil`. - public convenience init(id: String, environment: VGSEnvironment = .sandbox, dataRegion: String? = nil, hostname: String? = nil) { + /// - satellitePort: `Int?` object, custom port for satellite configuration. Default is `nil`. **IMPORTANT! Use only with .sandbox environment! Hostname should be specified for valid http://localhost (for simulator) or in local IP format http://192.168.X.X (for real device connected to the same local network as Mac)** . + public convenience init(id: String, environment: VGSEnvironment = .sandbox, dataRegion: String? = nil, hostname: String? = nil, satellitePort: Int? = nil) { let env = Self.generateRegionalEnvironmentString(environment, region: dataRegion) - self.init(id: id, environment: env, hostname: hostname) + self.init(id: id, environment: env, hostname: hostname, satellitePort: satellitePort) } // MARK: - Manage Views diff --git a/Sources/VGSShowSDK/Utils/Helpers/Loggers/VGSLogger.swift b/Sources/VGSShowSDK/Utils/Helpers/Loggers/VGSLogger.swift index 306f6e17..35c77845 100644 --- a/Sources/VGSShowSDK/Utils/Helpers/Loggers/VGSLogger.swift +++ b/Sources/VGSShowSDK/Utils/Helpers/Loggers/VGSLogger.swift @@ -5,7 +5,8 @@ import Foundation -/// `VGSLogger` encapsulates logging logic and debugging options for VGSShowSDK. Use `.configuration` property to setup these options. `VGSLogger` logging implies only printing logs to Xcode console. It doesn't save logs to persistent store/local file, also it doesn't send debugging logs to backend services. +/// `VGSLogger` encapsulates logging logic and debugging options for VGSShowSDK. Use `.configuration` property to setup these options. +/// `VGSLogger` logging implies only printing logs to Xcode console. It doesn't save logs to persistent store/local file, also it doesn't send debugging logs to backend services. /// **IMPORTANT** You should NOT use logging in your production configuration for live apps. public final class VGSLogger { diff --git a/Tests/VGSShowSDKTests/Masks/VGSShowRegexMaskTests.swift b/Tests/VGSShowSDKTests/Masks/VGSShowRegexMaskTests.swift index 9d9df20b..aa54fba0 100644 --- a/Tests/VGSShowSDKTests/Masks/VGSShowRegexMaskTests.swift +++ b/Tests/VGSShowSDKTests/Masks/VGSShowRegexMaskTests.swift @@ -31,16 +31,15 @@ final class VGSShowRegexMaskTests: XCTestCase { let regex = try NSRegularExpression(pattern: cardNumberPattern, options: []) vgsLabel.addTransformationRegex(regex, template: templates[index]) } catch { - assertionFailure("invalid regex") + XCTFail("invalid regex at index: \(index)") } - print("label.text: \(vgsLabel.label.secureText)") - XCTAssert(vgsLabel.label.secureText == transformedTexts[index]) + XCTAssert(vgsLabel.label.secureText == transformedTexts[index], "label.text: \(vgsLabel.label.secureText ?? "*nil*") should match \(transformedTexts[index])") } // Test reset to raw text. vgsLabel.resetAllMasks() - XCTAssert(vgsLabel.label.secureText == cardNumber) + XCTAssert(vgsLabel.label.secureText == cardNumber, "label.text: \(vgsLabel.label.secureText ?? "*nil*") after reset should match unformatted \(cardNumber)") // Test multiple formatters. for index in 0.. Bool { // Override point for customization after application launch. + return true } } diff --git a/VGSShowDemoApp/VGSShowDemoApp/Controllers/CollectViewController.swift b/VGSShowDemoApp/VGSShowDemoApp/Controllers/CollectViewController.swift index e401c208..e026a354 100644 --- a/VGSShowDemoApp/VGSShowDemoApp/Controllers/CollectViewController.swift +++ b/VGSShowDemoApp/VGSShowDemoApp/Controllers/CollectViewController.swift @@ -70,7 +70,7 @@ class CollectViewController: UIViewController { let cardHolderNameConfiguration = VGSConfiguration(collector: vgsCollect, fieldName: "cardHolderName") cardHolderNameConfiguration.type = .cardHolderName cardHolderNameConfiguration.isRequiredValidOnly = false - cardHolderNameConfiguration.keyboardType = .asciiCapableNumberPad + cardHolderNameConfiguration.keyboardType = .asciiCapable cardHolderName.configuration = cardHolderNameConfiguration cardHolderName.placeholder = "Joe Business" cardHolderName.textAlignment = .natural diff --git a/VGSShowDemoApp/VGSShowDemoApp/Controllers/ShowDemoViewController.swift b/VGSShowDemoApp/VGSShowDemoApp/Controllers/ShowDemoViewController.swift index eb0ac556..fab299f2 100644 --- a/VGSShowDemoApp/VGSShowDemoApp/Controllers/ShowDemoViewController.swift +++ b/VGSShowDemoApp/VGSShowDemoApp/Controllers/ShowDemoViewController.swift @@ -89,15 +89,6 @@ class ShowDemoViewController: UIViewController { let placeholderColor = UIColor.white.withAlphaComponent(0.7) - // Log only warnings and errors. - VGSLogger.shared.configuration.level = .warning - - // Log network requests. - VGSLogger.shared.configuration.isNetworkDebugEnabled = true - - // *You can stop all loggers in app: - // VGSLogger.shared.disableAllLoggers() - // Secure revealed data with mask in ranges cardNumberLabel.isSecureText = true cardNumberLabel.setSecureText(ranges: [VGSTextRange(start: 5, end: 8), diff --git a/VGSShowDemoApp/VGSShowDemoApp/Helpers/UIViewController+Extensions.swift b/VGSShowDemoApp/VGSShowDemoApp/Helpers/UIViewController+Extensions.swift index a1f2c1fe..0867564b 100644 --- a/VGSShowDemoApp/VGSShowDemoApp/Helpers/UIViewController+Extensions.swift +++ b/VGSShowDemoApp/VGSShowDemoApp/Helpers/UIViewController+Extensions.swift @@ -9,6 +9,7 @@ import Foundation import UIKit extension UIViewController { + // swiftlint:disable static func show(message: String, controller: UIViewController) { let toastView = UIView(frame: .zero) toastView.backgroundColor = UIColor.black.withAlphaComponent(0.7) diff --git a/VGSShowDemoApp/VGSShowDemoApp/Info.plist b/VGSShowDemoApp/VGSShowDemoApp/Info.plist index af8868b2..2456211a 100644 --- a/VGSShowDemoApp/VGSShowDemoApp/Info.plist +++ b/VGSShowDemoApp/VGSShowDemoApp/Info.plist @@ -22,6 +22,11 @@ UILaunchStoryboardName LaunchScreen + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + UIMainStoryboardFile Main UIRequiredDeviceCapabilities diff --git a/VGSShowDemoApp/VGSShowDemoApp/Storyboards/Main.storyboard b/VGSShowDemoApp/VGSShowDemoApp/Storyboards/Main.storyboard index 052ab14f..b4c415bd 100644 --- a/VGSShowDemoApp/VGSShowDemoApp/Storyboards/Main.storyboard +++ b/VGSShowDemoApp/VGSShowDemoApp/Storyboards/Main.storyboard @@ -129,7 +129,7 @@ - + @@ -198,10 +198,14 @@