Skip to content

Commit 39056be

Browse files
committed
Added API logEventForTransaction for StoreKit2 Support
1 parent 020fdc6 commit 39056be

File tree

6 files changed

+142
-3
lines changed

6 files changed

+142
-3
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Mac
2+
.build
23
.DS_Store
34
.LSOverride
45

BranchSDK.xcodeproj/project.pbxproj

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,9 @@
488488
E52E5B0A2CC79E5C00F553EE /* BranchFileLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = E52E5B092CC79E5C00F553EE /* BranchFileLogger.m */; };
489489
E52E5B0B2CC79E5C00F553EE /* BranchFileLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = E52E5B092CC79E5C00F553EE /* BranchFileLogger.m */; };
490490
E563942E2CC7A8E600E18E65 /* BranchFileLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = E52E5B052CC79E4E00F553EE /* BranchFileLogger.h */; };
491+
E710E5E72EB48AB90051AE51 /* BranchEvent+StoreKit2.swift in Sources */ = {isa = PBXBuildFile; fileRef = E710E5E52EB48AB90051AE51 /* BranchEvent+StoreKit2.swift */; };
492+
E710E5E82EB48AB90051AE51 /* BranchEvent+StoreKit2.swift in Sources */ = {isa = PBXBuildFile; fileRef = E710E5E52EB48AB90051AE51 /* BranchEvent+StoreKit2.swift */; };
493+
E710E5E92EB48AB90051AE51 /* BranchEvent+StoreKit2.swift in Sources */ = {isa = PBXBuildFile; fileRef = E710E5E52EB48AB90051AE51 /* BranchEvent+StoreKit2.swift */; };
491494
E71E396F2DD3A92900110F59 /* BNCInAppBrowser.h in Headers */ = {isa = PBXBuildFile; fileRef = E71E396D2DD3A92900110F59 /* BNCInAppBrowser.h */; };
492495
E71E39712DD3A92900110F59 /* BNCInAppBrowser.m in Sources */ = {isa = PBXBuildFile; fileRef = E71E396E2DD3A92900110F59 /* BNCInAppBrowser.m */; };
493496
E71E39722DD3A92900110F59 /* BNCInAppBrowser.h in Headers */ = {isa = PBXBuildFile; fileRef = E71E396D2DD3A92900110F59 /* BNCInAppBrowser.h */; };
@@ -713,6 +716,7 @@
713716
5FF2AFDF28E7C22100393216 /* BranchSDK.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchSDK.h; sourceTree = "<group>"; };
714717
E52E5B052CC79E4E00F553EE /* BranchFileLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchFileLogger.h; sourceTree = "<group>"; };
715718
E52E5B092CC79E5C00F553EE /* BranchFileLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchFileLogger.m; sourceTree = "<group>"; };
719+
E710E5E52EB48AB90051AE51 /* BranchEvent+StoreKit2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BranchEvent+StoreKit2.swift"; sourceTree = "<group>"; };
716720
E71E396D2DD3A92900110F59 /* BNCInAppBrowser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BNCInAppBrowser.h; sourceTree = "<group>"; };
717721
E71E396E2DD3A92900110F59 /* BNCInAppBrowser.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BNCInAppBrowser.m; sourceTree = "<group>"; };
718722
E73D027F2DEE8AE90076C3F1 /* BranchConfigurationController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BranchConfigurationController.h; sourceTree = "<group>"; };
@@ -773,6 +777,7 @@
773777
isa = PBXGroup;
774778
children = (
775779
5FCDD36B2B7AC6A100EAF29F /* BranchSDK */,
780+
E710E5E62EB48AB90051AE51 /* BranchSDK_Swift */,
776781
5F2211702894A9C000C5B190 /* TestHost */,
777782
5F2211872894A9C100C5B190 /* TestHostTests */,
778783
5F2211912894A9C100C5B190 /* TestHostUITests */,
@@ -1022,6 +1027,15 @@
10221027
path = Framework;
10231028
sourceTree = "<group>";
10241029
};
1030+
E710E5E62EB48AB90051AE51 /* BranchSDK_Swift */ = {
1031+
isa = PBXGroup;
1032+
children = (
1033+
E710E5E52EB48AB90051AE51 /* BranchEvent+StoreKit2.swift */,
1034+
);
1035+
name = BranchSDK_Swift;
1036+
path = Sources/BranchSDK_Swift;
1037+
sourceTree = "<group>";
1038+
};
10251039
/* End PBXGroup section */
10261040

