Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions Data/Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ let project = Project.makeFramework(
.NetworkService,
.SPM.Supabase,
.SPM.GoogleSignIn,
.SPM.FirebaseAnalytics,
.SPM.FirebaseCrashlytics,
.SPM.AppAuth,
// .SPM.FirebaseAnalytics,
// .SPM.FirebaseCrashlytics,
],
hasTests: true,
settings: .settings(
base: SettingsDictionary()
.otherLinkerFlags(["-all_load", "-ObjC"]),
// .otherLinkerFlags(["-all_load", "-ObjC"]),
)
)
170 changes: 85 additions & 85 deletions Data/Sources/Analytics/FirebaseAnalyticsManager.swift
Original file line number Diff line number Diff line change
@@ -1,85 +1,85 @@
import Foundation
import FirebaseAnalytics
import Domain
import LogMacro

/// FirebaseAnalytics를 사용한 Analytics Repository 구현체
public class FirebaseAnalyticsRepository: AnalyticsRepositoryProtocol, @unchecked Sendable {

public init() {
#logDebug("🔥 [Analytics] ===== FIREBASE ANALYTICS REPOSITORY INITIALIZED =====")
#logDebug("🔥 [Analytics] Sending app_analytics_initialized event...")

Analytics.logEvent("app_analytics_initialized", parameters: [
"timestamp": Date().timeIntervalSince1970,
"version": "1.0"
])
}

public func sendEvent(_ event: AnalyticsEvent) async {
switch event {
case .auth(let eventType, let data):
await sendAuthEvent(eventType, data)
case .deeplink(let data):
await sendDeeplinkEvent(data)
case .travel(let eventType, let data):
await sendTravelEvent(eventType, data)
case .expense(let eventType, let data):
await sendExpenseEvent(eventType, data)
}
}

// MARK: - Private Event Handlers

private func sendAuthEvent(_ eventType: AuthEventType, _ data: AuthEventData) async {
var params: [String: Any] = ["social_type": data.socialType]
if let isFirst = data.isFirst {
params["is_first"] = isFirst
}

#logDebug("🔥 [Analytics] Parameters: \(params)")
Analytics.logEvent(eventType.rawValue, parameters: params)
}

private func sendDeeplinkEvent(_ data: DeeplinkEventData) async {
let parameters: [String: Any] = [
"deeplink": data.deeplink,
"deeplink_type": data.type
]

#logDebug("🔥 [Analytics] Parameters: \(parameters)")
Analytics.logEvent("deeplink_open", parameters: parameters)
#logDebug("🔥 [Analytics] ✅ deeplink_open event sent to Firebase")
}

private func sendTravelEvent(_ eventType: TravelEventType, _ data: TravelEventData) async {
var params: [String: Any] = ["travel_id": data.travelId]

// Optional fields based on event type
if let userId = data.userId { params["user_id"] = userId }
if let memberId = data.memberId { params["member_id"] = memberId }
if let role = data.role { params["role"] = role }
if let newOwnerId = data.newOwnerId { params["new_owner_id"] = newOwnerId }

#logDebug("🔥 [Analytics] Parameters: \(params)")
Analytics.logEvent(eventType.rawValue, parameters: params)
}

private func sendExpenseEvent(_ eventType: ExpenseEventType, _ data: ExpenseEventData) async {
var params: [String: Any] = ["travel_id": data.travelId]

// Add optional fields
if let expenseId = data.expenseId { params["expense_id"] = expenseId }
if let amount = data.amount { params["amount"] = amount }
if let currency = data.currency { params["currency"] = currency }
if let category = data.category { params["category"] = category }
if let payerId = data.payerId { params["payer_id"] = payerId }
if let source = data.source { params["source"] = source }
if let tab = data.tab { params["tab"] = tab }
if let expenseDate = data.expenseDate { params["expense_date"] = expenseDate }
if let errorCode = data.errorCode { params["error_code"] = errorCode }

#logDebug("🔥 [Analytics] Parameters: \(params)")
Analytics.logEvent(eventType.rawValue, parameters: params)
}
}
//import Foundation
//import FirebaseAnalytics
//import Domain
//import LogMacro
//
///// FirebaseAnalytics를 사용한 Analytics Repository 구현체
//public class FirebaseAnalyticsRepository: AnalyticsRepositoryProtocol, @unchecked Sendable {
//
// public init() {
// #logDebug("🔥 [Analytics] ===== FIREBASE ANALYTICS REPOSITORY INITIALIZED =====")
// #logDebug("🔥 [Analytics] Sending app_analytics_initialized event...")
//
// Analytics.logEvent("app_analytics_initialized", parameters: [
// "timestamp": Date().timeIntervalSince1970,
// "version": "1.0"
// ])
// }
//
// public func sendEvent(_ event: AnalyticsEvent) async {
// switch event {
// case .auth(let eventType, let data):
// await sendAuthEvent(eventType, data)
// case .deeplink(let data):
// await sendDeeplinkEvent(data)
// case .travel(let eventType, let data):
// await sendTravelEvent(eventType, data)
// case .expense(let eventType, let data):
// await sendExpenseEvent(eventType, data)
// }
// }
//
// // MARK: - Private Event Handlers
//
// private func sendAuthEvent(_ eventType: AuthEventType, _ data: AuthEventData) async {
// var params: [String: Any] = ["social_type": data.socialType]
// if let isFirst = data.isFirst {
// params["is_first"] = isFirst
// }
//
// #logDebug("🔥 [Analytics] Parameters: \(params)")
// Analytics.logEvent(eventType.rawValue, parameters: params)
// }
//
// private func sendDeeplinkEvent(_ data: DeeplinkEventData) async {
// let parameters: [String: Any] = [
// "deeplink": data.deeplink,
// "deeplink_type": data.type
// ]
//
// #logDebug("🔥 [Analytics] Parameters: \(parameters)")
// Analytics.logEvent("deeplink_open", parameters: parameters)
// #logDebug("🔥 [Analytics] ✅ deeplink_open event sent to Firebase")
// }
//
// private func sendTravelEvent(_ eventType: TravelEventType, _ data: TravelEventData) async {
// var params: [String: Any] = ["travel_id": data.travelId]
//
// // Optional fields based on event type
// if let userId = data.userId { params["user_id"] = userId }
// if let memberId = data.memberId { params["member_id"] = memberId }
// if let role = data.role { params["role"] = role }
// if let newOwnerId = data.newOwnerId { params["new_owner_id"] = newOwnerId }
//
// #logDebug("🔥 [Analytics] Parameters: \(params)")
// Analytics.logEvent(eventType.rawValue, parameters: params)
// }
//
// private func sendExpenseEvent(_ eventType: ExpenseEventType, _ data: ExpenseEventData) async {
// var params: [String: Any] = ["travel_id": data.travelId]
//
// // Add optional fields
// if let expenseId = data.expenseId { params["expense_id"] = expenseId }
// if let amount = data.amount { params["amount"] = amount }
// if let currency = data.currency { params["currency"] = currency }
// if let category = data.category { params["category"] = category }
// if let payerId = data.payerId { params["payer_id"] = payerId }
// if let source = data.source { params["source"] = source }
// if let tab = data.tab { params["tab"] = tab }
// if let expenseDate = data.expenseDate { params["expense_date"] = expenseDate }
// if let errorCode = data.errorCode { params["error_code"] = errorCode }
//
// #logDebug("🔥 [Analytics] Parameters: \(params)")
// Analytics.logEvent(eventType.rawValue, parameters: params)
// }
//}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation

