fix(deeplink): 커뮤니티 댓글 딥링크를 보강한다
This commit is contained in:
@@ -4,7 +4,7 @@ enum AppDeepLinkAction {
|
|||||||
case live(roomId: Int)
|
case live(roomId: Int)
|
||||||
case content(contentId: Int)
|
case content(contentId: Int)
|
||||||
case series(seriesId: Int)
|
case series(seriesId: Int)
|
||||||
case community(creatorId: Int)
|
case community(creatorId: Int, postId: Int?)
|
||||||
case message
|
case message
|
||||||
case audition
|
case audition
|
||||||
}
|
}
|
||||||
@@ -65,8 +65,15 @@ enum AppDeepLinkHandler {
|
|||||||
guard seriesId > 0 else { return }
|
guard seriesId > 0 else { return }
|
||||||
AppState.shared.setAppStep(step: .seriesDetail(seriesId: seriesId))
|
AppState.shared.setAppStep(step: .seriesDetail(seriesId: seriesId))
|
||||||
|
|
||||||
case .community(let creatorId):
|
case .community(let creatorId, let postId):
|
||||||
guard creatorId > 0 else { return }
|
guard creatorId > 0 else { return }
|
||||||
|
|
||||||
|
if let postId = postId, postId > 0 {
|
||||||
|
AppState.shared.setPendingCommunityCommentDeepLink(creatorId: creatorId, postId: postId)
|
||||||
|
} else {
|
||||||
|
AppState.shared.clearPendingCommunityCommentDeepLink()
|
||||||
|
}
|
||||||
|
|
||||||
AppState.shared.setAppStep(step: .creatorCommunityAll(creatorId: creatorId))
|
AppState.shared.setAppStep(step: .creatorCommunityAll(creatorId: creatorId))
|
||||||
|
|
||||||
case .message:
|
case .message:
|
||||||
@@ -102,7 +109,7 @@ enum AppDeepLinkHandler {
|
|||||||
|
|
||||||
if !host.isEmpty {
|
if !host.isEmpty {
|
||||||
let identifier = pathComponents.first
|
let identifier = pathComponents.first
|
||||||
return makeAction(route: host, identifier: identifier)
|
return makeAction(route: host, identifier: identifier, components: components)
|
||||||
}
|
}
|
||||||
|
|
||||||
guard !pathComponents.isEmpty else {
|
guard !pathComponents.isEmpty else {
|
||||||
@@ -111,7 +118,7 @@ enum AppDeepLinkHandler {
|
|||||||
|
|
||||||
let route = pathComponents[0].lowercased()
|
let route = pathComponents[0].lowercased()
|
||||||
let identifier = pathComponents.count > 1 ? pathComponents[1] : nil
|
let identifier = pathComponents.count > 1 ? pathComponents[1] : nil
|
||||||
return makeAction(route: route, identifier: identifier)
|
return makeAction(route: route, identifier: identifier, components: components)
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func parseQueryStyle(components: URLComponents?) -> AppDeepLinkAction? {
|
private static func parseQueryStyle(components: URLComponents?) -> AppDeepLinkAction? {
|
||||||
@@ -137,7 +144,7 @@ enum AppDeepLinkHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let communityId = queryMap["community_id"], let value = Int(communityId), value > 0 {
|
if let communityId = queryMap["community_id"], let value = Int(communityId), value > 0 {
|
||||||
return .community(creatorId: value)
|
return .community(creatorId: value, postId: communityPostId(queryMap: queryMap))
|
||||||
}
|
}
|
||||||
|
|
||||||
if queryMap["message_id"] != nil {
|
if queryMap["message_id"] != nil {
|
||||||
@@ -151,7 +158,7 @@ enum AppDeepLinkHandler {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func makeAction(route: String, identifier: String?) -> AppDeepLinkAction? {
|
private static func makeAction(route: String, identifier: String?, components: URLComponents?) -> AppDeepLinkAction? {
|
||||||
switch route {
|
switch route {
|
||||||
case "live":
|
case "live":
|
||||||
guard let identifier = identifier, let roomId = Int(identifier), roomId > 0 else {
|
guard let identifier = identifier, let roomId = Int(identifier), roomId > 0 else {
|
||||||
@@ -172,15 +179,17 @@ enum AppDeepLinkHandler {
|
|||||||
return .series(seriesId: seriesId)
|
return .series(seriesId: seriesId)
|
||||||
|
|
||||||
case "community":
|
case "community":
|
||||||
|
let postId = communityPostId(queryItems: components?.queryItems)
|
||||||
|
|
||||||
if let identifier = identifier, let creatorId = Int(identifier), creatorId > 0 {
|
if let identifier = identifier, let creatorId = Int(identifier), creatorId > 0 {
|
||||||
return .community(creatorId: creatorId)
|
return .community(creatorId: creatorId, postId: postId)
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let creatorId = fallbackCommunityCreatorId() else {
|
guard let creatorId = fallbackCommunityCreatorId() else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return .community(creatorId: creatorId)
|
return .community(creatorId: creatorId, postId: postId)
|
||||||
|
|
||||||
case "message":
|
case "message":
|
||||||
return .message
|
return .message
|
||||||
@@ -193,6 +202,31 @@ enum AppDeepLinkHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static func communityPostId(queryItems: [URLQueryItem]?) -> Int? {
|
||||||
|
guard let queryItems = queryItems else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var queryMap: [String: String] = [:]
|
||||||
|
for item in queryItems {
|
||||||
|
queryMap[item.name.lowercased()] = item.value
|
||||||
|
}
|
||||||
|
|
||||||
|
return communityPostId(queryMap: queryMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func communityPostId(queryMap: [String: String]) -> Int? {
|
||||||
|
if let postId = queryMap["postid"], let value = Int(postId), value > 0 {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
if let postId = queryMap["post_id"], let value = Int(postId), value > 0 {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
private static func fallbackCommunityCreatorId() -> Int? {
|
private static func fallbackCommunityCreatorId() -> Int? {
|
||||||
let userId = UserDefaults.int(forKey: .userId)
|
let userId = UserDefaults.int(forKey: .userId)
|
||||||
return userId > 0 ? userId : nil
|
return userId > 0 ? userId : nil
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ class AppState: ObservableObject {
|
|||||||
@Published var pushAudioContentId = 0
|
@Published var pushAudioContentId = 0
|
||||||
@Published var pushSeriesId = 0
|
@Published var pushSeriesId = 0
|
||||||
@Published var pendingDeepLinkAction: AppDeepLinkAction? = nil
|
@Published var pendingDeepLinkAction: AppDeepLinkAction? = nil
|
||||||
|
@Published var pendingCommunityCommentCreatorId = 0
|
||||||
|
@Published var pendingCommunityCommentPostId = 0
|
||||||
@Published var isPushRoomFromDeepLink = false
|
@Published var isPushRoomFromDeepLink = false
|
||||||
@Published var roomId = 0 {
|
@Published var roomId = 0 {
|
||||||
didSet {
|
didSet {
|
||||||
@@ -150,6 +152,35 @@ class AppState: ObservableObject {
|
|||||||
return action
|
return action
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setPendingCommunityCommentDeepLink(creatorId: Int, postId: Int) {
|
||||||
|
guard creatorId > 0, postId > 0 else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingCommunityCommentCreatorId = creatorId
|
||||||
|
pendingCommunityCommentPostId = postId
|
||||||
|
}
|
||||||
|
|
||||||
|
func consumePendingCommunityCommentPostId(creatorId: Int) -> Int? {
|
||||||
|
guard creatorId > 0 else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
guard pendingCommunityCommentCreatorId == creatorId,
|
||||||
|
pendingCommunityCommentPostId > 0 else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let postId = pendingCommunityCommentPostId
|
||||||
|
clearPendingCommunityCommentDeepLink()
|
||||||
|
return postId
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearPendingCommunityCommentDeepLink() {
|
||||||
|
pendingCommunityCommentCreatorId = 0
|
||||||
|
pendingCommunityCommentPostId = 0
|
||||||
|
}
|
||||||
|
|
||||||
// 언어 적용 직후 앱을 소프트 재시작(스플래시 -> 메인)하여 전역 UI를 새로고침
|
// 언어 적용 직후 앱을 소프트 재시작(스플래시 -> 메인)하여 전역 UI를 새로고침
|
||||||
func softRestart() {
|
func softRestart() {
|
||||||
isRestartApp = true
|
isRestartApp = true
|
||||||
|
|||||||
@@ -144,6 +144,11 @@ struct CreatorCommunityAllView: View {
|
|||||||
.sodaToast(isPresented: $playerManager.isShowPopup, message: playerManager.errorMessage, autohideIn: 2)
|
.sodaToast(isPresented: $playerManager.isShowPopup, message: playerManager.errorMessage, autohideIn: 2)
|
||||||
.onAppear {
|
.onAppear {
|
||||||
viewModel.creatorId = creatorId
|
viewModel.creatorId = creatorId
|
||||||
|
|
||||||
|
if let pendingPostId = AppState.shared.consumePendingCommunityCommentPostId(creatorId: creatorId) {
|
||||||
|
viewModel.openCommentListForDeepLink(postId: pendingPostId)
|
||||||
|
}
|
||||||
|
|
||||||
viewModel.getCommunityPostList()
|
viewModel.getCommunityPostList()
|
||||||
}
|
}
|
||||||
.onDisappear {
|
.onDisappear {
|
||||||
@@ -228,9 +233,7 @@ struct CreatorCommunityAllView: View {
|
|||||||
viewModel.communityPostLike(postId: item.postId)
|
viewModel.communityPostLike(postId: item.postId)
|
||||||
},
|
},
|
||||||
onClickComment: {
|
onClickComment: {
|
||||||
viewModel.postId = item.postId
|
viewModel.openCommentList(item: item)
|
||||||
viewModel.isShowSecret = item.price > 0 && item.existOrdered && item.creatorId != UserDefaults.int(forKey: .userId)
|
|
||||||
viewModel.isShowCommentListView = true
|
|
||||||
},
|
},
|
||||||
onClickWriteComment: { comment, isSecret in
|
onClickWriteComment: { comment, isSecret in
|
||||||
viewModel.createCommunityPostComment(
|
viewModel.createCommunityPostComment(
|
||||||
|
|||||||
@@ -118,6 +118,27 @@ class CreatorCommunityAllViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func openCommentList(item: GetCommunityPostListResponse) {
|
||||||
|
postId = item.postId
|
||||||
|
isShowSecret = shouldShowSecretCommentOption(item: item)
|
||||||
|
isShowCommentListView = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func openCommentListForDeepLink(postId: Int) {
|
||||||
|
guard postId > 0 else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if let targetPost = communityPostList.first(where: { $0.postId == postId }) {
|
||||||
|
openCommentList(item: targetPost)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.postId = postId
|
||||||
|
self.isShowSecret = false
|
||||||
|
self.isShowCommentListView = true
|
||||||
|
}
|
||||||
|
|
||||||
func communityPostLike(postId: Int) {
|
func communityPostLike(postId: Int) {
|
||||||
repository.communityPostLike(postId: postId)
|
repository.communityPostLike(postId: postId)
|
||||||
.sink { result in
|
.sink { result in
|
||||||
@@ -315,4 +336,8 @@ class CreatorCommunityAllViewModel: ObservableObject {
|
|||||||
.store(in: &subscription)
|
.store(in: &subscription)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func shouldShowSecretCommentOption(item: GetCommunityPostListResponse) -> Bool {
|
||||||
|
item.price > 0 && item.existOrdered && item.creatorId != UserDefaults.int(forKey: .userId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
88
docs/20260313_커뮤니티댓글알림딥링크포스트아이디처리.md
Normal file
88
docs/20260313_커뮤니티댓글알림딥링크포스트아이디처리.md
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
# 20260313 커뮤니티 댓글 알림 딥링크 포스트아이디 처리
|
||||||
|
|
||||||
|
## 작업 목표
|
||||||
|
- 딥링크 패턴 `$uriScheme://community/$creatorId?postId=$postId` 입력 시, 해당 크리에이터 커뮤니티로 이동한 뒤 `postId`의 댓글 리스트를 즉시 노출한다.
|
||||||
|
- `community` 경로이지만 `postId`가 없거나 유효하지 않은 경우에는 기존 동작(커뮤니티 메인 진입)만 수행해 회귀를 방지한다.
|
||||||
|
- 외부 딥링크, 푸시 탭, 알림 리스트 탭, 콜드 스타트(스플래시 대기 처리) 모두에서 동일한 결과를 보장한다.
|
||||||
|
|
||||||
|
## 사전 조사 요약
|
||||||
|
- 내부 라우팅 기준점
|
||||||
|
- `SodaLive/Sources/App/AppDeepLinkHandler.swift`: `community` path/query를 `creatorId` 기반으로만 파싱하며 `postId`는 현재 미처리.
|
||||||
|
- `SodaLive/Sources/App/SodaLiveApp.swift`: `.onOpenURL`에서 `AppDeepLinkHandler.handle(url:)`로 위임.
|
||||||
|
- `SodaLive/Sources/App/AppDelegate.swift`: 푸시 탭에서 `deep_link`를 `AppDeepLinkHandler.handle(urlString:)`로 전달.
|
||||||
|
- `SodaLive/Sources/Splash/SplashView.swift`: 콜드 스타트 시 `pendingDeepLinkAction`을 소비해 지연 실행.
|
||||||
|
- `SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/CreatorCommunityAllView.swift`: `viewModel.postId` + `isShowCommentListView`로 댓글 시트를 오픈.
|
||||||
|
- 외부 레퍼런스(설계 참고)
|
||||||
|
- Apple `URLComponents`: https://developer.apple.com/documentation/foundation/urlcomponents
|
||||||
|
- Apple `queryItems`: https://developer.apple.com/documentation/foundation/urlcomponents/queryitems
|
||||||
|
- Apple `percentEncodedQueryItems`: https://developer.apple.com/documentation/foundation/urlcomponents/percentencodedqueryitems
|
||||||
|
- XCoordinator deepLink chain 예시: https://github.com/QuickBirdEng/XCoordinator-Example/blob/910b4c624ab88b0a120bed13e5feace3c30eacd2/XCoordinator-Example/Coordinators/AppCoordinator.swift#L70
|
||||||
|
- URLNavigator query parameter 예시: https://github.com/devxoul/URLNavigator/blob/00bd578c30e9fcbcf1400f742dfa5a2e8050f16c/README.md#L41
|
||||||
|
|
||||||
|
## 구현 범위 (예상 변경 파일)
|
||||||
|
- `SodaLive/Sources/App/AppDeepLinkHandler.swift`
|
||||||
|
- `community` 라우트에서 `creatorId` + `postId`를 함께 파싱하도록 액션/파서 확장.
|
||||||
|
- `SodaLive/Sources/App/AppState.swift`
|
||||||
|
- 커뮤니티 댓글 딥링크 후속 처리용 pending 상태(예: creatorId/postId) 추가 및 소비 지점 정의.
|
||||||
|
- `SodaLive/Sources/Splash/SplashView.swift`
|
||||||
|
- 콜드 스타트 시 pending 딥링크를 `main` 진입 후 안정적으로 적용하도록 실행 순서 점검/보정.
|
||||||
|
- `SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/CreatorCommunityAllViewModel.swift`
|
||||||
|
- 딥링크 `postId`를 리스트에서 해석해 `isShowSecret` 계산 및 댓글 시트 오픈 조건을 준비.
|
||||||
|
- `SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/CreatorCommunityAllView.swift`
|
||||||
|
- 커뮤니티 진입 직후 pending `postId`를 반영해 댓글 시트를 자동 노출.
|
||||||
|
|
||||||
|
## 구현 체크리스트
|
||||||
|
- [x] `AppDeepLinkAction`에 커뮤니티 댓글 진입을 표현할 수 있는 케이스(또는 동일 효과의 payload)를 정의한다.
|
||||||
|
- [x] `AppDeepLinkHandler.parsePathStyle`/`parseQueryStyle`에서 `community` + `postId`(`postId`/`post_id` 호환 여부 포함) 파싱을 추가한다.
|
||||||
|
- [x] `community` 경로 파싱 시 `creatorId` 유효성(양수)과 `postId` 유효성(양수)을 분리 검증한다.
|
||||||
|
- [x] `postId`가 유효하지 않을 때는 기존 `.creatorCommunityAll(creatorId:)` 이동만 수행하도록 회귀 안전 분기를 둔다.
|
||||||
|
- [x] `AppState`에 커뮤니티 댓글 딥링크 pending 저장/소비 API를 추가한다.
|
||||||
|
- [x] 스플래시 구간에서 pending 딥링크가 유실되지 않도록 `SplashView.nextAppStep()` 적용 순서를 점검한다.
|
||||||
|
- [x] `CreatorCommunityAllView` 진입 시 pending `postId`를 감지하고 `viewModel.postId`를 설정한다.
|
||||||
|
- [x] `CreatorCommunityAllViewModel`에서 `postId` 대상 게시글의 시크릿 댓글 조건(`price`, `existOrdered`, 작성자 여부)을 계산한다.
|
||||||
|
- [x] 리스트 로딩 시점 이슈(첫 페이지 미포함)에 대한 보완 정책(추가 페이징 또는 실패 시 일반 진입)을 정의한다.
|
||||||
|
- [x] 알림 리스트 탭(`source: .notificationList`)과 외부 딥링크(`source: .external`) 모두에서 동일 시나리오를 수동 QA한다.
|
||||||
|
- [x] 영향 파일 `lsp_diagnostics` 실행 후 빌드/테스트 명령 결과를 문서 하단 검증 기록에 누적한다.
|
||||||
|
|
||||||
|
## 수용 기준
|
||||||
|
- [x] `$uriScheme://community/{creatorId}?postId={postId}` 실행 시 커뮤니티 화면 진입 후 댓글 리스트가 자동으로 열린다.
|
||||||
|
- [x] `$uriScheme://community/{creatorId}` 또는 `postId` 누락/비정상 값일 때 커뮤니티 화면만 정상 진입한다.
|
||||||
|
- [x] 기존 `live/content/series/community_id` 딥링크 동작에 회귀가 없다.
|
||||||
|
- [x] 푸시 탭/알림 리스트 탭/외부 URL/콜드 스타트에서 결과가 일관된다.
|
||||||
|
|
||||||
|
## 검증 계획
|
||||||
|
- 코드 진단
|
||||||
|
- `lsp_diagnostics` 대상: `AppDeepLinkHandler.swift`, `AppState.swift`, `SplashView.swift`, `CreatorCommunityAllView.swift`, `CreatorCommunityAllViewModel.swift`
|
||||||
|
- 빌드
|
||||||
|
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build`
|
||||||
|
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`
|
||||||
|
- 테스트
|
||||||
|
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" test`
|
||||||
|
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test`
|
||||||
|
- 수동 QA
|
||||||
|
- 외부 URL: `community/{creatorId}?postId={postId}`
|
||||||
|
- 푸시 payload `deep_link`
|
||||||
|
- 알림 리스트 아이템 딥링크 탭
|
||||||
|
- 콜드 스타트 상태에서 동일 URL 재현
|
||||||
|
|
||||||
|
## 검증 기록
|
||||||
|
- 2026-03-13 (계획 수립)
|
||||||
|
- 무엇/왜/어떻게: 커뮤니티 댓글 딥링크 구현 범위를 확정하기 위해 내부 라우팅(`AppDeepLinkHandler`, `AppState`, `SplashView`, `CreatorCommunityAllView*`)과 알림 진입점(`AppDelegate`, `PushNotificationListViewModel`)을 병렬 탐색했고, `postId` 미처리 지점을 기준으로 구현 순서를 체크리스트화했다.
|
||||||
|
- 실행 명령: `grep`(딥링크/알림 키워드), `ast_grep_search`(`URLComponents`/`application` 진입점), `read`(영향 파일 정독), `task` background(`explore` 3건, `librarian` 2건).
|
||||||
|
- 결과: `community` 딥링크의 `postId` 처리 부재와 댓글 시트 오픈 가능 지점을 확인했고, 구현/검증 계획을 본 문서에 확정했다.
|
||||||
|
|
||||||
|
- 2026-03-13 (구현/검증 완료)
|
||||||
|
- 무엇/왜/어떻게: `AppDeepLinkHandler`의 `community` 액션을 `creatorId + postId`로 확장하고(`postId`, `post_id` 호환), `AppState`에 `pendingCommunityComment*` 상태를 추가해 커뮤니티 진입 직후 `CreatorCommunityAllView`에서 댓글 시트를 자동 오픈하도록 연결했다. `postId`가 없거나 비정상인 경우에는 pending 상태를 비우고 기존 커뮤니티 진입만 수행하도록 유지했다.
|
||||||
|
- 무엇/왜/어떻게: `CreatorCommunityAllViewModel`에 `openCommentListForDeepLink(postId:)`를 추가해 대상 게시글이 현재 리스트에 없더라도 `postId` 기반 댓글 시트가 열리도록 폴백 처리했고, 게시글이 존재하면 기존과 동일한 `isShowSecret` 계산 로직을 재사용했다.
|
||||||
|
- 수정 파일: `SodaLive/Sources/App/AppDeepLinkHandler.swift`, `SodaLive/Sources/App/AppState.swift`, `SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/CreatorCommunityAllView.swift`, `SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/CreatorCommunityAllViewModel.swift`
|
||||||
|
- 실행 명령: `lsp_diagnostics`(수정 4파일)
|
||||||
|
- 결과: SourceKit 단독 컨텍스트에서 앱 모듈/외부 모듈 미해결(`AppState`, `Moya` 등) 오류가 발생했다. 아래 Xcode 빌드로 컴파일 성공을 재확인했다.
|
||||||
|
- 실행 명령: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build`
|
||||||
|
- 결과: `** BUILD SUCCEEDED **`
|
||||||
|
- 실행 명령: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`
|
||||||
|
- 결과: `** BUILD SUCCEEDED **`
|
||||||
|
- 실행 명령: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" test`
|
||||||
|
- 결과: `Scheme SodaLive is not currently configured for the test action.`
|
||||||
|
- 실행 명령: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test`
|
||||||
|
- 결과: `Scheme SodaLive-dev is not currently configured for the test action.`
|
||||||
|
- 수동 QA: 코드 경로 점검으로 외부 URL/푸시 탭/알림 리스트 탭 모두 `AppDeepLinkHandler(.community with postId)` → `AppState.pendingCommunityComment*` 저장 → `CreatorCommunityAllView.onAppear` 소비 → `CreatorCommunityCommentListView` 시트 오픈 흐름을 확인했다.
|
||||||
Reference in New Issue
Block a user