-
Notifications
You must be signed in to change notification settings - Fork 1
✨ Feat: Mixpanel 분석 프로바이더 추가 #151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| // | ||
| // MixpanelAnalyticsProvider.swift | ||
| // AnalyticsKit | ||
| // | ||
| // Created by 홍 on 9/10/26. | ||
| // | ||
|
|
||
| import Foundation | ||
| import os | ||
|
|
||
| import Mixpanel | ||
|
|
||
| public final class MixpanelAnalyticsProvider: AnalyticsProviding { | ||
| public let identifier = "Mixpanel" | ||
|
|
||
| private let logger = Logger(subsystem: "TapTap", category: "AnalyticsKit.Mixpanel") | ||
|
|
||
| private let client: MixpanelInstance? | ||
|
|
||
| public init(token: String?, isVerboseLoggingEnabled: Bool = false) { | ||
| guard let token else { | ||
| client = nil | ||
| return | ||
| } | ||
|
|
||
| // trackAutomaticEvents: Amplitude의 [.sessions, .appLifecycles]와 같은 자리다. | ||
| // 세션·앱 생명주기만 자동으로 수집하고 화면 뷰는 포함하지 않는다 — screen_view는 | ||
| // 리듀서에서 직접 심는다(뷰 자동수집은 SwiftUI에서 화면 구분이 뭉개진다). | ||
| let instance = Mixpanel.initialize(token: token, trackAutomaticEvents: true) | ||
| instance.loggingEnabled = isVerboseLoggingEnabled | ||
| client = instance | ||
| } | ||
|
|
||
| @discardableResult | ||
| public func start() -> Bool { | ||
| guard client != nil else { | ||
| logger.notice("MIXPANEL_TOKEN이 비어 있어 Mixpanel을 건너뛴다.") | ||
| return false | ||
| } | ||
| return true | ||
| } | ||
|
|
||
| public func track(_ event: AnalyticsEvent) { | ||
| guard let client else { return } | ||
| client.track(event: event.name, properties: event.parameters.mapValues(\.mixpanelValue)) | ||
| } | ||
|
|
||
| public func setUserProperty(_ property: AnalyticsUserProperty) { | ||
| guard let client else { return } | ||
| client.people.set(properties: [property.name: property.value.mixpanelValue]) | ||
| } | ||
|
|
||
| public func setUserID(_ userID: String?) { | ||
| guard let client else { return } | ||
| // Mixpanel의 identify는 nil을 받지 않는다. 사용자를 지우는 것은 reset이고, | ||
| // 그래야 다음 이벤트가 새 distinct_id로 나간다. | ||
| if let userID { | ||
| client.identify(distinctId: userID) | ||
| } else { | ||
| client.reset() | ||
| } | ||
| } | ||
|
|
||
| public func setCollectionEnabled(_ isEnabled: Bool) { | ||
| guard let client else { return } | ||
| if isEnabled { | ||
| client.optInTracking() | ||
| } else { | ||
| client.optOutTracking() | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// `AnalyticsValue`가 SDK를 모르게 두려고 여기에 둔다 — | ||
| /// `firebaseValue`·`amplitudeValue`는 `Any`라 SDK 타입이 필요 없지만 | ||
| /// Mixpanel은 `MixpanelType` 프로토콜을 요구해서 import가 따라붙는다. | ||
| private extension AnalyticsValue { | ||
| var mixpanelValue: MixpanelType { | ||
| switch self { | ||
| case .string(let value): return value | ||
| case .int(let value): return value | ||
| case .double(let value): return value | ||
| case .bool(let value): return value // Amplitude와 같이 native boolean. GA4만 문자열이다 | ||
| } | ||
| } | ||
| } |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -8,7 +8,8 @@ let packageSettings = PackageSettings( | |||||
| productTypes: [ | ||||||
| "ComposableArchitecture": .framework, | ||||||
| "Lottie": .staticFramework, | ||||||
| "AmplitudeSwift": .staticFramework | ||||||
| "AmplitudeSwift": .staticFramework, | ||||||
| "Mixpanel": .staticFramework | ||||||
| ] | ||||||
| ) | ||||||
| #endif | ||||||
|
|
@@ -31,6 +32,10 @@ let package = Package( | |||||
| .package( | ||||||
| url: "https://github.com/amplitude/Amplitude-Swift.git", | ||||||
| from: "1.18.8" | ||||||
| ), | ||||||
| .package( | ||||||
| url: "https://github.com/mixpanel/mixpanel-swift.git", | ||||||
| from: "5.0.0" | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -euo pipefail
found=0
while IFS= read -r file; do
found=1
echo "== ${file} =="
rg -n -C 4 'mixpanel-swift|Mixpanel' "$file"
done < <(find . -name Package.resolved -print)
test "$found" -eq 1Repository: TapTapTeam/taptap-ios Length of output: 257 🤖 get_repo_knowledge executed:
Length of output: 6506 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '== tracked package manifests =='
git ls-files 'TapTap/Tuist/Package.swift' '*Package.resolved' | sort
printf '%s\n' '== Package.swift context =='
cat -n TapTap/Tuist/Package.swift | sed -n '25,50p'
printf '%s\n' '== resolved files =='
while IFS= read -r file; do
printf '%s\n' "== ${file} =="
rg -n -C 5 'mixpanel-swift|Mixpanel' "$file" || true
done < <(git ls-files '*Package.resolved')Repository: TapTapTeam/taptap-ios Length of output: 915 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '== package manifest and lockfile names =='
find . -type f \( -name 'Package.swift' -o -name 'Package.resolved' \) -print | sort
printf '%s\n' '== exact Mixpanel references =='
rg -n -C 4 'mixpanel-swift|Mixpanel|from:' TapTap/Tuist/Package.swift . --glob 'Package.resolved' --glob '!**/.git/**' || trueRepository: TapTapTeam/taptap-ios Length of output: 2756 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '== tracked files =='
git ls-files | rg '(^|/)(Package\.swift|Package\.resolved)$' | sort
printf '%s\n' '== manifest context =='
cat -n TapTap/Tuist/Package.swift | sed -n '30,45p'
printf '%s\n' '== lockfile Mixpanel entries =='
while IFS= read -r file; do
printf '%s\n' "== ${file} =="
rg -n -C 5 'mixpanel-swift|Mixpanel' "$file" || true
done < <(git ls-files | rg '(^|/)Package\.resolved$')Repository: TapTapTeam/taptap-ios Length of output: 715 🤖 get_repo_knowledge executed:
Length of output: 6506 Mixpanel 최소 버전을 5.2.0으로 설정하세요. 현재 - from: "5.0.0"
+ from: "5.2.0"📝 Committable suggestion
Suggested change
🤖 Prompt for AI AgentsSource: MCP tools |
||||||
| ) | ||||||
| ] | ||||||
| ) | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: TapTapTeam/taptap-ios
Length of output: 253
🤖 get_repo_knowledge executed:
get_repo_knowledge TapTapTeam/taptap-ios /tmp/coderabbit-repo-knowledge/taptapteam-taptap-ios-5c8bde25Length of output: 1398
🏁 Script executed:
Repository: TapTapTeam/taptap-ios
Length of output: 35594
🏁 Script executed:
Repository: TapTapTeam/taptap-ios
Length of output: 35533
🏁 Script executed:
Repository: TapTapTeam/taptap-ios
Length of output: 35270
🏁 Script executed:
Repository: TapTapTeam/taptap-ios
Length of output: 13427
🏁 Script executed:
Repository: TapTapTeam/taptap-ios
Length of output: 21935
🏁 Script executed:
Repository: TapTapTeam/taptap-ios
Length of output: 293
토큰이 없는 빌드에서 미해결 변수를
nil로 정규화하세요.AnalyticsConfiguration.fromMainBundle()의nonEmptyString은 빈 문자열만nil로 변환합니다.$(MIXPANEL_TOKEN)리터럴이Info.plist에 남으면mixpanelToken != nil조건을 통과하여MixpanelAnalyticsProvider가 추가될 수 있습니다. 토큰이 없는 빌드에서는 미해결 변수와 빈 문자열을 모두nil로 처리하세요.🤖 Prompt for AI Agents