Skip to content

Commit db73c8a

Browse files
committed
Merge remote-tracking branch 'origin/feature/enrichment' into web2app-and-enrichment
2 parents cb82eef + 2dabd6d commit db73c8a

File tree

21 files changed

+330
-174
lines changed

21 files changed

+330
-174
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ The changelog for `SuperwallKit`. Also see the [releases](https://github.com/sup
5555

5656
### Enhancements
5757

58+
- Adds `demandScore` and `demandTier` to device attributes. A user is assigned these based on a variety of factors to determine whether they're more or less likely to pay higher prices and can be used within audience filters.
5859
- Updates Superscript to 0.2.4.
5960

6061
## 4.0.6

Sources/SuperwallKit/Analytics/Internal Tracking/Trackable Events/TrackableSuperwallEvent.swift

Lines changed: 58 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ enum InternalSuperwallEvent {
4040
func getSuperwallParameters() async -> [String: Any] {
4141
return [
4242
"application_installed_at": appInstalledAtString,
43-
"using_purchase_controller": hasExternalPurchaseController
43+
"using_purchase_controller": hasExternalPurchaseController,
4444
]
4545
}
4646
}
@@ -70,7 +70,7 @@ enum InternalSuperwallEvent {
7070
let output = paywallInfo.audienceFilterParams()
7171
return output + [
7272
"survey_selected_option_title": selectedOption.title,
73-
"survey_custom_response": customResponse as Any
73+
"survey_custom_response": customResponse as Any,
7474
]
7575
}
7676
let survey: Survey
@@ -82,7 +82,7 @@ enum InternalSuperwallEvent {
8282
let params: [String: Any] = [
8383
"survey_id": survey.id,
8484
"survey_assignment_key": survey.assignmentKey,
85-
"survey_selected_option_id": selectedOption.id
85+
"survey_selected_option_id": selectedOption.id,
8686
]
8787

8888
return await paywallInfo.placementParams(otherParams: params)
@@ -128,7 +128,7 @@ enum InternalSuperwallEvent {
128128
"lastPathComponent": url.lastPathComponent,
129129
"host": url.host ?? "",
130130
"query": url.query ?? "",
131-
"fragment": url.fragment ?? ""
131+
"fragment": url.fragment ?? "",
132132
]
133133
}
134134

@@ -207,7 +207,7 @@ enum InternalSuperwallEvent {
207207
var params = options.toDictionary()
208208
params += [
209209
"using_purchase_controller": hasExternalPurchaseController,
210-
"has_delegate": hasDelegate
210+
"has_delegate": hasDelegate,
211211
]
212212
return params
213213
}
@@ -323,14 +323,14 @@ enum InternalSuperwallEvent {
323323
return params + [
324324
"variant_id": experiment.variant.id as Any,
325325
"experiment_id": experiment.id as Any,
326-
"result": "holdout"
326+
"result": "holdout",
327327
]
328328
case let .paywall(experiment):
329329
return params + [
330330
"variant_id": experiment.variant.id as Any,
331331
"experiment_id": experiment.id as Any,
332332
"paywall_identifier": experiment.variant.paywallId as Any,
333-
"result": "present"
333+
"result": "present",
334334
]
335335
case .placementNotFound:
336336
return params + [
@@ -364,19 +364,21 @@ enum InternalSuperwallEvent {
364364
"source_event_name": placementData?.name ?? "",
365365
"pipeline_type": type.description,
366366
"status": status.rawValue,
367-
"status_reason": statusReason?.description ?? ""
367+
"status_reason": statusReason?.description ?? "",
368368
]
369369

370370
if let featureFlags = factory.makeFeatureFlags(),
371-
featureFlags.enableExpressionParameters {
371+
featureFlags.enableExpressionParameters
372+
{
372373
let computedPropertyRequests = factory.makeComputedPropertyRequests()
373374
let audienceFilters = await factory.makeAudienceFilterAttributes(
374375
forPlacement: placementData,
375376
withComputedProperties: computedPropertyRequests
376377
)
377378

378379
if let jsonData = try? JSONSerialization.data(withJSONObject: audienceFilters),
379-
let decoded = String(data: jsonData, encoding: .utf8) {
380+
let decoded = String(data: jsonData, encoding: .utf8)
381+
{
380382
params += [
381383
"expression_params": decoded
382384
]
@@ -582,7 +584,7 @@ enum InternalSuperwallEvent {
582584
var placementParams: [String: Any] = [
583585
"store": "APP_STORE",
584586
"source": source.rawValue,
585-
"storekit_version": storeKitVersion.description
587+
"storekit_version": storeKitVersion.description,
586588
]
587589

588590
switch state {
@@ -597,9 +599,11 @@ enum InternalSuperwallEvent {
597599
placementParams += [
598600
"storefront_countryCode": storefrontCountryCode,
599601
"storefront_id": storefrontId,
600-
"transaction_type": type.description
602+
"transaction_type": type.description,
601603
]
602-
let appleSearchAttributes = Superwall.shared.userAttributes.filter { $0.key.hasPrefix("apple_search_ads_") }
604+
let appleSearchAttributes = Superwall.shared.userAttributes.filter {
605+
$0.key.hasPrefix("apple_search_ads_")
606+
}
603607
placementParams += appleSearchAttributes
604608
fallthrough
605609
case .start,
@@ -801,7 +805,7 @@ enum InternalSuperwallEvent {
801805
"config_build_id": buildId,
802806
"retry_count": retryCount,
803807
"cache_status": cacheStatus.rawValue,
804-
"fetch_duration": fetchDuration
808+
"fetch_duration": fetchDuration,
805809
]
806810
}
807811
}
@@ -905,4 +909,44 @@ enum InternalSuperwallEvent {
905909
let audienceFilterParams: [String: Any] = [:]
906910
func getSuperwallParameters() async -> [String: Any] { return [:] }
907911
}
912+
913+
struct EnrichmentLoad: TrackableSuperwallEvent {
914+
enum State {
915+
case start
916+
case complete(Enrichment)
917+
case fail
918+
}
919+
let state: State
920+
921+
var superwallEvent: SuperwallEvent {
922+
switch state {
923+
case .start:
924+
return .enrichmentStart
925+
case .complete(let enrichment):
926+
return .enrichmentComplete(
927+
userEnrichment: enrichment.user.dictionaryObject,
928+
deviceEnrichment: enrichment.device.dictionaryObject
929+
)
930+
case .fail:
931+
return .enrichmentFail
932+
}
933+
}
934+
let audienceFilterParams: [String: Any] = [:]
935+
936+
func getSuperwallParameters() async -> [String: Any] {
937+
var output: [String: Any] = [:]
938+
switch state {
939+
case .complete(let enrichment):
940+
for (key, value) in enrichment.user {
941+
output["user_\(key)"] = value
942+
}
943+
for (key, value) in enrichment.device {
944+
output["device_\(key)"] = value
945+
}
946+
return output
947+
default:
948+
return [:]
949+
}
950+
}
951+
}
908952
}

Sources/SuperwallKit/Analytics/Superwall Placement/SuperwallEvent.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,15 @@ public enum SuperwallEvent {
216216
/// When the redemption of a code fails.
217217
case redemptionFail
218218

219+
/// When the enrichment request starts.
220+
case enrichmentStart
221+
222+
/// When the enrichment request completes.
223+
case enrichmentComplete(userEnrichment: [String: Any]?, deviceEnrichment: [String: Any]?)
224+
225+
/// When the enrichment request fails.
226+
case enrichmentFail
227+
219228
var canImplicitlyTriggerPaywall: Bool {
220229
switch self {
221230
case .appInstall,
@@ -373,6 +382,12 @@ extension SuperwallEvent {
373382
return .init(objcEvent: .redemptionComplete)
374383
case .redemptionFail:
375384
return .init(objcEvent: .redemptionFail)
385+
case .enrichmentFail:
386+
return .init(objcEvent: .enrichmentFail)
387+
case .enrichmentStart:
388+
return .init(objcEvent: .enrichmentStart)
389+
case .enrichmentComplete:
390+
return .init(objcEvent: .enrichmentComplete)
376391
}
377392
}
378393
}

Sources/SuperwallKit/Analytics/Superwall Placement/SuperwallEventObjc.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,15 @@ public enum SuperwallEventObjc: Int, CaseIterable {
196196
/// When the redemption of a code fails.
197197
case redemptionFail
198198

199+
/// When the enrichment request starts.
200+
case enrichmentStart
201+
202+
/// When the enrichment request completes.
203+
case enrichmentComplete
204+
205+
/// When the enrichment request fails.
206+
case enrichmentFail
207+
199208
public init(event: SuperwallEvent) {
200209
self = event.backingData.objcEvent
201210
}
@@ -318,6 +327,13 @@ public enum SuperwallEventObjc: Int, CaseIterable {
318327
return "redemption_complete"
319328
case .redemptionFail:
320329
return "redemption_fail"
330+
case .enrichmentStart:
331+
return "enrichment_start"
332+
case .enrichmentFail:
333+
return "enrichment_fail"
334+
case .enrichmentComplete:
335+
return "enrichment_complete"
336+
321337
}
322338
}
323339
}

Sources/SuperwallKit/Config/ConfigManager.swift

Lines changed: 14 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ class ConfigManager {
9393
}
9494

9595
do {
96+
Task {
97+
try? await deviceHelper.getEnrichment()
98+
}
9699
let startAt = Date()
97100
let newConfig = try await network.getConfig { [weak self] attempt in
98101
self?.configRetryCount = attempt
@@ -146,10 +149,7 @@ class ConfigManager {
146149
if let cachedConfig = cachedConfig,
147150
enableConfigRefresh {
148151
do {
149-
let result = try await self.fetchWithTimeout({
150-
try await self.network.getConfig(maxRetry: 0)
151-
},
152-
timeout: timeout)
152+
let result = try await self.network.getConfig(maxRetry: 0, timeout: timeout)
153153
return (result, false)
154154
} catch {
155155
// Return the cached config and set isUsingCached to true
@@ -164,34 +164,31 @@ class ConfigManager {
164164
}
165165
}()
166166

167-
async let isUsingCachedGeo: Bool = { [weak self] in
167+
async let isUsingCachedEnrichment: Bool = { [weak self] in
168168
guard let self = self else {
169169
return false
170170
}
171-
let cachedGeoInfo = self.storage.get(LatestGeoInfo.self)
171+
let cachedEnrichment = self.storage.get(LatestEnrichment.self)
172172

173-
if let cachedGeoInfo = cachedGeoInfo,
173+
if let cachedEnrichment = cachedEnrichment,
174174
enableConfigRefresh {
175175
do {
176-
let geoInfo = try await self.fetchWithTimeout({
177-
try await self.network.getGeoInfo(maxRetry: 0)
178-
},
179-
timeout: timeout)
180-
self.deviceHelper.geoInfo = geoInfo
176+
let enrichment = try await self.deviceHelper.getEnrichment(maxRetry: 0, timeout: timeout)
177+
self.deviceHelper.enrichment = enrichment
181178
return false
182179
} catch {
183-
self.deviceHelper.geoInfo = cachedGeoInfo
180+
self.deviceHelper.enrichment = cachedEnrichment
184181
return true
185182
}
186183
} else {
187-
await self.deviceHelper.getGeoInfo()
184+
_ = try? await self.deviceHelper.getEnrichment()
188185
return false
189186
}
190187
}()
191188

192189
let (config, isUsingCachedConfig) = try await configResult
193190
let configFetchDuration = Date().timeIntervalSince(startAt)
194-
let isUsingCachedGeoInfo = await isUsingCachedGeo
191+
let usingCachedEnrichment = await isUsingCachedEnrichment
195192

196193
let cacheStatus: InternalSuperwallEvent.ConfigCacheStatus =
197194
isUsingCachedConfig ? .cached : .notCached
@@ -217,9 +214,9 @@ class ConfigManager {
217214
Task {
218215
await preloadPaywalls()
219216
}
220-
if isUsingCachedGeoInfo {
217+
if usingCachedEnrichment {
221218
Task {
222-
await deviceHelper.getGeoInfo()
219+
try? await deviceHelper.getEnrichment()
223220
}
224221
}
225222
if isUsingCachedConfig {
@@ -245,35 +242,6 @@ class ConfigManager {
245242
}
246243
}
247244

248-
private func fetchWithTimeout<T>(
249-
_ task: @escaping () async throws -> T,
250-
timeout: TimeInterval
251-
) async throws -> T {
252-
try await withThrowingTaskGroup(of: T.self) { group in
253-
group.addTask {
254-
try await task()
255-
}
256-
257-
group.addTask {
258-
try await Task.sleep(nanoseconds: UInt64(timeout * 1_000_000_000))
259-
throw CancellationError()
260-
}
261-
262-
do {
263-
let result = try await group.next()
264-
group.cancelAll()
265-
if let result = result {
266-
return result
267-
} else {
268-
throw CancellationError()
269-
}
270-
} catch {
271-
group.cancelAll()
272-
throw error
273-
}
274-
}
275-
}
276-
277245
private func processConfig(
278246
_ config: Config,
279247
isFirstTime: Bool

Sources/SuperwallKit/Config/Options/SuperwallOptions.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,8 @@ public final class SuperwallOptions: NSObject, Encodable {
126126
}
127127
}
128128

129-
var geoHost: String {
130-
"geo-api.superwall.com"
129+
var enrichmentHost: String {
130+
"enrichment-api.superwall.com"
131131
}
132132

133133
var adServicesHost: String {

Sources/SuperwallKit/Dependencies/DependencyContainer.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,14 +170,18 @@ final class DependencyContainer {
170170
}
171171
}
172172

173-
// MARK: - IdentityInfoFactory
174-
extension DependencyContainer: IdentityInfoFactory {
173+
// MARK: - IdentityFactory
174+
extension DependencyContainer: IdentityFactory {
175175
func makeIdentityInfo() -> IdentityInfo {
176176
return IdentityInfo(
177177
aliasId: identityManager.aliasId,
178178
appUserId: identityManager.appUserId
179179
)
180180
}
181+
182+
func makeIdentityManager() -> IdentityManager {
183+
return identityManager
184+
}
181185
}
182186

183187
extension DependencyContainer: TransactionManagerFactory {

Sources/SuperwallKit/Dependencies/FactoryProtocols.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,9 @@ protocol ConfigManagerFactory: AnyObject {
8080
) -> Paywall?
8181
}
8282

83-
protocol IdentityInfoFactory: AnyObject {
83+
protocol IdentityFactory: AnyObject {
8484
func makeIdentityInfo() async -> IdentityInfo
85+
func makeIdentityManager() -> IdentityManager
8586
}
8687

8788
protocol TransactionManagerFactory: AnyObject {

0 commit comments

Comments
 (0)