Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
c66a659
chore: #79 권한 설정 뷰컨트롤러 폴더링 수정
dev-domo Oct 24, 2025
3328ca3
refactor: #79 알림/위치 권한 설정 화면 이미지 교체 및 레이아웃 제약 수정
dev-domo Oct 24, 2025
74f12ff
setting: #79 깜빡임 문제 해결을 위한 로띠 파일 교체
dev-domo Oct 24, 2025
ca0ce12
refactor: #79 앱 초기 화면 설정에서 UINavigationController 제거
dev-domo Oct 24, 2025
d87eb9e
refactor: #79 홈 뷰컨트롤러 -> 내비게이션 컨트롤러 없이 단독 사용
dev-domo Oct 24, 2025
0f0a14c
refactor: #79 홈에서 선택한 날짜에 따라 텍스트필드 placeHolder가 달라지도록 수정
dev-domo Oct 26, 2025
b9e99e0
refactor: #79 과거/미래 날짜에 따른 말풍선 및 placeHolder 문구 변경
dev-domo Oct 26, 2025
8cf69db
refactor: #79 날짜에 따라 텍스트필드 상태 변경
dev-domo Oct 30, 2025
999457e
refactor: #79 달력을 현재 달 기준 앞뒤 한 달만 이동할 수 있도록 수정
dev-domo Oct 30, 2025
df46701
refactor: #79 requestDate 함수 분리
dev-domo Oct 30, 2025
47103f3
refactor: #79 캘린더 외 화면 딤 & 블러 처리
dev-domo Oct 30, 2025
84a3e33
refactor: #79 미션 추가 시 미션 텍스트필드를 비우도록 수정
dev-domo Oct 30, 2025
a5b3680
refactor: #79 시나리오, 메모, 미션 앞뒤 공백 제거
dev-domo Oct 30, 2025
b2722a5
refactor: #79 isBlank 속성 추가
dev-domo Oct 31, 2025
0906ef1
refactor: #79 미션 글자수 바인딩
dev-domo Oct 31, 2025
0fc3a31
refactor: #79 미션 텍스트필드 입력 시 플러스 버튼 색상 변경
dev-domo Oct 31, 2025
6bd22e1
chore: #79 푸시 알림 설정 제목/description 수정
dev-domo Oct 31, 2025
0a01e08
feat: #79 앱 종료 시 알림 호출
dev-domo Nov 4, 2025
84366a4
feat: #79 too many request 처리를 위한 모달 구현
dev-domo Nov 4, 2025
11f7622
refactor: #79 알림을 껐을 때 해당 알림의 시나리오가 탭되어있도록 수정
dev-domo Nov 4, 2025
8d08ed1
feat: #79 푸시 알람을 동의할 때 아이폰 설정 화면으로 넘어가도록 구현
dev-domo Nov 4, 2025
1d95879
refactor: #79 공지사항 뷰 추가
dev-domo Nov 4, 2025
35339f3
feat: #79 미션 개수 제한 시 토스트 메세지 띄우도록 구현
dev-domo Nov 4, 2025
08164a2
refactor: #79 수정한 닉네임이 이전 닉네임과 동일할 때 네트워킹 호출하지 않도록 수정
dev-domo Nov 4, 2025
58d5053
refactor: #79 이벤트 푸시 알람 추가
dev-domo Nov 4, 2025
4a87b01
refactor: #79 시나리오가 하나이거나 변경한 순서가 이전과 동일한 경우 처리
dev-domo Nov 4, 2025
40d9995
refactor: #79 미션 개수 제한 에러 정의
dev-domo Nov 4, 2025
2e19965
setting: #79 라이트 모드 설정
dev-domo Nov 4, 2025
98b879b
refactor: #79 메서드 분리
dev-domo Nov 4, 2025
5e04e0b
refactor: #79 미완료 미션, 오늘의 미션을 기준으로 missions 정렬
dev-domo Nov 4, 2025
dc3f8d4
style: #79 이미지 세팅
dev-domo Nov 4, 2025
d1f9f41
refactor: #79 컴포넌트 세팅 메서드 분리
dev-domo Nov 4, 2025
ae20421
refactor: #79 이벤트 푸시 알람 addTarget
dev-domo Nov 4, 2025
fcbcacd
refactor: #79 푸시 알림 버튼 클릭 시 로직 변경
dev-domo Nov 4, 2025
ee2e6a7
refactor: #79 tooManyRequest 에러 정의 및 네트워킹 분기처리
dev-domo Nov 4, 2025
124ab75
feat: #79 NetworkRequestErrorHandler 구현
dev-domo Nov 4, 2025
da51eaf
chore: #79 extension에서 프로토콜 채택
dev-domo Nov 4, 2025
7de7d05
refactor: #79 설정 뷰 스크롤뷰 제약 설정
dev-domo Nov 4, 2025
26dcbb5
refactor: #79 실제 앱 버전 정보 가져오기
dev-domo Nov 4, 2025
1c7db0f
refactor: #79 알람 화면에 시나리오명 반영
dev-domo Nov 4, 2025
50ccfcb
refactor: #79 푸시 알람 소리 설정(default)
dev-domo Nov 4, 2025
501f9e5
refactor: #79 공지사항 링크 연결
dev-domo Nov 4, 2025
b156dd3
chore: #79 로그인 뷰컨트롤러로 시작
dev-domo Nov 4, 2025
0539e65
chore: #79 불필요한 코드 제거
dev-domo Nov 4, 2025
6309ddc
refactor: #79 미션 개수 바인딩
dev-domo Nov 4, 2025
bda2d74
chore: #79 QA 반영
dev-domo Nov 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions BeforeGoing/Application/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
) -> UIInterfaceOrientationMask {
return .portrait
}

