feat(notification): 알림함 진입 및 딥링크 라우팅을 추가한다
This commit is contained in:
190
SodaLive/Sources/App/AppDeepLinkHandler.swift
Normal file
190
SodaLive/Sources/App/AppDeepLinkHandler.swift
Normal file
@@ -0,0 +1,190 @@
|
||||
import Foundation
|
||||
|
||||
enum AppDeepLinkAction {
|
||||
case live(roomId: Int)
|
||||
case content(contentId: Int)
|
||||
case series(seriesId: Int)
|
||||
case community(creatorId: Int)
|
||||
case message
|
||||
case audition
|
||||
}
|
||||
|
||||
enum AppDeepLinkHandler {
|
||||
static func handle(url: URL) -> Bool {
|
||||
guard isSupportedScheme(url) else {
|
||||
return false
|
||||
}
|
||||
|
||||
guard let action = parseAction(url: url) else {
|
||||
return false
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
if case .splash = AppState.shared.rootStep {
|
||||
AppState.shared.pendingDeepLinkAction = action
|
||||
return
|
||||
}
|
||||
|
||||
apply(action: action)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
static func handle(urlString: String) -> Bool {
|
||||
let trimmed = urlString.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard let url = URL(string: trimmed) else {
|
||||
return false
|
||||
}
|
||||
|
||||
return handle(url: url)
|
||||
}
|
||||
|
||||
static func apply(action: AppDeepLinkAction) {
|
||||
switch action {
|
||||
case .live(let roomId):
|
||||
guard roomId > 0 else { return }
|
||||
AppState.shared.pushRoomId = 0
|
||||
AppState.shared.pushRoomId = roomId
|
||||
|
||||
case .content(let contentId):
|
||||
guard contentId > 0 else { return }
|
||||
AppState.shared.setAppStep(step: .contentDetail(contentId: contentId))
|
||||
|
||||
case .series(let seriesId):
|
||||
guard seriesId > 0 else { return }
|
||||
AppState.shared.setAppStep(step: .seriesDetail(seriesId: seriesId))
|
||||
|
||||
case .community(let creatorId):
|
||||
guard creatorId > 0 else { return }
|
||||
AppState.shared.setAppStep(step: .creatorCommunityAll(creatorId: creatorId))
|
||||
|
||||
case .message:
|
||||
AppState.shared.setAppStep(step: .message)
|
||||
|
||||
case .audition:
|
||||
AppState.shared.setAppStep(step: .audition)
|
||||
}
|
||||
}
|
||||
|
||||
private static func isSupportedScheme(_ url: URL) -> Bool {
|
||||
guard let scheme = url.scheme?.lowercased() else {
|
||||
return false
|
||||
}
|
||||
|
||||
let appScheme = APPSCHEME.lowercased()
|
||||
return scheme == appScheme || scheme == "voiceon" || scheme == "voiceon-test"
|
||||
}
|
||||
|
||||
private static func parseAction(url: URL) -> AppDeepLinkAction? {
|
||||
let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
|
||||
|
||||
if let routeAction = parsePathStyle(url: url, components: components) {
|
||||
return routeAction
|
||||
}
|
||||
|
||||
return parseQueryStyle(components: components)
|
||||
}
|
||||
|
||||
private static func parsePathStyle(url: URL, components: URLComponents?) -> AppDeepLinkAction? {
|
||||
let pathComponents = url.pathComponents.filter { $0 != "/" && !$0.isEmpty }
|
||||
let host = components?.host?.lowercased() ?? ""
|
||||
|
||||
if !host.isEmpty {
|
||||
let identifier = pathComponents.first
|
||||
return makeAction(route: host, identifier: identifier)
|
||||
}
|
||||
|
||||
guard !pathComponents.isEmpty else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let route = pathComponents[0].lowercased()
|
||||
let identifier = pathComponents.count > 1 ? pathComponents[1] : nil
|
||||
return makeAction(route: route, identifier: identifier)
|
||||
}
|
||||
|
||||
private static func parseQueryStyle(components: URLComponents?) -> AppDeepLinkAction? {
|
||||
guard let queryItems = components?.queryItems else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var queryMap: [String: String] = [:]
|
||||
for item in queryItems {
|
||||
queryMap[item.name.lowercased()] = item.value
|
||||
}
|
||||
|
||||
if let roomId = queryMap["room_id"], let value = Int(roomId), value > 0 {
|
||||
return .live(roomId: value)
|
||||
}
|
||||
|
||||
if let contentId = queryMap["content_id"], let value = Int(contentId), value > 0 {
|
||||
return .content(contentId: value)
|
||||
}
|
||||
|
||||
if let seriesId = queryMap["series_id"], let value = Int(seriesId), value > 0 {
|
||||
return .series(seriesId: value)
|
||||
}
|
||||
|
||||
if let communityId = queryMap["community_id"], let value = Int(communityId), value > 0 {
|
||||
return .community(creatorId: value)
|
||||
}
|
||||
|
||||
if queryMap["message_id"] != nil {
|
||||
return .message
|
||||
}
|
||||
|
||||
if queryMap["audition_id"] != nil {
|
||||
return .audition
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
private static func makeAction(route: String, identifier: String?) -> AppDeepLinkAction? {
|
||||
switch route {
|
||||
case "live":
|
||||
guard let identifier = identifier, let roomId = Int(identifier), roomId > 0 else {
|
||||
return nil
|
||||
}
|
||||
return .live(roomId: roomId)
|
||||
|
||||
case "content":
|
||||
guard let identifier = identifier, let contentId = Int(identifier), contentId > 0 else {
|
||||
return nil
|
||||
}
|
||||
return .content(contentId: contentId)
|
||||
|
||||
case "series":
|
||||
guard let identifier = identifier, let seriesId = Int(identifier), seriesId > 0 else {
|
||||
return nil
|
||||
}
|
||||
return .series(seriesId: seriesId)
|
||||
|
||||
case "community":
|
||||
if let identifier = identifier, let creatorId = Int(identifier), creatorId > 0 {
|
||||
return .community(creatorId: creatorId)
|
||||
}
|
||||
|
||||
guard let creatorId = fallbackCommunityCreatorId() else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return .community(creatorId: creatorId)
|
||||
|
||||
case "message":
|
||||
return .message
|
||||
|
||||
case "audition":
|
||||
return .audition
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private static func fallbackCommunityCreatorId() -> Int? {
|
||||
let userId = UserDefaults.int(forKey: .userId)
|
||||
return userId > 0 ? userId : nil
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user