Conversation
There was a problem hiding this comment.
Pull request overview
Implements a new post-login “initial setup” flow (policy agreement → name/tag setup → tutorial) driven by /users/init-settings, and adds an in-app update prompt (force/optional) based on server response.
Changes:
- Added
InitialSetupFlowViewModelplus a full set of setup/tutorial SwiftUI screens, wired via a new.setupapp flow step. - Updated
AppViewModel/RootFlowViewto resolve post-login routing asynchronously and present an update alert (force/optional) + loading overlay. - Extended
UserService/models to support init-settings fetch, policy agreement submission, and username update; added tests for validation and routing.
Reviewed changes
Copilot reviewed 12 out of 13 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| KillingPartTests/InitialSetupFlowTests.swift | Adds coverage for name/tag validation and post-login route/update prompt behavior. |
| KillingPart/ViewModels/InitialSetupFlowViewModel.swift | New setup flow state machine + validation + tutorial data loading. |
| KillingPart/Views/Screens/Setup/InitialSetupFlowView.swift | Hosts step-based setup flow UI with transitions. |
| KillingPart/Views/Screens/Setup/components/PolicyAgreementScreen.swift | New policy agreement UI + policy document sheet rendering. |
| KillingPart/Views/Screens/Setup/components/NameSetupScreen.swift | New name entry screen for setup. |
| KillingPart/Views/Screens/Setup/components/TagSetupScreen.swift | New tag entry screen for setup. |
| KillingPart/Views/Screens/Setup/components/InitialSetupSkipButtonRow.swift | Shared “skip” header component for setup/tutorial screens. |
| KillingPart/Views/Screens/Setup/components/TutorialChoiceScreen.swift | Tutorial entry decision screen (add first killing part vs skip). |
| KillingPart/Views/Screens/Setup/components/TutorialTrackSearchScreen.swift | Tutorial track search UI using existing add-search VM. |
| KillingPart/Views/Screens/Setup/components/TutorialTrimScreen.swift | Tutorial trim flow wrapper around AddSearchDetailView. |
| KillingPart/Views/Screens/Setup/components/TutorialHomeScreen.swift | Tutorial “My” home mock showing collection/calendar with fetched data. |
| KillingPart/Views/Screens/Setup/components/TutorialDiaryDetailScreen.swift | Tutorial diary detail “preview” screen. |
| KillingPart/Views/Screens/Setup/components/TutorialNotificationScreen.swift | Tutorial notification mock screen. |
| KillingPart/Views/Screens/Setup/components/TutorialFinalScreen.swift | Tutorial completion screen. |
| KillingPart/ViewModels/AppViewModel.swift | Adds post-login flow resolution, update prompt handling, and setup flow routing. |
| KillingPart/Views/Screens/RootFlowView.swift | Integrates .setup flow, update alert, and resolving overlay. |
| KillingPart/Services/UserService.swift | Adds init-settings fetch, policy agreement submit, and username update endpoints. |
| KillingPart/Models/UserModel.swift | Adds request/response models for init-settings + policy agreements + username update. |
| KillingPart/Models/AppFlowStep.swift | Introduces .setup step (removes onboarding). |
| KillingPart/Views/Screens/Main/Add/AddSearchDetail/AddSearchDetailView.swift | Adds tutorial-specific UI affordances (skip button + trim focus behavior). |
| KillingPart/Views/Screens/Main/Add/AddSearchDetail/components/AddSearchDetailVideoCandidateSection.swift | Tweaks candidate section default expanded state + chevron styling. |
| KillingPart/Views/Screens/Main/My/MyCollection/Components/ProfileCard/MyCollectionProfileCard.swift | Makes edit button optional to reuse card in tutorial context. |
| KillingPart/Views/Screens/Main/My/MusicCalendar/components/MusicCalendarCalendarSection.swift | Refactors layout/background to a simpler container structure. |
| KillingPart/Info.plist | Adds APP_STORE_URL config entry. |
| KillingPart.xcodeproj/project.pbxproj | Bumps build/marketing versions. |
| .claude/settings.local.json | Adds local Claude tool settings file (should not be committed). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| guard | ||
| let raw = viewModel.tutorialDiaries.first?.profileImageUrl?.trimmingCharacters(in: .whitespacesAndNewlines), | ||
| !raw.isEmpty | ||
| else { | ||
| return nil | ||
| } | ||
| if let parsed = URL(string: raw), parsed.scheme != nil { | ||
| return parsed | ||
| } | ||
| if raw.hasPrefix("//"), let parsed = URL(string: "https:\(raw)") { | ||
| return parsed | ||
| } | ||
| return URL(string: "https://\(raw)") |
There was a problem hiding this comment.
profileImageURL re-implements profile image URL normalization. The codebase already has resolvedProfileImageURL(from:) (used by UserModel/SubscribeUserModel) which also handles http→https and percent-encoding. Please reuse that helper here to keep URL handling consistent and avoid edge cases.
| guard | |
| let raw = viewModel.tutorialDiaries.first?.profileImageUrl?.trimmingCharacters(in: .whitespacesAndNewlines), | |
| !raw.isEmpty | |
| else { | |
| return nil | |
| } | |
| if let parsed = URL(string: raw), parsed.scheme != nil { | |
| return parsed | |
| } | |
| if raw.hasPrefix("//"), let parsed = URL(string: "https:\(raw)") { | |
| return parsed | |
| } | |
| return URL(string: "https://\(raw)") | |
| resolvedProfileImageURL(from: viewModel.tutorialDiaries.first?.profileImageUrl) |
| 가. 개인정보 보호 책임자 | ||
|
|
||
| 1) 성명: 김기영 | ||
|
|
||
| 2) 직책: PO | ||
|
|
||
| 3) 전화번호: 010-8831-8775 |
There was a problem hiding this comment.
PolicyDocumentContent.privacyPolicy hardcodes a specific person’s name/phone number as the privacy officer contact. Please avoid embedding personal contact info directly in the app binary/source (privacy/PII concern); prefer an official support channel, a generic placeholder, or loading the contact details from a managed configuration/source.
| 가. 개인정보 보호 책임자 | |
| 1) 성명: 김기영 | |
| 2) 직책: PO | |
| 3) 전화번호: 010-8831-8775 | |
| 가. 개인정보 보호 관련 문의처 | |
| 1) 부서명: 개인정보 보호 담당부서 | |
| 2) 직책: 개인정보 보호 담당자 | |
| 3) 연락처: 공식 고객지원 채널을 이용해 주시기 바랍니다. |
| enum PolicyDocumentContent { | ||
| static let privacyPolicy = """ | ||
| 제1조(목적) | ||
| 킬링파트(이하 '회사'라고 함)는 회사가 제공하고자 하는 서비스(이하 '회사 서비스')를 이용하는 개인(이하 '이용자' 또는 '개인')의 정보(이하 '개인정보')를 보호하기 위해, 개인정보보호법, 정보통신망 이용촉진 및 정보보호 등에 관한 법률(이하 '정보통신망법') 등 관련 법령을 준수하고, 서비스 이용자의 개인정보 보호 관련한 고충을 신속하고 원활하게 처리할 수 있도록 하기 위하여 다음과 같이 개인정보처리방침(이하 '본 방침')을 수립합니다. | ||
|
|
There was a problem hiding this comment.
PolicyDocumentContent embeds very large legal documents directly in a Swift source file, which makes this screen hard to maintain/review and can increase compile times. Consider moving the policy/terms text to bundled resources (e.g. .txt/.md) or a dedicated localized resource loader so the rendering/parsing code stays separate from the content.
| .scrollIndicators(.hidden) | ||
| } | ||
| } | ||
| .dynamicTypeSize(.xSmall ... .large) |
There was a problem hiding this comment.
This view caps Dynamic Type to .xSmall ... .large, which prevents users who rely on larger accessibility text sizes from using the screen comfortably. Please consider removing this cap or supporting up to the accessibility sizes (and adjusting layout as needed) to meet accessibility expectations.
| .dynamicTypeSize(.xSmall ... .large) |
| .font(AppFont.paperlogy4Regular(size: 14)) | ||
| .foregroundStyle(.white) | ||
|
|
||
| Text("*한글 8자 이내, 영어 16자 이내(숫자 포함)") |
There was a problem hiding this comment.
The helper text says "*한글 8자 이내, 영어 16자 이내(숫자 포함)", but InitialSetupFlowViewModel.validateName currently allows up to 20 characters and permits spaces/mixed scripts. Please align the UI guidance and the actual validation rules (either update the text or adjust validateName to match the stated limits).
| Text("*한글 8자 이내, 영어 16자 이내(숫자 포함)") | |
| Text("*이름은 최대 20자까지 입력할 수 있습니다") |
변경점 👍
새로 구현한 기능 및 주요 변경점
버그 해결 💊
해결한 버그
테스트 💻
변경점을 테스트하기 위한 방법 기술
스크린샷 🖼
변경된 부분에 대한 스크린샷
비고 ✏
리뷰어에게 전하는 말 등
사용하지 않은 항목은 모두 지워주세요.