10271041
/* Begin PBXHeadersBuildPhase section */
@@ -1660,6 +1674,7 @@
16601674
5FCDD59A2B7AC6A400EAF29F /* BNCServerResponse.m in Sources */,
16611675
5FCDD5852B7AC6A400EAF29F /* BNCInitSessionResponse.m in Sources */,
16621676
5FCDD4412B7AC6A100EAF29F /* BNCPreferenceHelper.m in Sources */,
1677+
E710E5E92EB48AB90051AE51 /* BranchEvent+StoreKit2.swift in Sources */,
16631678
5FCDD5792B7AC6A400EAF29F /* BranchContentPathProperties.m in Sources */,
16641679
);
16651680
runOnlyForDeploymentPostprocessing = 0;
@@ -1767,6 +1782,7 @@
17671782
5FCDD59B2B7AC6A400EAF29F /* BNCServerResponse.m in Sources */,
17681783
5FCDD5862B7AC6A400EAF29F /* BNCInitSessionResponse.m in Sources */,
17691784
5FCDD4422B7AC6A100EAF29F /* BNCPreferenceHelper.m in Sources */,
1785+
E710E5E82EB48AB90051AE51 /* BranchEvent+StoreKit2.swift in Sources */,
17701786
5FCDD57A2B7AC6A400EAF29F /* BranchContentPathProperties.m in Sources */,
17711787
);
17721788
runOnlyForDeploymentPostprocessing = 0;
@@ -1786,6 +1802,7 @@
17861802
5FCDD4732B7AC6A100EAF29F /* BranchLATDRequest.m in Sources */,
17871803
5FCDD57E2B7AC6A400EAF29F /* BNCPasteboard.m in Sources */,
17881804
5F5FDA182B7DE2FE00F14A43 /* BranchLogger.m in Sources */,
1805+
E710E5E72EB48AB90051AE51 /* BranchEvent+StoreKit2.swift in Sources */,
17891806
5FCDD4612B7AC6A100EAF29F /* BNCRequestFactory.m in Sources */,
17901807
5FCDD41F2B7AC6A100EAF29F /* BNCDeepLinkViewControllerInstance.m in Sources */,
17911808
5FCDD58A2B7AC6A400EAF29F /* BNCLinkCache.m in Sources */,

Package.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ let package = Package(
1111
products: [
1212
.library(
1313
name: "BranchSDK",
14-
targets: ["BranchSDK"]),
14+
targets: ["BranchSDK", "BranchSwiftSDK"]),
1515
],
1616
dependencies: [
1717
],
@@ -37,5 +37,10 @@ let package = Package(
3737
.linkedFramework("AdServices", .when(platforms: [.iOS]))
3838
]
3939
),
40+
.target(
41+
name: "BranchSwiftSDK",
42+
dependencies: ["BranchSDK"],
43+
path: "Sources/BranchSDK_Swift"
44+
)
4045
]
4146
)

