feat: 라이브 30분 연속 청취시 트래킹 API 호출 기능 추가

This commit is contained in:
Yu Sung
2025-05-17 17:29:48 +09:00
parent 35d98b2378
commit 0e61ee1adf
5 changed files with 114 additions and 0 deletions

View File

@@ -20,6 +20,7 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
private let userRepository = UserRepository()
private let reportRepository = ReportRepository()
private let rouletteRepository = RouletteRepository()
private let userActionRepository = UserActionRepository()
private var subscription = Set<AnyCancellable>()
@Published var isSpeakerMute = false
@@ -218,6 +219,7 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
var timer: DispatchSourceTimer?
var heartTimer: DispatchSourceTimer?
var periodicPlaybackTimer: DispatchSourceTimer?
var isAvailableLikeHeart = false
@@ -284,6 +286,8 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
if containNoChatRoom() {
startNoChatting()
}
startPeriodicPlaybackValidation()
}
func agoraConnectFail() {
@@ -1784,6 +1788,36 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
)
}
func stopPeriodicPlaybackValidation() {
periodicPlaybackTimer?.cancel()
periodicPlaybackTimer = nil
}
private func trackEventLiveContinuousListen30() {
userActionRepository.trackEvent(actionType: .LIVE_CONTINUOUS_LISTEN_30)
.sink { result in
switch result {
case .finished:
DEBUG_LOG("finish")
case .failure(let error):
ERROR_LOG(error.localizedDescription)
}
} receiveValue: { response in
DEBUG_LOG("트래킹 성공: \(response)")
}
.store(in: &subscription)
}
private func startPeriodicPlaybackValidation() {
let queue = DispatchQueue.global(qos: .background)
periodicPlaybackTimer = DispatchSource.makeTimerSource(queue: queue)
periodicPlaybackTimer?.schedule(deadline: .now() + 1800, repeating: 1800)
periodicPlaybackTimer?.setEventHandler { [weak self] in
self?.trackEventLiveContinuousListen30()
}
periodicPlaybackTimer?.resume()
}
private func calculatePercentages(options: [RouletteItem]) -> [RoulettePreviewItem] {
let updatedOptions = options.map { option in
return RoulettePreviewItem(title: option.title, percent: "\(String(format: "%.2f", option.percentage))%")

View File

@@ -433,6 +433,7 @@ struct LiveRoomViewV2: View {
.onDisappear {
UIApplication.shared.isIdleTimerDisabled = false
NotificationCenter.default.removeObserver(self)
viewModel.stopPeriodicPlaybackValidation()
}
ZStack {

View File

@@ -0,0 +1,46 @@
//
// UserActionApi.swift
// SodaLive
//
// Created by klaus on 5/17/25.
//
import Foundation
import Moya
enum UserActionApi {
case trackEvent(request: UserActionRequest)
}
extension UserActionApi: TargetType {
var baseURL: URL {
return URL(string: BASE_URL)!
}
var path: String {
switch self {
case .trackEvent:
return "/user-action"
}
}
var method: Moya.Method {
switch self {
case .trackEvent:
return .post
}
}
var task: Moya.Task {
switch self {
case .trackEvent(let request):
return .requestJSONEncodable(request)
}
}
var headers: [String : String]? {
return ["Authorization": "Bearer \(UserDefaults.string(forKey: UserDefaultsKey.token))"]
}
}

View File

@@ -0,0 +1,19 @@
//
// UserActionRepository.swift
// SodaLive
//
// Created by klaus on 5/17/25.
//
import Foundation
import CombineMoya
import Combine
import Moya
final class UserActionRepository {
private let api = MoyaProvider<UserActionApi>()
func trackEvent(actionType: ActionType) -> AnyPublisher<Response, MoyaError> {
return api.requestPublisher(.trackEvent(request: UserActionRequest(actionType: actionType)))
}
}

View File

@@ -0,0 +1,14 @@
//
// UserActionRequest.swift
// SodaLive
//
// Created by klaus on 5/17/25.
//
struct UserActionRequest: Encodable {
let actionType: ActionType
}
enum ActionType: String, Encodable {
case LIVE_CONTINUOUS_LISTEN_30
}