/// Analytics 이벤트 전송을 위한 Repository 프로토콜
public protocol AnalyticsRepositoryProtocol: Sendable {
func sendEvent(_ event: AnalyticsEvent) async
}
//import Foundation
//
///// Analytics 이벤트 전송을 위한 Repository 프로토콜
//public protocol AnalyticsRepositoryProtocol: Sendable {
// func sendEvent(_ event: AnalyticsEvent) async
//}
188 changes: 94 additions & 94 deletions Domain/Sources/Repository/Analytics/MockAnalyticsRepository.swift
Original file line number Diff line number Diff line change
@@ -1,94 +1,94 @@
import Foundation

/// Mock Analytics Repository for testing
public actor MockAnalyticsRepository: AnalyticsRepositoryProtocol, Sendable {

// MARK: - Tracking

private let eventsStorage = ThreadSafeContainer<[AnalyticsEvent]>([])

/// 추적된 이벤트들 (테스트에서 확인용)
public var trackedEvents: [AnalyticsEvent] {
eventsStorage.value
}

public init() {}

public func sendEvent(_ event: AnalyticsEvent) async {
eventsStorage.modify { $0.append(event) }
}

/// 추적된 이벤트 초기화 (테스트 간 클린업용)
public func clearTrackedEvents() {
eventsStorage.modify { $0.removeAll() }
}

/// 특정 타입의 이벤트가 추적되었는지 확인
public func hasTrackedEvent<T>(type: T.Type) -> Bool {
return trackedEvents.contains { event in
switch event {
case .auth:
return T.self == AuthEventType.self
case .travel:
return T.self == TravelEventType.self
case .expense:
return T.self == ExpenseEventType.self
case .deeplink:
return T.self == DeeplinkEventData.self
}
}
}

/// Auth 이벤트 추적 개수
public var authEventCount: Int {
trackedEvents.filter {
if case .auth = $0 { return true }
return false
}.count
}

/// Travel 이벤트 추적 개수
public var travelEventCount: Int {
trackedEvents.filter {
if case .travel = $0 { return true }
return false
}.count
}

/// Expense 이벤트 추적 개수
public var expenseEventCount: Int {
trackedEvents.filter {
if case .expense = $0 { return true }
return false
}.count
}

/// Deeplink 이벤트 추적 개수
public var deeplinkEventCount: Int {
trackedEvents.filter {
if case .deeplink = $0 { return true }
return false
}.count
}
}

