Skip to content

Commit

Permalink
Merge branch 'develop' into COIOS-802_identify_native_redirect_flow
Browse files Browse the repository at this point in the history
  • Loading branch information
goergisn authored Nov 14, 2024
2 parents 3d9f471 + 0b3bd49 commit 896e151
Show file tree
Hide file tree
Showing 7,117 changed files with 8,496 additions and 7,811 deletions.
The diff you're trying to view is too large. We only load the first 3000 changed files.
17 changes: 17 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
push:
branches:
- develop
- release/**

jobs:
tests:
Expand All @@ -15,6 +16,22 @@ jobs:
matrix:
include:

- version: '17.2'
runtime: 'iOS-17-2'
device: 'iPhone 15'
displayname: 'iPhone-15'
os: 'macos-14-xlarge'
xcode_version: '15.1'
needs_custom_sim: false # Takes the shipped simulator that comes with Xcode 15

- version: '17.2'
runtime: 'iOS-17-2'
device: 'iPad Air (5th generation)'
displayname: 'iPad-Air-5'
os: 'macos-14-xlarge'
xcode_version: '15.1'
needs_custom_sim: false # Takes the shipped simulator that comes with Xcode 15

- version: '16.4'
runtime: 'iOS-16-4'
device: 'iPhone 14'
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/detect_api_changes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
PROJECT_FOLDER=$PWD
NEW="${{ env.source }}~${{ env.githubRepo }}"
if [[ '${{ github.head_ref || env.noTargetBranch }}' == 'release/*' ]]
if [[ '${{ github.head_ref || env.noTargetBranch }}' == release/* ]]
then
LATEST_TAG=$(git describe --tags --abbrev=0)
OLD="$LATEST_TAG~${{ env.githubRepo }}"
Expand All @@ -47,7 +47,7 @@ jobs:
fi
cd Scripts
./public-api-diff project --new "$NEW" --old "$OLD" --output "$PROJECT_FOLDER/api_comparison.md" --log-output "$PROJECT_FOLDER/logs.txt"
./public-api-diff project --new "$NEW" --old "$OLD" --platform iOS --output "$PROJECT_FOLDER/api_comparison.md" --log-output "$PROJECT_FOLDER/logs.txt"
cat "$PROJECT_FOLDER/logs.txt"
cat "$PROJECT_FOLDER/api_comparison.md" >> $GITHUB_STEP_SUMMARY
env:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/validate_ticket.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ jobs:
- name: Check ticket reference
env:
PR_BODY: ${{ github.event.pull_request.body }}
TICKET_REGEX: '<ticket>\s*COIOS-[0-9]+\s*</ticket>'
TICKET_REGEX: '<ticket>\s*[A-Z]+-[0-9]+\s*</ticket>'
run: |
if ! grep -zqP "$TICKET_REGEX" <<< "$PR_BODY"; then
echo "::error::Pull requests must have a ticket number in the pull request body in the format '<ticket>COIOS-XXX</ticket>' where XXX is a numeric ticket number"
echo "::error::Pull requests must have a ticket number in the pull request body in the format '<ticket>STRING-XXX</ticket>' where XXX is a numeric ticket number"
exit 1
fi
4 changes: 2 additions & 2 deletions Adyen.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'Adyen'
s.version = '5.12.0'
s.version = '5.13.0'
s.summary = "Adyen Components for iOS"
s.description = <<-DESC
Adyen Components for iOS allows you to accept in-app payments by providing you with the building blocks you need to create a checkout experience.
Expand Down Expand Up @@ -94,7 +94,7 @@ Pod::Spec.new do |s|

s.subspec 'DelegatedAuthentication' do |plugin|
plugin.source_files = 'AdyenDelegatedAuthentication/**/*.swift'
plugin.dependency 'AdyenAuthentication', '3.0.0'
plugin.dependency 'AdyenAuthentication', '3.1.0'
end

s.subspec 'Core' do |plugin|
Expand Down
138 changes: 75 additions & 63 deletions Adyen.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

131 changes: 18 additions & 113 deletions Adyen/Analytics/AnalyticsProvider/AnalyticsProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,73 +7,40 @@
import AdyenNetworking
import Foundation

@_spi(AdyenInternal)
public protocol AnalyticsProviderProtocol {

var checkoutAttemptId: String? { get }

/// Sends the initial data and retrieves the checkout attempt id as a response.
func sendInitialAnalytics(with flavor: AnalyticsFlavor, additionalFields: AdditionalAnalyticsFields?)

/// Adds an info event to be sent.
func add(info: AnalyticsEventInfo)

/// Adds a log event to be sent.
func add(log: AnalyticsEventLog)

/// Adds an error event to be sent.
func add(error: AnalyticsEventError)
}