func applicationWillTerminate(_ application: UIApplication) {
Task {
await NotificationManager.shared.pushTerminateNotification()
}
}
}

9 changes: 6 additions & 3 deletions BeforeGoing/Application/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let windowScene = (scene as? UIWindowScene) else { return }

let splashViewController = ViewControllerFactory.shared.makeLoginViewController()
let navigationViewController = UINavigationController(rootViewController: splashViewController)
let loginViewController = ViewControllerFactory.shared.makeLoginViewController()

let window = UIWindow(windowScene: windowScene)
window.rootViewController = navigationViewController
window.rootViewController = loginViewController
window.makeKeyAndVisible()
self.window = window

Expand All @@ -50,6 +49,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func sceneDidBecomeActive(_ scene: UIScene) {
// Called when the scene has moved from an inactive state to an active state.
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
if let root = (window?.rootViewController as? UINavigationController)?
.viewControllers.first(where: { $0 is SettingViewController }) as? SettingViewController {
root.pushNoticeDidBecomeActive()
}
}

func sceneWillResignActive(_ scene: UIScene) {
Expand Down
2 changes: 2 additions & 0 deletions BeforeGoing/Core/BeforeGoingError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ enum BeforeGoingError: Error, Equatable {
case weatherServiceError
case unknownError
case loginExpired
case missionLimitError
case tooManyRequset

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

tooManyRequset에 오타가 있습니다. tooManyRequest로 수정하는 것이 좋겠습니다. 이 오타는 NetworkService.swift와 새로 추가된 NetworkRequestErrorHandler.swift에서도 사용되고 있어, 정의 부분을 수정하면 관련된 모든 코드의 가독성과 유지보수성이 향상될 것입니다.

Suggested change
case tooManyRequset
case tooManyRequest

}
3 changes: 3 additions & 0 deletions BeforeGoing/Data/Network/Service/NetworkService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ final class NetworkService: APIManaging {
if statuscode == 404 {
return .notFoundError
}
if statuscode == 429 {
return .tooManyRequset
}
if statuscode == 503 {
return .weatherServiceError
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ final class CalendarViewController: BaseViewController {
calendar.firstWeekday = 2
return calendar
}()
public var currentDate: Date = DateUtil.getCurrentDate()
var firstDate: Date = DateUtil.getCurrentDate()
var currentDate: Date = DateUtil.getCurrentDate()
private var startOfMonth: Date {
guard let date = calendar.date(from: calendar.dateComponents([.year, .month], from: currentDate)) else {
fatalError("Unable to calculate the start of the month.")
Expand All @@ -36,8 +37,9 @@ final class CalendarViewController: BaseViewController {
private var days: [String?] = []

private let calendarView = CalendarView()
private let blurEffect = UIBlurEffect(style: .systemMaterialLight)
private lazy var blurView: UIVisualEffectView = UIVisualEffectView(effect: blurEffect)
private let blurEffect = UIBlurEffect(style: .light)
private lazy var blurView = UIVisualEffectView(effect: blurEffect)
private let dimView = UIView()
var onDayDidTap: ((Date) -> Void)?
var onDismiss: (() -> Void)?

Expand All @@ -51,11 +53,19 @@ final class CalendarViewController: BaseViewController {

private func setStyle() {
view.backgroundColor = .clear
blurView.do {
$0.alpha = 0.8
}
dimView.do {
$0.backgroundColor = .black
$0.alpha = 0.2
}
}

private func setUI() {
view.addSubviews(
blurView,
dimView,
calendarView
)
}
Expand All @@ -64,6 +74,9 @@ final class CalendarViewController: BaseViewController {
blurView.snp.makeConstraints {
$0.edges.equalToSuperview()
}
dimView.snp.makeConstraints {
$0.edges.equalToSuperview()
}
calendarView.snp.makeConstraints {
$0.center.equalToSuperview()
}
Expand Down Expand Up @@ -153,7 +166,7 @@ extension CalendarViewController {
}

private func isDateInSelectableRange(date: Date) -> Bool {
guard let startDate = selectableStartDate,
guard let startDate = selectableStartDate,
let endDate = selectableEndDate else {
return false
}
Expand All @@ -166,13 +179,19 @@ extension CalendarViewController {
@objc
private func previousButtonDidTap() {
let date = DateUtil.getPreviousMonth(from: currentDate)
if DateUtil.isTwoMonthsApart(from: date, to: firstDate) {
return
}
currentDate = date
reload()
}

@objc
private func nextButtonDidTap() {
let date = DateUtil.getNextMonth(from: currentDate)
if DateUtil.isTwoMonthsApart(from: firstDate, to: date) {
return
}
currentDate = date
reload()
}
Expand All @@ -181,10 +200,14 @@ extension CalendarViewController {
private func backgroundDidTap(_ sender: UIGestureRecognizer) {
let location = sender.location(in: view)
if !calendarView.frame.contains(location) {
self.dismiss(animated: true)
onDismiss?()
dismiss()
}
}

private func dismiss() {
self.dismiss(animated: true)
onDismiss?()
}
}

extension CalendarViewController: UICollectionViewDelegate {
Expand All @@ -197,6 +220,7 @@ extension CalendarViewController: UICollectionViewDelegate {
selectedDate = date
onDayDidTap?(date)
reload()
dismiss()
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions BeforeGoing/Presentation/Common/Modal/ModalComponent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
import UIKit

struct ModalComponent {
let image: UIImage
let mainTitle: String
let image: UIImage?
let mainTitle: String?
let description: String
let dismissTitle: String
let dismissTitle: String?
let actionTitle: String
}
23 changes: 22 additions & 1 deletion BeforeGoing/Presentation/Common/Modal/ModalType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@

enum ModalType {

case expirationLogin, logout, withdraw
case expirationLogin
case logout
case withdraw
case tooManyRequest
case eventPushAgree(isAgreed: Bool, currentDate: String)

var component: ModalComponent {
switch self {
Expand Down Expand Up @@ -35,6 +39,23 @@ enum ModalType {
dismissTitle: "취소",
actionTitle: "탈퇴하기"
)
case .tooManyRequest:
return .init(
image: .withdrawWorry,
mainTitle: "응답 제한",
description: "너무 많은 수의 요청을 보냈어요",
dismissTitle: nil,
actionTitle: "확인"
)
case .eventPushAgree(let isAgreed, let currentDate):
let agreeStatus = isAgreed ? "수신 동의" : "수신 거부"
return .init(
image: nil,
mainTitle: nil,
description: "[나가기전에]에서 보내는 이벤트/마케팅 관련\n푸시알림 수신 여부가 ‘\(agreeStatus)’로\n변경되었습니다.\n\(currentDate)",
dismissTitle: nil,
actionTitle: "확인"
)
}
}
}
38 changes: 27 additions & 11 deletions BeforeGoing/Presentation/Common/Modal/ModalView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import UIKit

final class ModalView: BaseView {

private var modalType: ModalType?
private let imageView = UIImageView()
private let titleLabel = UILabel()
private let descriptionLabel = UILabel()
Expand All @@ -17,14 +18,10 @@ final class ModalView: BaseView {
private(set) var actionButton = UIButton()

init(type: ModalType) {
self.modalType = type
super.init(frame: .zero)

let component = type.component
imageView.image = component.image
titleLabel.text = component.mainTitle
descriptionLabel.text = component.description
dismissButton.setTitle(component.dismissTitle, for: .normal)
actionButton.setTitle(component.actionTitle, for: .normal)
setComponent(type.component)
}

required init?(coder: NSCoder) {
Expand All @@ -50,7 +47,6 @@ final class ModalView: BaseView {
$0.font = .custom(.headingH5)
}
descriptionLabel.do {
$0.textColor = .gray400
$0.textAlignment = .center
$0.numberOfLines = 0
$0.font = .custom(.bodyMDMedium)
Expand Down Expand Up @@ -78,10 +74,10 @@ final class ModalView: BaseView {
descriptionLabel,
buttonStackView
)
buttonStackView.addArrangedSubviews(
dismissButton,
actionButton
)
if let dismissTitle = modalType?.component.dismissTitle {
buttonStackView.addArrangedSubview(dismissButton)
}
buttonStackView.addArrangedSubview(actionButton)
}

override func setLayout() {
Expand All @@ -105,4 +101,24 @@ final class ModalView: BaseView {
$0.height.equalTo(48.adjustedH)
}
}

private func setComponent(_ component: ModalComponent) {
if let image = component.image {
imageView.image = component.image
}

if let mainTitle = component.mainTitle {
titleLabel.text = component.mainTitle
descriptionLabel.textColor = .gray400
} else {
descriptionLabel.textColor = .gray900
}

if let dismissTitle = component.dismissTitle {
dismissButton.setTitle(component.dismissTitle, for: .normal)
}

descriptionLabel.text = component.description
actionButton.setTitle(component.actionTitle, for: .normal)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,28 @@ final class BottomNavigationViewController: UITabBarController {
self.selectedIndex = item.rawValue
}

func handleScenarioTap(title: String) {
let homeIndex = BottomNavigationItem.home.rawValue

guard let viewControllers = self.viewControllers,
homeIndex < viewControllers.count else {
return
}

var homeVC = viewControllers[homeIndex]

if let navController = homeVC as? UINavigationController,
let rootVC = navController.viewControllers.first {
homeVC = rootVC
}

guard let finalHomeVC = homeVC as? HomeViewController else {
return
}

finalHomeVC.handleScenarioTap(title: title)
}

private func setViewControllers() {
self.viewControllers = BottomNavigationItem.allCases.map {
createViewController(
Expand All @@ -46,12 +68,14 @@ final class BottomNavigationViewController: UITabBarController {
title: String,
image: UIImage
) -> UIViewController {
let viewController = UINavigationController(rootViewController: rootViewController)
rootViewController.tabBarItem.do{
rootViewController.tabBarItem.do {
$0.title = title
$0.image = image.withRenderingMode(.alwaysTemplate)
}
return viewController
if let viewController = rootViewController as? HomeViewController {
return viewController
}
return UINavigationController(rootViewController: rootViewController)
Comment on lines +75 to +78

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

HomeViewControllerUINavigationController로 감싸지 않는 특별한 처리를 하고 있습니다. 다른 탭의 뷰 컨트롤러들은 UINavigationController로 감싸져 있어 일관성이 부족하며, 향후 홈 탭에서 네비게이션 기능이 필요할 때 예기치 않은 동작을 유발할 수 있습니다. 모든 탭이 일관된 네비게이션 구조를 갖도록 HomeViewControllerUINavigationController로 감싸는 것을 고려해 보세요. 만약 특별한 의도가 있다면 주석을 추가하여 다른 개발자들이 이해할 수 있도록 하는 것이 좋겠습니다.

}

private func createTabBarAppearance() -> UITabBarAppearance {
Expand Down
30 changes: 30 additions & 0 deletions BeforeGoing/Presentation/Common/Toast/ToastMessageType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// ToastMessageType.swift
// BeforeGoing
//
// Created by APPLE on 11/2/25.
//

import UIKit

enum ToastMessageType {

case todayMissionLimit
case missionLimit
case duplicateMission

var image: UIImage {
return .errorToast
}

var message: String {
switch self {
case .todayMissionLimit:
return "오늘의 미션은 20개까지만 설정할 수 있어요"
case .missionLimit:
return "미션은 20개까지만 설정할 수 있어요"
case .duplicateMission:
return "중복된 이름의 미션은 설정할 수 없어요"
}
}
}
Loading