Files
purchases-ios/Sources/Checkpoints/DefaultCheckpointWorkflowResolver+Offering.swift
Álvaro MarcoandÁlvaro Marco 2ebc74ec40 fix: preserve workflow attribution in custom paywall impressions (#8183)
* fix(paywalls): preserve workflow context in custom impressions

* refactor: simplify offering context copies

---------

Co-authored-by: Álvaro Marco <alvaro.marco@revenuecat.com>
2026-11-03 09:27:56 +01:00

66 lines
2.4 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
//
// DefaultCheckpointWorkflowResolver+Offering.swift
//
// Created by Rosie Watson on 10/30/26.
import Foundation
extension DefaultCheckpointWorkflowResolver {
func resolveOfferingStep(
_ rule: CheckpointRule,
step: WorkflowStep,
in workflowData: WorkflowDataResult,
checkpointIdentifier: String,
traceID: String
) async -> (resolution: CheckpointResolution, stepEvents: [WorkflowEvent]) {
guard workflowData.workflow.steps.count == 1 else {
return (Self.unservable(rule, reason: "an offering step cannot be mixed with other steps"), [])
}
// Validated before the step starts, so a step that was never servable emits nothing.
guard let offeringID = step.offeringIdentifier else {
return (Self.unservable(rule, reason: "the offering step has no valid offering identifier"), [])
}
return await Self.trackingStepEvents(for: step, in: workflowData, traceID: traceID) {
guard let offering = await self.offering(identifier: offeringID, for: rule) else {
return .noAction(.configurationUnavailable)
}
return .matchedOffering(
offering.withPresentedCheckpointIdentifier(checkpointIdentifier, traceID: traceID)
.withPresentedWorkflowContext(
workflowId: workflowData.workflow.id,
stepId: step.id,
blobRef: workflowData.workflowBlobRef
),
traceID: traceID
)
}
}
/// Loads the offerings and picks `identifier` out of them, logging why the rule can't be served when it
/// isn't there.
private func offering(
identifier: String,
for rule: CheckpointRule
) async -> Offering? {
guard let offerings = await self.loadOfferings() else { return nil }
guard let offering = offerings.offering(identifier: identifier) else {
Self.unservable(rule, reason: "offering '\(identifier)' is unavailable")
return nil
}
return offering
}
}