Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add appSessionID to PaywallEventsManager #4482

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion Sources/Events/StoredEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,18 @@ struct StoredEvent {

private(set) var encodedEvent: AnyEncodable
private(set) var userID: String
private(set) var appSessionID: UUID
private(set) var feature: Feature

init?<T: Encodable>(event: T, userID: String, feature: Feature) {
init?<T: Encodable>(event: T, userID: String, appSessionID: UUID, feature: Feature) {
guard let data = try? JSONEncoder.default.encode(value: event),
let dictionary = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
return nil
}

self.encodedEvent = AnyEncodable(dictionary)
self.userID = userID
self.appSessionID = appSessionID
self.feature = feature
}

Expand All @@ -49,6 +51,7 @@ extension StoredEvent: Codable {

case encodedEvent = "event"
case userID = "userId"
case appSessionID = "appSessionID"
case feature

}
Expand All @@ -64,6 +67,19 @@ extension StoredEvent: Codable {
} else {
self.feature = .paywalls
}
if let appSessionID = try container.decodeIfPresent(UUID.self, forKey: .appSessionID) {
self.appSessionID = appSessionID
} else {
// Backward compatibility for PaywallEvents from before we started storing the user session ID
// Just use the paywall event's session ID
// Or generate a new one in the worst case
if let eventData = encodedEvent.value as? [String: Any],
let paywallEvent: PaywallEvent = try JSONDecoder.default.decode(dictionary: eventData) {
self.appSessionID = paywallEvent.data.sessionIdentifier
} else {
self.appSessionID = UUID()
}
}
}

}
Expand Down
9 changes: 9 additions & 0 deletions Sources/Paywalls/Events/PaywallEventsManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ protocol PaywallEventsManagerType {
@available(iOS 15.0, tvOS 15.0, macOS 12.0, watchOS 8.0, *)
func flushEvents(count: Int) async throws -> Int

func resetAppSessionID() async

}

@available(iOS 15.0, tvOS 15.0, macOS 12.0, watchOS 8.0, *)
Expand All @@ -31,6 +33,7 @@ actor PaywallEventsManager: PaywallEventsManagerType {
private let internalAPI: InternalAPI
private let userProvider: CurrentUserProvider
private let store: PaywallEventStoreType
private var appSessionID: UUID

private var flushInProgress = false

Expand All @@ -42,11 +45,17 @@ actor PaywallEventsManager: PaywallEventsManagerType {
self.internalAPI = internalAPI
self.userProvider = userProvider
self.store = store
self.appSessionID = UUID()
}

func resetAppSessionID() {
self.appSessionID = UUID()
}

func track(paywallEvent: PaywallEvent) async {
guard let event: StoredEvent = .init(event: AnyEncodable(paywallEvent),
userID: self.userProvider.currentAppUserID,
appSessionID: self.appSessionID,
feature: .paywalls) else {
Logger.error(Strings.paywalls.event_cannot_serialize)
return
Expand Down
8 changes: 8 additions & 0 deletions Sources/Purchasing/Purchases/Purchases.swift
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,10 @@ public extension Purchases {
self.systemInfo.isApplicationBackgrounded { isAppBackgrounded in
self.updateOfferingsCache(isAppBackgrounded: isAppBackgrounded)
}

Task {
await self.paywallEventsManager?.resetAppSessionID()
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am unsure about this, but paywallEventsManager is an actor

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, since paywallEventsManager is an actor, this call has to be async. Ideally, the login function itself would be async and we wouldn't have to spawn a child task here, but that's a whole other refactor 🤷‍♂️

I think the biggest risk is that login() completes before resetAppSessionID() is done, and then some event gets emitted without the new app session ID. I don't think it's likely, but if we want to be safe here, we could consider calling the completion handler in the Task after resetAppSessionID completes.

}
}

Expand Down Expand Up @@ -853,6 +857,10 @@ public extension Purchases {
return
}

Task {
await self.paywallEventsManager?.resetAppSessionID()
}

self.updateAllCaches {
completion?($0.value, $0.error)
}
Expand Down