// MARK: - Thread Safety Helper

private class ThreadSafeContainer<T>: @unchecked Sendable {
private var storage: T
private let lock = NSLock()

var value: T {
lock.withLock { storage }
}

init(_ value: T) {
self.storage = value
}

func modify(_ transform: (inout T) -> Void) {
lock.withLock {
transform(&storage)
}
}
}
//import Foundation
//
///// Mock Analytics Repository for testing
//public actor MockAnalyticsRepository: AnalyticsRepositoryProtocol, Sendable {
//
// // MARK: - Tracking
//
// private let eventsStorage = ThreadSafeContainer<[AnalyticsEvent]>([])
//
// /// 추적된 이벤트들 (테스트에서 확인용)
// public var trackedEvents: [AnalyticsEvent] {
// eventsStorage.value
// }
//
// public init() {}
//
// public func sendEvent(_ event: AnalyticsEvent) async {
// eventsStorage.modify { $0.append(event) }
// }
//
// /// 추적된 이벤트 초기화 (테스트 간 클린업용)
// public func clearTrackedEvents() {
// eventsStorage.modify { $0.removeAll() }
// }
//
// /// 특정 타입의 이벤트가 추적되었는지 확인
// public func hasTrackedEvent<T>(type: T.Type) -> Bool {
// return trackedEvents.contains { event in
// switch event {
// case .auth:
// return T.self == AuthEventType.self
// case .travel:
// return T.self == TravelEventType.self
// case .expense:
// return T.self == ExpenseEventType.self
// case .deeplink:
// return T.self == DeeplinkEventData.self
// }
// }
// }
//
// /// Auth 이벤트 추적 개수
// public var authEventCount: Int {
// trackedEvents.filter {
// if case .auth = $0 { return true }
// return false
// }.count
// }
//
// /// Travel 이벤트 추적 개수
// public var travelEventCount: Int {
// trackedEvents.filter {
// if case .travel = $0 { return true }
// return false
// }.count
// }
//
// /// Expense 이벤트 추적 개수
// public var expenseEventCount: Int {
// trackedEvents.filter {
// if case .expense = $0 { return true }
// return false
// }.count
// }
//
// /// Deeplink 이벤트 추적 개수
// public var deeplinkEventCount: Int {
// trackedEvents.filter {
// if case .deeplink = $0 { return true }
// return false
// }.count
// }
//}
//
//// MARK: - Thread Safety Helper
//
//private class ThreadSafeContainer<T>: @unchecked Sendable {
// private var storage: T
// private let lock = NSLock()
//
// var value: T {
// lock.withLock { storage }
// }
//
// init(_ value: T) {
// self.storage = value
// }
//
// func modify(_ transform: (inout T) -> Void) {
// lock.withLock {
// transform(&storage)
// }
// }
//}
Loading