Skip to content

Commit

Permalink
Paywalls: created PaywallEventsManager (#3159)
Browse files Browse the repository at this point in the history
This combines the types created in prior PRs (#3157, #3158) to store and
post events.
  • Loading branch information
NachoSoto committed Sep 14, 2023
1 parent 384866b commit 7b6f3e2
Show file tree
Hide file tree
Showing 5 changed files with 376 additions and 0 deletions.
16 changes: 16 additions & 0 deletions RevenueCat.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,10 @@
4FFCED882AA941D200118EF4 /* PaywallEventsRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FFCED852AA941D200118EF4 /* PaywallEventsRequest.swift */; };
4FFCED892AA941D200118EF4 /* PaywallHTTPRequestPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FFCED862AA941D200118EF4 /* PaywallHTTPRequestPath.swift */; };
4FFCED8A2AA941D200118EF4 /* PostPaywallEventsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FFCED872AA941D200118EF4 /* PostPaywallEventsOperation.swift */; };
4FFFE6C42AA9464100B2955C /* PaywallEventsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FFFE6C32AA9464100B2955C /* PaywallEventsManager.swift */; };
4FFFE6C62AA9465000B2955C /* MockPaywallEventsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FFFE6C52AA9465000B2955C /* MockPaywallEventsManager.swift */; };
4FFFE6C82AA9467800B2955C /* PaywallEventsManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FFFE6C72AA9467800B2955C /* PaywallEventsManagerTests.swift */; };
4FFFE6CA2AA946A700B2955C /* MockInternalAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FFFE6C92AA946A700B2955C /* MockInternalAPI.swift */; };
57032ABF28C13CE4004FF47A /* StoreKit2SettingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57032ABE28C13CE4004FF47A /* StoreKit2SettingTests.swift */; };
57045B3829C514A8001A5417 /* ProductEntitlementMappingDecodingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57045B3729C514A8001A5417 /* ProductEntitlementMappingDecodingTests.swift */; };
57045B3A29C51751001A5417 /* GetProductEntitlementMappingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57045B3929C51751001A5417 /* GetProductEntitlementMappingOperation.swift */; };
Expand Down Expand Up @@ -1052,6 +1056,10 @@
4FFCED862AA941D200118EF4 /* PaywallHTTPRequestPath.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaywallHTTPRequestPath.swift; sourceTree = "<group>"; };
4FFCED872AA941D200118EF4 /* PostPaywallEventsOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostPaywallEventsOperation.swift; sourceTree = "<group>"; };
4FFD88BE2A4B56E2008E98AC /* __Snapshots__ */ = {isa = PBXFileReference; lastKnownFileType = folder; path = __Snapshots__; sourceTree = "<group>"; };
4FFFE6C32AA9464100B2955C /* PaywallEventsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaywallEventsManager.swift; sourceTree = "<group>"; };
4FFFE6C52AA9465000B2955C /* MockPaywallEventsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockPaywallEventsManager.swift; sourceTree = "<group>"; };
4FFFE6C72AA9467800B2955C /* PaywallEventsManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaywallEventsManagerTests.swift; sourceTree = "<group>"; };
4FFFE6C92AA946A700B2955C /* MockInternalAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockInternalAPI.swift; sourceTree = "<group>"; };
57032ABE28C13CE4004FF47A /* StoreKit2SettingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreKit2SettingTests.swift; sourceTree = "<group>"; };
57045B3729C514A8001A5417 /* ProductEntitlementMappingDecodingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductEntitlementMappingDecodingTests.swift; sourceTree = "<group>"; };
57045B3929C51751001A5417 /* GetProductEntitlementMappingOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetProductEntitlementMappingOperation.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1834,6 +1842,7 @@
351B513E26D4496000BD2BD7 /* MockIdentityManager.swift */,
37E355744D64075AA91342DE /* MockInAppPurchaseBuilder.swift */,
351B517126D44EF300BD2BD7 /* MockInMemoryCachedOfferings.swift */,
4FFFE6C92AA946A700B2955C /* MockInternalAPI.swift */,
351B515B26D44B7900BD2BD7 /* MockIntroEligibilityCalculator.swift */,
35E840CD2710E2EB00899AE2 /* MockManageSubscriptionsHelper.swift */,
351B515526D44B2300BD2BD7 /* MockNotificationCenter.swift */,
Expand All @@ -1846,6 +1855,7 @@
4F9BB63E2A7AFB72001C120D /* MockPayment.swift */,
351B517326D44F4B00BD2BD7 /* MockPaymentDiscount.swift */,
5733D00828CFA7A4008638D8 /* MockPaymentQueueWrapper.swift */,
4FFFE6C52AA9465000B2955C /* MockPaywallEventsManager.swift */,
37E35C9439E087F63ECC4F59 /* MockProductsManager.swift */,
37E35B08709090FBBFB16EBD /* MockProductsRequest.swift */,
37E35F783903362B65FB7AF3 /* MockProductsRequestFactory.swift */,
Expand Down Expand Up @@ -2327,6 +2337,7 @@
children = (
4FFCED842AA941D200118EF4 /* Networking */,
4FD3688A2AA7C12600F63354 /* PaywallEvent.swift */,
4FFFE6C32AA9464100B2955C /* PaywallEventsManager.swift */,
4FD368B32AA7CFED00F63354 /* PaywallEventStore.swift */,
4FD368B52AA7D09C00F63354 /* PaywallEventSerializer.swift */,
4FE6FEE42AA940B700780B45 /* PaywallStoredEvent.swift */,
Expand All @@ -2338,6 +2349,7 @@
isa = PBXGroup;
children = (
4FFCED812AA941B200118EF4 /* PaywallEventsBackendTests.swift */,
4FFFE6C72AA9467800B2955C /* PaywallEventsManagerTests.swift */,
4FFCED802AA941B200118EF4 /* PaywallEventsRequestTests.swift */,
4FE6FEE82AA940E300780B45 /* PaywallEventSerializerTests.swift */,
4FE6FEE72AA940E300780B45 /* PaywallEventStoreTests.swift */,
Expand Down Expand Up @@ -3458,6 +3470,7 @@
2D9C7BB326D838FC006838BE /* UIApplication+RCExtensions.swift in Sources */,
F56E2E7727622B5E009FED5B /* TransactionsManager.swift in Sources */,
B34605CC279A6E380031CA74 /* LogInOperation.swift in Sources */,
4FFFE6C42AA9464100B2955C /* PaywallEventsManager.swift in Sources */,
4F8929192A65EF3000A91EA2 /* EnsureNonEmptyCollectionDecodable.swift in Sources */,
35F82BB626A9B8040051DF03 /* AttributionDataMigrator.swift in Sources */,
4F5F47082AA91F8A005649D8 /* HealthOperation.swift in Sources */,
Expand Down Expand Up @@ -3610,6 +3623,7 @@
4FBBD4E42A620539001CBA21 /* PaywallColorTests.swift in Sources */,
5733B1AA27FFBCF900EC2045 /* BaseErrorTests.swift in Sources */,
578FB10E27ADDA8000F70709 /* AvailabilityChecks.swift in Sources */,
4FFFE6CA2AA946A700B2955C /* MockInternalAPI.swift in Sources */,
57E415EF284697A300EA5460 /* PurchasesDeferredPurchasesTests.swift in Sources */,
35F82BB226A98EC50051DF03 /* AttributionDataMigratorTests.swift in Sources */,
5759B41E296DFD4C002472D5 /* MockFileReader.swift in Sources */,
Expand Down Expand Up @@ -3701,10 +3715,12 @@
5796A3C027D7D64500653165 /* ResultExtensionsTests.swift in Sources */,
351B51A726D450D400BD2BD7 /* SystemInfoTests.swift in Sources */,
5733B1A827FFBCC800EC2045 /* BackendErrorTests.swift in Sources */,
4FFFE6C82AA9467800B2955C /* PaywallEventsManagerTests.swift in Sources */,
351B515626D44B2300BD2BD7 /* MockNotificationCenter.swift in Sources */,
351B515226D44AF000BD2BD7 /* MockReceiptFetcher.swift in Sources */,
351B51C226D450E800BD2BD7 /* ProductRequestDataTests.swift in Sources */,
4F54DF422A1D8D0700FD72BF /* MockTransactionPoster.swift in Sources */,
4FFFE6C62AA9465000B2955C /* MockPaywallEventsManager.swift in Sources */,
351B51BE26D450E800BD2BD7 /* CustomerInfoTests.swift in Sources */,
35272E1B26D0029300F22C3B /* DeviceCacheSubscriberAttributesTests.swift in Sources */,
5796A39627D6BDAB00653165 /* BackendPostOfferForSigningTests.swift in Sources */,
Expand Down
31 changes: 31 additions & 0 deletions Sources/Logging/Strings/PaywallsStrings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ enum PaywallsStrings {
case warming_up_images(imageURLs: Set<URL>)
case error_prefetching_image(URL, Error)

// MARK: - Events

case event_manager_not_initialized_not_available
case event_manager_failed_to_initialize(Error)

case event_flush_already_in_progress
case event_flush_with_empty_store
case event_flush_starting(count: Int)
case event_flush_failed(BackendError)

}

extension PaywallsStrings: LogMessage {
Expand All @@ -35,6 +45,27 @@ extension PaywallsStrings: LogMessage {

case let .error_prefetching_image(url, error):
return "Error pre-fetching paywall image '\(url)': \((error as NSError).description)"

// MARK: - Events

case .event_manager_not_initialized_not_available:
return "Won't initialize PaywallEventsManager: not available on current device."

case let .event_manager_failed_to_initialize(error):
return "PaywallEventsManager won't be initialized, event store failed to create " +
"with error: \((error as NSError).localizedDescription)"

case .event_flush_already_in_progress:
return "Paywall event flushing already in progress. Skipping."

case .event_flush_with_empty_store:
return "Paywall event flushing requested with empty store."

case let .event_flush_starting(count):
return "Paywall event flush: posting \(count) events."

case let .event_flush_failed(error):
return "Paywall event flushing failed, will retry. Error: \(error.localizedDescription)"
}
}

Expand Down
79 changes: 79 additions & 0 deletions Sources/Paywalls/Events/PaywallEventsManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//
// 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
//
// PaywallEventsManager.swift
//
// Created by Nacho Soto on 9/6/23.

import Foundation

protocol PaywallEventsManagerType {

@available(iOS 15.0, tvOS 15.0, macOS 12.0, watchOS 8.0, *)
func track(paywallEvent: PaywallEvent) async

@available(iOS 15.0, tvOS 15.0, macOS 12.0, watchOS 8.0, *)
func flushEvents(count: Int) async

}

@available(iOS 15.0, tvOS 15.0, macOS 12.0, watchOS 8.0, *)
actor PaywallEventsManager: PaywallEventsManagerType {

private let internalAPI: InternalAPI
private let userProvider: CurrentUserProvider
private let store: PaywallEventStoreType

private var flushInProgress = false

init(
internalAPI: InternalAPI,
userProvider: CurrentUserProvider,
store: PaywallEventStoreType
) {
self.internalAPI = internalAPI
self.userProvider = userProvider
self.store = store
}

func track(paywallEvent: PaywallEvent) async {
await self.store.store(.init(event: paywallEvent, userID: self.userProvider.currentAppUserID))
}

func flushEvents(count: Int) async {
guard !self.flushInProgress else {
Logger.debug(Strings.paywalls.event_flush_already_in_progress)
return
}
self.flushInProgress = true
defer { self.flushInProgress = false }

let events = await self.store.fetch(count)

guard !events.isEmpty else {
Logger.verbose(Strings.paywalls.event_flush_with_empty_store)
return
}

Logger.verbose(Strings.paywalls.event_flush_starting(count: events.count))

let error = await self.internalAPI.postPaywallEvents(events: events)

if let error {
Logger.error(Strings.paywalls.event_flush_failed(error))

if error.successfullySynced {
await self.store.clear(count)
}
} else {
await self.store.clear(count)
}
}

}
34 changes: 34 additions & 0 deletions Tests/UnitTests/Mocks/MockPaywallEventsManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// 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
//
// MockPaywallEventsManager.swift
//
// Created by Nacho Soto on 9/6/23.

import Foundation
@testable import RevenueCat

@available(iOS 15.0, tvOS 15.0, macOS 12.0, watchOS 8.0, *)
actor MockPaywallEventsManager: PaywallEventsManagerType {

var trackedEvents: [PaywallEvent] = []

func track(paywallEvent: PaywallEvent) async {
self.trackedEvents.append(paywallEvent)
}

var invokedFlushEvents = false
var invokedFlushEventsCount = 0

func flushEvents(count: Int) async {
self.invokedFlushEvents = true
self.invokedFlushEventsCount += 1
}

}
Loading

0 comments on commit 7b6f3e2

Please sign in to comment.