Skip to content

Commit 5e27d38

Browse files
authored
initial support for web2web (#122)
* initial support for web2web * fixed bug with consumable purchases * improve transaction finish
1 parent f231c43 commit 5e27d38

7 files changed

+72
-12
lines changed

ApphudSDK.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'ApphudSDK'
3-
s.version = '3.5.3'
3+
s.version = '3.5.5'
44
s.summary = 'Build and Measure In-App Subscriptions on iOS.'
55
s.description = 'Apphud covers every aspect when it comes to In-App Subscriptions from integration to analytics on iOS and Android.'
66
s.homepage = 'https://github.com/apphud/ApphudSDK'

Sources/Internal/ApphudAsyncStoreKit.swift

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,8 @@ internal class ApphudAsyncStoreKit {
113113
break
114114
}
115115

116-
if let tr = transaction {
117-
_ = await ApphudInternal.shared.handleTransaction(tr)
118-
await tr.finish()
116+
if let transaction {
117+
await Self.processTransaction(transaction)
119118
}
120119

121120
self.isPurchasing = false
@@ -132,6 +131,16 @@ internal class ApphudAsyncStoreKit {
132131
}
133132
}
134133

134+
fileprivate static func processTransaction(_ transaction: StoreKit.Transaction) async {
135+
_ = await ApphudInternal.shared.handleTransaction(transaction)
136+
Task {
137+
if (transaction.productType == .consumable) {
138+
try? await Task.sleep(nanoseconds: 3_000_000_000)
139+
}
140+
await transaction.finish()
141+
}
142+
}
143+
135144
#if os(iOS) || os(tvOS) || os(macOS) || os(watchOS)
136145
@MainActor
137146
func purchase(product: Product, apphudProduct: ApphudProduct?, isPurchasing: Binding<Bool>? = nil) async -> ApphudAsyncPurchaseResult {
@@ -181,8 +190,7 @@ final class ApphudAsyncTransactionObserver {
181190
return
182191
}
183192

184-
_ = await ApphudInternal.shared.handleTransaction(transaction)
185-
await transaction.finish()
193+
await ApphudAsyncStoreKit.processTransaction(transaction)
186194
}
187195
} else {
188196
apphudLog("Received transaction [\(transaction.id), \(transaction.productID)] from StoreKit2")

Sources/Internal/ApphudInternal+Attribution.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,4 +217,33 @@ extension ApphudInternal {
217217
return ["token": appleAttibutionToken]
218218
}
219219
}
220+
221+
@MainActor
222+
internal func tryWebAttribution(attributionData: [AnyHashable: Any], completion: @escaping (Bool, ApphudUser?) -> Void) {
223+
let userId = attributionData["aph_user_id"] ?? attributionData["apphud_user_id"]
224+
if let userId = userId as? String, !userId.isEmpty {
225+
226+
if (currentUser?.userId == userId) {
227+
apphudLog("Already web2web user, skipping")
228+
completion(true, currentUser)
229+
return
230+
}
231+
232+
apphudLog("Found a match from web click, updating User ID to \(userId)", forceDisplay: true)
233+
self.updateUser(fields: ["user_id": userId, "from_web2web": true]) { (result, _, data, _, _, _, attempts) in
234+
if result {
235+
Task {
236+
await self.parseUser(data: data)
237+
Task { @MainActor in
238+
completion(true, self.currentUser)
239+
}
240+
}
241+
} else {
242+
completion(false, self.currentUser)
243+
}
244+
}
245+
} else {
246+
completion(false, currentUser)
247+
}
248+
}
220249
}

Sources/Internal/ApphudInternal+Purchase.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,10 @@ extension ApphudInternal {
108108
transactionProductIdentifier: productID,
109109
transactionState: isRecentlyPurchased ? .purchased : nil,
110110
receiptString: receipt,
111-
notifyDelegate: true) { _ in }
111+
notifyDelegate: true) { _ in
112+
continuation.resume(returning: true)
113+
}
112114
}
113-
114-
continuation.resume(returning: true)
115115
}
116116
}
117117
}

Sources/Internal/ApphudInternal+UserUpdate.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ extension ApphudInternal {
172172
}
173173

174174
@MainActor
175-
private func updateUser(fields: [String: Any], delay: Double = 0, callback: @escaping ApphudHTTPResponseCallback) {
175+
internal func updateUser(fields: [String: Any], delay: Double = 0, callback: @escaping ApphudHTTPResponseCallback) {
176176

177177
// Requires @MainActor since it collects data from UIDevice
178178
#if os(macOS)
@@ -338,6 +338,7 @@ extension ApphudInternal {
338338
Task {
339339
let values = await self.preparePropertiesParams(isAudience: force)
340340
guard let params = values.0, let properties = values.1 else {
341+
completion?(false)
341342
return
342343
}
343344

Sources/Internal/ApphudStoreKitWrapper.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,10 @@ internal class ApphudStoreKitWrapper: NSObject, SKPaymentTransactionObserver, SK
298298
internal func finishTransaction(_ transaction: SKPaymentTransaction) {
299299
apphudLog("Finish Transaction: \(transaction.payment.productIdentifier), state: \(transaction.transactionState.rawValue), id: \(transaction.transactionIdentifier ?? "")")
300300
NotificationCenter.default.post(name: _ApphudWillFinishTransactionNotification, object: transaction)
301-
SKPaymentQueue.default().finishTransaction(transaction)
301+
302+
if (transaction.transactionState != .purchasing) {
303+
SKPaymentQueue.default().finishTransaction(transaction)
304+
}
302305
self.purchasingProductID = nil
303306
self.purchasingValue = nil
304307
}

Sources/Public/Apphud.swift

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import Foundation
1414
import UserNotifications
1515
import SwiftUI
1616

17-
internal let apphud_sdk_version = "3.5.3"
17+
internal let apphud_sdk_version = "3.5.5"
1818

1919
// MARK: - Initialization
2020

@@ -788,6 +788,25 @@ final public class Apphud: NSObject {
788788
@objc public static func addAttribution(data: [AnyHashable: Any]?, from provider: ApphudAttributionProvider, identifer: String? = nil, callback: ApphudBoolCallback?) {
789789
ApphudInternal.shared.addAttribution(rawData: data, from: provider, identifer: identifer, callback: callback)
790790
}
791+
792+
/**
793+
Web-to-Web flow only. Attempts to attribute the user with the provided attribution data.
794+
If the `data` parameter contains either `aph_user_id` or `apphud_user_id`, the SDK will submit this information to the Apphud server.
795+
The server will return a premium web user if found; otherwise, the callback will return `false`.
796+
797+
Additionally, the delegate methods `apphudSubscriptionsUpdated` and `apphudDidChangeUserID` will be called.
798+
799+
The callback returns `true` if the user is successfully attributed via the web and includes the updated `ApphudUser` object.
800+
After this callback, you can check the `Apphud.hasPremiumAccess()` method, which should return `true` if the user has premium access.
801+
802+
- Parameters:
803+
- data: A dictionary containing the attribution data.
804+
- callback: A closure that returns a boolean indicating whether the web attribution was successful, and the updated `ApphudUser` object.
805+
*/
806+
@MainActor
807+
public static func attributeFromWeb(data: [AnyHashable: Any], callback: @escaping (Bool, ApphudUser?) -> Void) {
808+
ApphudInternal.shared.tryWebAttribution(attributionData: data, completion: callback)
809+
}
791810

792811
// MARK: - Eligibility Checks
793812

0 commit comments

Comments
 (0)