feat(event): 이벤트 상세 딥링크와 OneLink를 연결한다

This commit is contained in:
Yu Sung
2026-10-29 15:44:21 +09:00
parent ed92dfbe40
commit c78f7417ab
7 changed files with 595 additions and 10 deletions
+113 -2
View File
@@ -1,5 +1,7 @@
import Foundation
import AppsFlyerLib
enum AppDeepLinkAction {
case live(roomId: Int)
case userCreatorChat(roomId: Int)
@@ -7,6 +9,7 @@ enum AppDeepLinkAction {
case series(seriesId: Int)
case giftDetail(applicationNo: String)
case community(postId: Int)
case eventDetail(eventId: Int)
case message
case audition
}
@@ -17,7 +20,14 @@ enum AppDeepLinkSource {
}
enum AppDeepLinkHandler {
private static var navigatingEventId: Int?
static func handle(url: URL, source: AppDeepLinkSource = .external) -> Bool {
if isOneLink(url) {
AppsFlyerLib.shared().performOnAppAttribution(with: url)
return true
}
guard isSupportedScheme(url) else {
return false
}
@@ -26,16 +36,58 @@ enum AppDeepLinkHandler {
return false
}
dispatch(action: action, source: source)
return true
}
static func handleEventDeepLink(result: DeepLinkResult) -> Bool {
guard result.status == .found,
let clickEvent = result.deepLink?.clickEvent,
clickEvent["deep_link_value"] as? String == "event-detail",
let id = eventId(clickEvent["deep_link_sub5"] as? String) else {
return false
}
dispatch(action: .eventDetail(eventId: id))
return true
}
static func handlePushDeepLink(userInfo: [AnyHashable: Any]) -> Bool {
if userInfo["notifly_message_type"] as? String == "push-notification" {
if let url = userInfo["url"] as? URL, !url.absoluteString.isEmpty {
return true
}
if let urlString = userInfo["url"] as? String, !urlString.isEmpty {
return true
}
}
let deepLinkString = (userInfo["deep_link"] as? String ?? "")
.trimmingCharacters(in: .whitespacesAndNewlines)
guard !deepLinkString.isEmpty else { return false }
if let url = URL(string: deepLinkString), isOneLink(url) {
AppsFlyerLib.shared().handlePushNotification(userInfo)
} else {
_ = handle(urlString: deepLinkString)
}
return true
}
private static func dispatch(action: AppDeepLinkAction, source: AppDeepLinkSource = .external) {
DispatchQueue.main.async {
if case .splash = AppState.shared.rootStep {
if case .eventDetail(let eventId) = action,
case .eventDetail(let pendingId)? = AppState.shared.pendingDeepLinkAction,
pendingId == eventId {
return
}
AppState.shared.pendingDeepLinkAction = action
return
}
apply(action: action, source: source)
}
return true
}
static func handle(urlString: String, source: AppDeepLinkSource = .external) -> Bool {
@@ -78,6 +130,35 @@ enum AppDeepLinkHandler {
guard postId > 0 else { return }
AppState.shared.setAppStep(step: .creatorChannelCommunityPostDetail(postId: postId))
case .eventDetail(let eventId):
guard eventId > 0, navigatingEventId != eventId else { return }
navigatingEventId = eventId
AppState.shared.requestExternalNavigation(
navigationAction: {
guard navigatingEventId == eventId else { return }
AppState.shared.setAppStep(
step: .eventDetail(eventId: eventId),
ifCurrentStep: { currentStep in
guard navigatingEventId == eventId else { return false }
if case .eventDetail(let currentId) = currentStep {
return currentId != eventId
}
return true
},
completion: {
if navigatingEventId == eventId {
navigatingEventId = nil
}
}
)
},
cancelAction: {
if navigatingEventId == eventId {
navigatingEventId = nil
}
}
)
case .message:
AppState.shared.setAppStep(step: .message)
@@ -86,6 +167,26 @@ enum AppDeepLinkHandler {
}
}
private static func isOneLink(_ url: URL) -> Bool {
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
return false
}
return components.scheme?.lowercased() == "https"
&& components.host?.lowercased() == "voiceon.onelink.me"
&& components.user == nil && components.password == nil
&& (components.port == nil || components.port == 443)
}
private static func eventId(_ identifier: String?) -> Int? {
guard let identifier = identifier, !identifier.isEmpty,
identifier.utf8.allSatisfy({ $0 >= 48 && $0 <= 57 }),
let value = Int64(identifier), value > 0 else {
return nil
}
return Int(exactly: value)
}
private static func isSupportedScheme(_ url: URL) -> Bool {
guard let scheme = url.scheme?.lowercased() else {
return false
@@ -100,6 +201,16 @@ enum AppDeepLinkHandler {
let host = components?.host?.lowercased() ?? ""
let pathRoute = url.pathComponents.first { $0 != "/" && !$0.isEmpty }?.lowercased()
if host == "event-detail" || (host.isEmpty && pathRoute == "event-detail") {
let scheme = url.scheme?.lowercased()
guard scheme == "voiceon" || scheme == "voiceon-test" else { return nil }
let path = url.pathComponents.filter { $0 != "/" && !$0.isEmpty }
guard path.count == (host.isEmpty ? 2 : 1), let id = eventId(path.last) else {
return nil
}
return .eventDetail(eventId: id)
}
if host == "community" || (host.isEmpty && pathRoute == "community") {
return parsePathStyle(url: url, components: components)
}