mirror of
https://github.com/RevenueCat/purchases-ios
synced 2026-11-09 22:35:56 +05:45
* Add RC Ads event tracking infrastructure Implements event tracking for RevenueCat Ads following the Paywalls Events pattern. - Adds three event types: ad displayed, ad opened, and ad revenue - Uses actor-based concurrency with file-based event storage - Posts events to e.revenue.cat/v1/events endpoint - Marks track(adEvent:) method as @_spi(Internal) for internal use - Includes comprehensive unit tests with snapshot testing - Available on iOS 15.0+, tvOS 15.0+, macOS 12.0+, watchOS 8.0+ Event schema follows khepri RC Ads input events specification with app_session_id field matching backend expectations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Avoid mapping in EventsRequest+Ad * Consolidate ad events into PaywallEventsManager Eliminates duplication between AdEventsManager and PaywallEventsManager by: - Making PaywallEventsManager handle all event types (ads, paywalls, customerCenter) - Adding feature-aware routing in flushEvents() to send events to correct domains - Ad events → https://e.revenue.cat/v1/events - Other events → https://api-paywalls.revenuecat.com/v1/events - Removing AdEventsManager, AdEventStore, and their tests (~500 lines) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Remove AdsStrings and use PaywallsStrings instead AdsStrings only had 2 strings in use, both of which already exist in PaywallsStrings. Consolidates logging strings to eliminate duplication. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Namespace Precision enum inside AdEvent.RevenueData Moves Precision from a top-level public enum to AdEvent.RevenueData.Precision to avoid polluting the global namespace and reduce autocomplete noise. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Move Precision conformances to end of file Follows the pattern of other types by declaring protocol conformances at the end of the file rather than inline with the type definition. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Remove redundant sessionIdentifier from AdImpressionData The sessionIdentifier field in AdImpressionData was redundant since StoredEvent already tracks appSessionID. This change: - Removes sessionIdentifier from AdImpressionData struct - Uses storedEvent.appSessionID directly when building requests - Renames sessionId to appSessionId to match backend field app_session_id - Updates documentation examples to remove sessionIdentifier parameter - Adds guard statement for optional appSessionID - Updates all test fixtures and regenerates snapshots - Removes iOS26 snapshot references from project 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Remove unneeded SessionId * Add proper assertions to PurchasesAdEventsTests Enhanced test coverage for public ad tracking APIs: - Verify events are tracked via mockPaywallEventsManager - Assert correct event types (displayed, opened, revenue) - Validate all impression data fields - Verify revenue-specific fields (revenueMicros, currency, precision) All 3 tests now pass with proper verification. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Add ENABLE_AD_EVENTS_TRACKING compiler flag * Simplify track ad events APIs * Fix ENABLE_AD_EVENTS_TRACKING scope and refactor Precision to struct Compiler flag fixes: - Move ENABLE_AD_EVENTS_TRACKING #endif to wrap all ad event code including internal enum - Add compiler flag guards to all three test files Precision API refactor: - Change AdRevenue.Precision from enum to struct following Apple's API patterns (like Message.Reason) - Use internal init(rawValue:) to prevent custom precision values - Convert enum cases to static properties maintaining same public API 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Refactor mediatorName from String to MediatorName struct Replace string-based mediator names with a type-safe struct following Apple's API patterns: - Add MediatorName struct with public init(rawValue:) to support custom values - Include predefined static properties for .adMob and .appLovin - Update AdEventData protocol to require MediatorName type - Update all event types (AdDisplayed, AdOpened, AdRevenue) to use MediatorName - Use .rawValue when serializing to network requests - Update all tests and documentation examples to use .appLovin 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Remove RawRepresentable conformance from Precision and update test snapshots - Remove RawRepresentable conformance from AdRevenue.Precision to allow internal init - Update test assertions to use .appLovin instead of string comparison - Update all snapshot test files to use "AppLovin" instead of "MAX" - All ad event tests now passing (11/11) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Fix PaywallDataDecodingTests locale check for region overrides The test was failing when the system locale included region overrides (e.g., "en_US@rg=eszzzz" instead of just "en_US"). Changes: - Use beginWith("en_US") instead of exact equality check - Compare against enConfig directly instead of fetching via Locale.current This makes the test more robust while maintaining the same behavior validation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Rename timestampMs to timestamp but keep serialized timestamp_ms * Use different serverHostURL * Simplify and paralelize postEvents * Adjust usage of ENABLE_AD_EVENTS_TRACKING flag * Move track ad event methods to AdTracker * Remove PaywallEventsManager implementation * Improving naming overlapping naming AdEvent->AdEventRequest * Simplify compiler flag usage * Use Experimental * Remove duplicate with PostPaywallEventsOperation and PostAdEventsOperation * Remove postAdEvents methods assuming reuse of event storage * Make Precision rawValue internal * Remove runtime checks * Trigger build * Move Public Types MARK comment after internal protocol * Add documentation for AdDisplayed, AdOpened, and AdRevenue properties * Remove redundant smoke tests The "DoesNotCrash" tests were completely redundant with the comprehensive "StoresEvent" tests. The comprehensive tests already: 1. Verify methods don't crash (implicitly) 2. Verify events are stored 3. Verify event data is correct Removed 3 redundant tests, keeping only the 3 comprehensive tests. * Fix bug not tracking initialized events --------- Co-authored-by: Claude <noreply@anthropic.com>
124 lines
2.8 KiB
Swift
124 lines
2.8 KiB
Swift
//
|
|
// Copyright RevenueCat Inc. All Rights Reserved.
|
|
//
|
|
// Licensed under the MIT License (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// https://opensource.org/licenses/MIT
|
|
//
|
|
// HTTPRequest.swift
|
|
//
|
|
// Created by Nacho Soto on 2/27/22.
|
|
|
|
import Foundation
|
|
|
|
/// A request to be made by ``HTTPClient``
|
|
struct HTTPRequest {
|
|
|
|
typealias Headers = [String: String]
|
|
|
|
var method: Method
|
|
var path: HTTPRequestPath
|
|
/// If present, this will be used by the server to compute a checksum of the response signed with a private key.
|
|
var nonce: Data?
|
|
/// Whether or not this request should be retried by the HTTPClient for certain status codes.
|
|
var isRetryable: Bool
|
|
|
|
init(
|
|
method: Method,
|
|
path: HTTPRequest.Path,
|
|
nonce: Data? = nil,
|
|
isRetryable: Bool = false
|
|
) {
|
|
self.init(method: method, requestPath: path, nonce: nonce, isRetryable: isRetryable)
|
|
}
|
|
|
|
init(
|
|
method: Method,
|
|
path: HTTPRequest.PaywallPath,
|
|
nonce: Data? = nil,
|
|
isRetryable: Bool = false
|
|
) {
|
|
self.init(method: method, requestPath: path, nonce: nonce, isRetryable: isRetryable)
|
|
}
|
|
|
|
init(
|
|
method: Method,
|
|
path: HTTPRequest.DiagnosticsPath,
|
|
nonce: Data? = nil,
|
|
isRetryable: Bool = false
|
|
) {
|
|
self.init(method: method, requestPath: path, nonce: nonce, isRetryable: isRetryable)
|
|
}
|
|
|
|
init(
|
|
method: Method,
|
|
path: HTTPRequest.WebBillingPath,
|
|
nonce: Data? = nil,
|
|
isRetryable: Bool = false
|
|
) {
|
|
self.init(method: method, requestPath: path, nonce: nonce, isRetryable: isRetryable)
|
|
}
|
|
|
|
init(
|
|
method: Method,
|
|
path: HTTPRequest.AdPath,
|
|
nonce: Data? = nil,
|
|
isRetryable: Bool = false
|
|
) {
|
|
self.init(method: method, requestPath: path, nonce: nonce, isRetryable: isRetryable)
|
|
}
|
|
|
|
internal init(
|
|
method: Method,
|
|
requestPath: HTTPRequestPath,
|
|
nonce: Data? = nil,
|
|
isRetryable: Bool = false
|
|
) {
|
|
assert(nonce == nil || nonce?.count == Data.nonceLength,
|
|
"Invalid nonce: \(nonce?.description ?? "")")
|
|
|
|
self.method = method
|
|
self.path = requestPath
|
|
self.nonce = nonce
|
|
self.isRetryable = isRetryable
|
|
}
|
|
|
|
}
|
|
|
|
// MARK: - Method
|
|
|
|
extension HTTPRequest {
|
|
|
|
enum Method {
|
|
|
|
case get
|
|
case post(HTTPRequestBody)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
extension HTTPRequest {
|
|
|
|
var requestBody: HTTPRequestBody? {
|
|
switch self.method {
|
|
case let .post(body): return body
|
|
case .get: return nil
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
extension HTTPRequest.Method {
|
|
|
|
var httpMethod: String {
|
|
switch self {
|
|
case .get: return "GET"
|
|
case .post: return "POST"
|
|
}
|
|
}
|
|
|
|
}
|