internal final class AnalyticsProvider: AnalyticsProviderProtocol {

private enum Constants {
static let batchInterval: TimeInterval = 10
static let infoLimit = 50
static let logLimit = 5
static let errorLimit = 5
}
internal final class AnalyticsProvider: AnyAnalyticsProvider {

// MARK: - Properties

internal let apiClient: APIClientProtocol
internal let configuration: AnalyticsConfiguration
internal private(set) var checkoutAttemptId: String?
internal let eventDataSource: AnyAnalyticsEventDataSource
internal var checkoutAttemptId: String? {
didSet {
eventAnalyticsProvider?.checkoutAttemptId = checkoutAttemptId
}
}

internal var eventAnalyticsProvider: AnyEventAnalyticsProvider?

private let uniqueAssetAPIClient: UniqueAssetAPIClient<InitialAnalyticsResponse>

private var batchTimer: Timer?
private let batchInterval: TimeInterval
private let context: AnalyticsContext

// MARK: - Initializers

internal init(
apiClient: APIClientProtocol,
configuration: AnalyticsConfiguration,
eventDataSource: AnyAnalyticsEventDataSource,
batchInterval: TimeInterval = Constants.batchInterval
context: AnalyticsContext,
eventAnalyticsProvider: AnyEventAnalyticsProvider?
) {
self.apiClient = apiClient
self.configuration = configuration
self.context = context
self.eventAnalyticsProvider = eventAnalyticsProvider
self.uniqueAssetAPIClient = UniqueAssetAPIClient<InitialAnalyticsResponse>(apiClient: apiClient)
self.eventDataSource = eventDataSource
self.batchInterval = batchInterval
}

deinit {
// attempt to send remaining events on deallocation
batchTimer?.invalidate()
sendEventsIfNeeded()
}

// MARK: - AnalyticsProviderProtocol
// MARK: - AnyAnalyticsProvider

internal func sendInitialAnalytics(with flavor: AnalyticsFlavor, additionalFields: AdditionalAnalyticsFields?) {
let analyticsData = AnalyticsData(
flavor: flavor,
additionalFields: additionalFields,
context: configuration.context
context: context
)

let initialAnalyticsRequest = InitialAnalyticsRequest(data: analyticsData)
Expand All @@ -84,87 +51,25 @@ internal final class AnalyticsProvider: AnalyticsProviderProtocol {
}

internal func add(info: AnalyticsEventInfo) {
guard configuration.isEnabled else { return }

eventDataSource.add(info: info)
eventAnalyticsProvider?.add(info: info)
}

internal func add(log: AnalyticsEventLog) {
guard configuration.isEnabled else { return }

eventDataSource.add(log: log)
sendEventsIfNeeded()
eventAnalyticsProvider?.add(log: log)
}

internal func add(error: AnalyticsEventError) {
guard configuration.isEnabled else { return }

eventDataSource.add(error: error)
sendEventsIfNeeded()
}

internal func sendEventsIfNeeded() {
guard configuration.isEnabled else { return }
guard let request = requestWithAllEvents() else { return }

apiClient.perform(request) { [weak self] result in
guard let self else { return }
// clear the sent events on successful send
switch result {
case .success:
self.removeEvents(sentBy: request)
self.startNextTimer()
case .failure:
break
}
}
eventAnalyticsProvider?.add(error: error)
}

// MARK: - Private

/// Checks the event arrays safely and creates the request with them if there is any to send.
private func requestWithAllEvents() -> AnalyticsRequest? {
guard let checkoutAttemptId,
let events = eventDataSource.allEvents() else { return nil }

// as per this call's limitation, we only send up to the
// limit of each event and discard the older ones
let platform = configuration.context.platform.rawValue
var request = AnalyticsRequest(
checkoutAttemptId: checkoutAttemptId,
platform: platform
)
request.infos = events.infos.suffix(Constants.infoLimit)
request.logs = events.logs.suffix(Constants.logLimit)
request.errors = events.errors.suffix(Constants.errorLimit)
return request
}

private func saveCheckoutAttemptId(from result: Result<InitialAnalyticsResponse, Error>) {
switch result {
case let .success(response):
checkoutAttemptId = response.checkoutAttemptId
startNextTimer()
case .failure:
checkoutAttemptId = nil
}
}

private func removeEvents(sentBy request: AnalyticsRequest) {
let collection = AnalyticsEventWrapper(
infos: request.infos,
logs: request.logs,
errors: request.errors
)
eventDataSource.removeEvents(matching: collection)
}

private func startNextTimer() {
guard configuration.isEnabled else { return }

batchTimer?.invalidate()
batchTimer = Timer.scheduledTimer(withTimeInterval: batchInterval, repeats: true) { [weak self] _ in
self?.sendEventsIfNeeded()
}
}
}
32 changes: 32 additions & 0 deletions Adyen/Analytics/AnalyticsProvider/AnalyticsProviderProtocol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// Copyright (c) 2024 Adyen N.V.
//
// This file is open source and available under the MIT license. See the LICENSE file for more info.
//

import Foundation

@_spi(AdyenInternal)
public protocol AnyInitialAnalyticsProvider {

/// Sends the initial data and retrieves the checkout attempt id as a response.
func sendInitialAnalytics(with flavor: AnalyticsFlavor, additionalFields: AdditionalAnalyticsFields?)
}

@_spi(AdyenInternal)
public protocol AnyEventAnalyticsProvider {

var checkoutAttemptId: String? { get set }

/// Adds an info event to be sent.
func add(info: AnalyticsEventInfo)

/// Adds a log event to be sent.
func add(log: AnalyticsEventLog)

/// Adds an error event to be sent.
func add(error: AnalyticsEventError)
}

@_spi(AdyenInternal)
public protocol AnyAnalyticsProvider: AnyInitialAnalyticsProvider, AnyEventAnalyticsProvider {}
Loading

0 comments on commit 896e151

Please sign in to comment.