Skip to content

Commit e895245

Browse files
authored
Analytics improvements (#68)
* Analytics improvements * Add test cases * Update analytics tests
1 parent afe72f5 commit e895245

File tree

2 files changed

+282
-9
lines changed

2 files changed

+282
-9
lines changed

Sources/SkipFirebaseAnalytics/SkipFirebaseAnalytics.swift

Lines changed: 112 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception
22
#if !SKIP_BRIDGE
33
#if SKIP
4+
import Foundation
45
import SkipFirebaseCore
56
import kotlinx.coroutines.tasks.await
67

@@ -14,14 +15,119 @@ public final class Analytics {
1415
self.analytics = analytics
1516
}
1617

17-
// public static func analytics() -> Analytics {
18-
// Analytics(analytics: com.google.firebase.analytics.FirebaseAnalytics.getInstance())
19-
// }
18+
private static var instance: com.google.firebase.analytics.FirebaseAnalytics {
19+
com.google.firebase.analytics.FirebaseAnalytics.getInstance(skip.foundation.ProcessInfo.processInfo.androidContext)
20+
}
2021

21-
public static func logEvent(_ name: String, parameters: [String: Any] = [:]) {
22+
private static func toBundle(_ parameters: [String: Any]?) -> android.os.Bundle? {
23+
guard let parameters = parameters else { return nil }
2224
let bundle = android.os.Bundle()
23-
// TODO: add parameters to bundle
24-
com.google.firebase.analytics.FirebaseAnalytics.getInstance(skip.foundation.ProcessInfo.processInfo.androidContext).logEvent(name, bundle)
25+
for (key, value) in parameters {
26+
if let s = value as? String {
27+
bundle.putString(key, s)
28+
} else if let b = value as? Bool {
29+
bundle.putBoolean(key, b)
30+
} else if let i = value as? Int {
31+
bundle.putLong(key, Int64(i))
32+
} else if let l = value as? Int64 {
33+
bundle.putLong(key, l)
34+
} else if let d = value as? Double {
35+
bundle.putDouble(key, d)
36+
} else if let f = value as? Float {
37+
bundle.putFloat(key, f)
38+
} else {
39+
bundle.putString(key, "\(value)")
40+
}
41+
}
42+
return bundle
43+
}
44+
45+
public static func logEvent(_ name: String, parameters: [String: Any]? = nil) {
46+
instance.logEvent(name, toBundle(parameters ?? [:]))
47+
}
48+
49+
public static func setUserProperty(_ value: String?, forName name: String) {
50+
instance.setUserProperty(name, value)
51+
}
52+
53+
public static func setUserID(_ userID: String?) {
54+
instance.setUserId(userID)
55+
}
56+
57+
public static func setAnalyticsCollectionEnabled(_ enabled: Bool) {
58+
instance.setAnalyticsCollectionEnabled(enabled)
59+
}
60+
61+
public static func setDefaultEventParameters(_ parameters: [String: Any]?) {
62+
instance.setDefaultEventParameters(toBundle(parameters))
63+
}
64+
65+
public static func resetAnalyticsData() {
66+
instance.resetAnalyticsData()
67+
}
68+
69+
public static func appInstanceID() -> String? {
70+
// Android's getAppInstanceId() returns Task<String>; block to match synchronous iOS API
71+
return com.google.android.gms.tasks.Tasks.await(instance.getAppInstanceId())
72+
}
73+
74+
public static func sessionID() async throws -> Int64? {
75+
let id = instance.getSessionId().await()
76+
return id as? Int64
77+
}
78+
79+
public static func setSessionTimeoutInterval(_ seconds: TimeInterval) {
80+
instance.setSessionTimeoutDuration(Int64(seconds * 1000.0))
81+
}
82+
83+
public static func setConsent(_ consentSettings: [ConsentType: ConsentStatus]) {
84+
let map = java.util.HashMap<com.google.firebase.analytics.FirebaseAnalytics.ConsentType, com.google.firebase.analytics.FirebaseAnalytics.ConsentStatus>()
85+
for (type, status) in consentSettings {
86+
map.put(type.platformValue, status.platformValue)
87+
}
88+
instance.setConsent(map)
89+
}
90+
}
91+
92+
public struct ConsentType: Hashable {
93+
public let rawValue: String
94+
95+
public init(rawValue: String) {
96+
self.rawValue = rawValue
97+
}
98+
99+
public static let adPersonalization = ConsentType(rawValue: "ad_personalization")
100+
public static let adStorage = ConsentType(rawValue: "ad_storage")
101+
public static let adUserData = ConsentType(rawValue: "ad_user_data")
102+
public static let analyticsStorage = ConsentType(rawValue: "analytics_storage")
103+
104+
public var platformValue: com.google.firebase.analytics.FirebaseAnalytics.ConsentType {
105+
switch rawValue {
106+
case "ad_personalization": return com.google.firebase.analytics.FirebaseAnalytics.ConsentType.AD_PERSONALIZATION
107+
case "ad_storage": return com.google.firebase.analytics.FirebaseAnalytics.ConsentType.AD_STORAGE
108+
case "ad_user_data": return com.google.firebase.analytics.FirebaseAnalytics.ConsentType.AD_USER_DATA
109+
case "analytics_storage": return com.google.firebase.analytics.FirebaseAnalytics.ConsentType.ANALYTICS_STORAGE
110+
default: return com.google.firebase.analytics.FirebaseAnalytics.ConsentType.ANALYTICS_STORAGE
111+
}
112+
}
113+
}
114+
115+
public struct ConsentStatus: Hashable {
116+
public let rawValue: String
117+
118+
public init(rawValue: String) {
119+
self.rawValue = rawValue
120+
}
121+
122+
public static let granted = ConsentStatus(rawValue: "granted")
123+
public static let denied = ConsentStatus(rawValue: "denied")
124+
125+
public var platformValue: com.google.firebase.analytics.FirebaseAnalytics.ConsentStatus {
126+
switch rawValue {
127+
case "granted": return com.google.firebase.analytics.FirebaseAnalytics.ConsentStatus.GRANTED
128+
case "denied": return com.google.firebase.analytics.FirebaseAnalytics.ConsentStatus.DENIED
129+
default: return com.google.firebase.analytics.FirebaseAnalytics.ConsentStatus.DENIED
130+
}
25131
}
26132
}
27133

Tests/SkipFirebaseAnalyticsTests/SkipFirebaseAnalyticsTests.swift

Lines changed: 170 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,175 @@ import SkipFirebaseAnalytics
1313
let logger: Logger = Logger(subsystem: "SkipFirebaseAnalyticsTests", category: "Tests")
1414

1515
@MainActor final class SkipFirebaseAnalyticsTests: XCTestCase {
16-
func testSkipFirebaseAnalyticsTests() async throws {
17-
Analytics.logEvent("x", parameters: ["a": [1, 2, false]])
16+
func skipTests() throws {
17+
throw XCTSkip("test intentionally skipped because it exists just for compiler validation")
1818
}
19-
}
2019

20+
func testLogEvent() throws {
21+
try skipTests()
22+
23+
let params: [String: Any] = [
24+
AnalyticsParameterItemName: "test_item",
25+
AnalyticsParameterPrice: 9.99,
26+
AnalyticsParameterQuantity: 1,
27+
AnalyticsParameterCurrency: "USD"
28+
]
29+
Analytics.logEvent("ABC", parameters: params)
30+
}
31+
32+
func testSetUserProperty() throws {
33+
try skipTests()
34+
35+
Analytics.setUserProperty("X", forName: "Y")
36+
Analytics.setUserProperty(nil, forName: "Y")
37+
}
38+
39+
func testSetUserID() throws {
40+
try skipTests()
41+
42+
Analytics.setUserID(nil)
43+
Analytics.setUserID("ABC")
44+
}
45+
46+
func testSetAnalyticsCollectionEnabled() throws {
47+
try skipTests()
48+
49+
Analytics.setAnalyticsCollectionEnabled(false)
50+
}
51+
52+
func testSetDefaultEventParameters() throws {
53+
try skipTests()
54+
55+
Analytics.setDefaultEventParameters(nil)
56+
Analytics.setDefaultEventParameters(["x": false])
57+
}
58+
59+
func testResetAnalyticsData() throws {
60+
try skipTests()
61+
62+
Analytics.resetAnalyticsData()
63+
}
64+
65+
func testAppInstanceID() throws {
66+
try skipTests()
67+
68+
let _: String? = Analytics.appInstanceID()
69+
}
70+
71+
func testSessionID() async throws {
72+
try skipTests()
73+
74+
let _: Int64? = try await Analytics.sessionID()
75+
}
76+
77+
func testSetSessionTimeoutInterval() throws {
78+
try skipTests()
79+
80+
Analytics.setSessionTimeoutInterval(TimeInterval(100.0))
81+
}
82+
83+
func testConsentTypes() throws {
84+
try skipTests()
85+
86+
// Verify ConsentType static members exist and are the right type
87+
let _: ConsentType = .adPersonalization
88+
let _: ConsentType = .adStorage
89+
let _: ConsentType = .adUserData
90+
let _: ConsentType = .analyticsStorage
91+
}
92+
93+
func testConsentStatus() throws {
94+
try skipTests()
95+
96+
// Verify ConsentStatus static members exist and are the right type
97+
let _: ConsentStatus = .granted
98+
let _: ConsentStatus = .denied
99+
}
100+
101+
func testSetConsent() throws {
102+
try skipTests()
103+
104+
Analytics.setConsent([
105+
.analyticsStorage: .granted,
106+
.adPersonalization: .denied
107+
])
108+
}
109+
110+
func testEventNameConstants() throws {
111+
try skipTests()
112+
113+
// Verify event name constants exist and are strings
114+
let events: [String] = [
115+
AnalyticsEventAdImpression,
116+
AnalyticsEventAddPaymentInfo,
117+
AnalyticsEventAddShippingInfo,
118+
AnalyticsEventAddToCart,
119+
AnalyticsEventAddToWishlist,
120+
AnalyticsEventAppOpen,
121+
AnalyticsEventBeginCheckout,
122+
AnalyticsEventCampaignDetails,
123+
AnalyticsEventEarnVirtualCurrency,
124+
AnalyticsEventGenerateLead,
125+
AnalyticsEventJoinGroup,
126+
AnalyticsEventLevelEnd,
127+
AnalyticsEventLevelStart,
128+
AnalyticsEventLevelUp,
129+
AnalyticsEventLogin,
130+
AnalyticsEventPostScore,
131+
AnalyticsEventPurchase,
132+
AnalyticsEventRefund,
133+
AnalyticsEventRemoveFromCart,
134+
AnalyticsEventScreenView,
135+
AnalyticsEventSearch,
136+
AnalyticsEventSelectContent,
137+
AnalyticsEventSelectItem,
138+
AnalyticsEventSelectPromotion,
139+
AnalyticsEventShare,
140+
AnalyticsEventSignUp,
141+
AnalyticsEventSpendVirtualCurrency,
142+
AnalyticsEventTutorialBegin,
143+
AnalyticsEventTutorialComplete,
144+
AnalyticsEventUnlockAchievement,
145+
AnalyticsEventViewCart,
146+
AnalyticsEventViewItem,
147+
AnalyticsEventViewItemList,
148+
AnalyticsEventViewPromotion,
149+
AnalyticsEventViewSearchResults,
150+
]
151+
XCTAssertFalse(events.isEmpty)
152+
}
153+
154+
func testParameterNameConstants() throws {
155+
try skipTests()
156+
157+
// Verify a representative set of parameter constants exist and are strings
158+
let params: [String] = [
159+
AnalyticsParameterItemName,
160+
AnalyticsParameterItemID,
161+
AnalyticsParameterPrice,
162+
AnalyticsParameterQuantity,
163+
AnalyticsParameterCurrency,
164+
AnalyticsParameterValue,
165+
AnalyticsParameterScreenName,
166+
AnalyticsParameterScreenClass,
167+
AnalyticsParameterSearchTerm,
168+
AnalyticsParameterMethod,
169+
AnalyticsParameterScore,
170+
AnalyticsParameterLevel,
171+
AnalyticsParameterContent,
172+
AnalyticsParameterContentType,
173+
AnalyticsParameterCoupon,
174+
AnalyticsParameterTransactionID,
175+
AnalyticsParameterShipping,
176+
AnalyticsParameterTax,
177+
]
178+
XCTAssertFalse(params.isEmpty)
179+
}
180+
181+
func testUserPropertyConstants() throws {
182+
try skipTests()
183+
184+
let _: String = AnalyticsUserPropertyAllowAdPersonalizationSignals
185+
let _: String = AnalyticsUserPropertySignUpMethod
186+
}
187+
}

0 commit comments

Comments
 (0)