Sources/BranchSDK/BranchEvent.m

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,6 @@ + (BOOL)supportsSecureCoding {
157157
#pragma mark - BranchEvent
158158

159159
@interface BranchEvent ()<SKRequestDelegate, SKProductsRequestDelegate>
160-
@property (nonatomic, copy) NSString* eventName;
161160
@property (strong, nonatomic) SKProductsRequest *request;
162161
@end
163162

Sources/BranchSDK/Public/BranchEvent.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ typedef NS_ENUM(NSInteger, BranchEventAdType) {
8888
@property (nonatomic, copy) NSString*_Nullable affiliation;
8989
@property (nonatomic, copy) NSString*_Nullable eventDescription;
9090
@property (nonatomic, copy) NSString*_Nullable searchQuery;
91-
91+
@property (nonatomic, copy) NSString*_Nullable eventName;
9292
@property (nonatomic, assign) BranchEventAdType adType;
9393

9494
@property (nonatomic, strong) NSArray<BranchUniversalObject*>*_Nonnull contentItems;
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
//
2+
// BranchEvent+StoreKit2.swift
3+
// Branch-SDK
4+
//
5+
// Created by Nidhi Dixit on 09/30/25.
6+
// Copyright 2024 Branch Metrics. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import StoreKit
11+
import BranchSDK
12+
13+
@available(iOS 15.0, tvOS 15.0, watchOS 8.0, *)
14+
extension BranchEvent {
15+
16+
/// This method extracts detailed product and transaction information from a StoreKit 2 transaction
17+
/// and logs a Branch PURCHASE event with all the extracted information.
18+
/// - Parameter transaction: The StoreKit 2 transaction
19+
public func logEventForTransaction( transaction: Transaction) {
20+
Task {
21+
await logEventAsync(with: transaction)
22+
}
23+
}
24+
25+
private func logEventAsync(with transaction: Transaction) async {
26+
do {
27+
let products = try await Product.products(for: [transaction.productID])
28+
guard let product = products.first else {
29+
BranchLogger.shared().logError("Could not load product for transaction: \(transaction.productID)", error: nil)
30+
return
31+
}
32+
self.populateBUO(with: transaction, product: product)
33+
try await self.logEvent()
34+
BranchLogger.shared().logDebug("Created and logged StoreKit 2 event: \(self.description)", error: nil)
35+
} catch {
36+
BranchLogger.shared().logError("Failed to load product for StoreKit 2 transaction: \(error.localizedDescription)", error: error)
37+
}
38+
}
39+
40+
private func populateBUO(with transaction: Transaction, product: Product) {
41+
let buo = BranchUniversalObject()
42+
buo.canonicalIdentifier = product.id
43+
buo.title = product.displayName
44+
buo.contentDescription = product.description
45+
buo.contentMetadata.quantity = Double(transaction.purchasedQuantity)
46+
buo.contentMetadata.price = NSDecimalNumber(decimal: product.price)
47+
buo.contentMetadata.currency = BNCCurrency(rawValue: product.priceFormatStyle.currencyCode)
48+
buo.contentMetadata.productName = product.displayName
49+
50+
var customMetadata: [String: Any] = [
51+
"logged_from_storekit2": true,
52+
"product_type": product.type.rawValue,
53+
"transaction_id": String(transaction.id),
54+
"original_transaction_id": String(transaction.originalID),
55+
"purchase_date": ISO8601DateFormatter().string(from: transaction.purchaseDate),
56+
"purchased_quantity": transaction.purchasedQuantity
57+
]
58+
59+
if let subscriptionInfo = product.subscription {
60+
customMetadata["subscription_group_id"] = subscriptionInfo.subscriptionGroupID
61+
customMetadata["subscription_period"] = formatSubscriptionPeriod(subscriptionInfo.subscriptionPeriod)
62+
63+
if let introductoryOffer = subscriptionInfo.introductoryOffer {
64+
customMetadata["introductory_offer_type"] = introductoryOffer.type.rawValue
65+
customMetadata["introductory_offer_period"] = formatSubscriptionPeriod(introductoryOffer.period)
66+
}
67+
}
68+
customMetadata["ownership_type"] = transaction.ownershipType.rawValue
69+
70+
if let revocationDate = transaction.revocationDate {
71+
customMetadata["revocation_date"] = ISO8601DateFormatter().string(from: revocationDate)
72+
}
73+
if let revocationReason = transaction.revocationReason {
74+
customMetadata["revocation_reason"] = revocationReason.rawValue
75+
}
76+
77+
buo.contentMetadata.customMetadata = NSMutableDictionary(dictionary: customMetadata)
78+
79+
self.contentItems = [buo]
80+
self.eventName = "PURCHASE"
81+
self.transactionID = String(transaction.id)
82+
self.eventDescription = "StoreKit 2: \(product.displayName)"
83+
self.currency = BNCCurrency(rawValue: product.priceFormatStyle.currencyCode)
84+
self.revenue = NSDecimalNumber(decimal: product.price)
85+
86+
switch product.type {
87+
case .autoRenewable, .nonRenewable:
88+
self.alias = "Subscription"
89+
case .consumable, .nonConsumable:
90+
self.alias = "IAP"
91+
default:
92+
self.alias = "IAP"
93+
}
94+
95+
var eventCustomData: [String: String] = [:]
96+
eventCustomData["transaction_identifier"] = String(transaction.id)
97+
eventCustomData["logged_from_storekit2"] = "true"
98+
self.customData = eventCustomData
99+
}
100+
101+
private func formatSubscriptionPeriod(_ period: Product.SubscriptionPeriod) -> String {
102+
let unitString: String
103+
switch period.unit {
104+
case .day:
105+
unitString = "day"
106+
case .week:
107+
unitString = "week"
108+
case .month:
109+
unitString = "month"
110+
case .year:
111+
unitString = "year"
112+
@unknown default:
113+
unitString = "unknown"
114+
}
115+
return "\(period.value) \(unitString)\(period.value > 1 ? "s" : "")"
116+
}
117+
}

0 commit comments

Comments
 (0)