Skip to content

Conversation

@baekteun
Copy link
Member

@baekteun baekteun commented Oct 11, 2025

💡 개요

주간 시간표

📃 작업내용

X

🔀 변경사항

🙋‍♂️ 질문사항

🍴 사용방법

🤔 고민과 해결방법

🎸 기타

Summary by CodeRabbit

  • 신기능

    • 주간 시간표 뷰 추가: 주말 표시 옵션, 당일 강조, 가변/스크롤 레이아웃, 로딩/빈 상태 및 당겨서 새로고침 지원.
    • 원격 설정 기반 토글로 일간/주간 시간표 전환 가능.
  • 스타일

    • 시간표 안내 문구 및 접근성 라벨을 “학기 초” 기준으로 업데이트.
    • 학교 설정의 다음 버튼 문구를 “확인!”으로 변경.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 11, 2025

Walkthrough

주간 시간표 기능이 추가되어 Core와 View가 신설되고 MainCore/MainView에 연결되었습니다. Remote Config를 도입해 enable_weekly_time_table 플래그로 일간/주간 시간표 표시를 토글합니다. 부가적으로 일간 시간표 안내 문구, 학교 설정 버튼 문구가 변경되었고, 사용자 속성에 새 키가 추가되었습니다.

Changes

Cohort / File(s) 요약
원격 설정 도입 및 토글 연동
Projects/App/iOS/Sources/Application/AppDelegate.swift, Projects/Feature/MainFeature/Sources/MainView.swift, Projects/Shared/TWLog/Sources/TWUserProperty.swift
FirebaseRemoteConfig import 추가, Remote Config 플래그 enable_weekly_time_table 프로퍼티 도입, 값 변경 시 TWLog 사용자 속성 기록, 플래그에 따라 주간/일간 시간표 뷰 전환
주간 시간표 기능 추가 및 메인 연결
Projects/Feature/TimeTableFeature/Sources/Weekly/WeeklyTimeTableCore.swift, Projects/Feature/TimeTableFeature/Sources/Weekly/WeeklyTimeTableView.swift, Projects/Feature/MainFeature/Sources/MainCore.swift
WeeklyTimeTableCore/State/Action 신설, 주간 데이터 비동기 수집/가공 로직 추가, 주말 표시 여부 처리, MainCore에 상태/액션 스코핑 및 onAppear 초기화, WeeklyTimeTableView UI 추가 및 로딩/빈 상태/강조 표시 구현
일간 시간표 안내 문구 수정
Projects/Feature/TimeTableFeature/Sources/Daily/TimeTableView.swift
3월 단일 체크 → 3월/9월 체크로 변경, 안내 및 접근성 라벨 문구를 “학기 초” 기준으로 업데이트
학교 설정 문구 변경
Projects/Feature/SchoolSettingFeature/Sources/SchoolSettingCore.swift
nextButtonTitle 반환 문자열 “확인” → “확인!”로 변경

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant MainView
  participant MainCore
  participant WeeklyTimeTableCore as WeeklyCore
  participant RemoteConfig
  participant TimeTableClient as API
  participant LocalDB

  Note over RemoteConfig,MainView: Remote Config 플래그 로드
  RemoteConfig-->>MainView: enable_weekly_time_table
  MainView->>MainCore: onAppear
  MainCore->>MainCore: weeklyTimeTableCore 생성 (displayDate 공유)
  MainView->>WeeklyCore: .onLoad / .onAppear
  WeeklyCore->>WeeklyCore: displayDate 변경 구독 → refreshData
  WeeklyCore->>WeeklyCore: mondayDate 계산, 주말 표시 여부 결정
  alt 수정 시간표 사용
    WeeklyCore->>LocalDB: 수정 시간표 조회
    LocalDB-->>WeeklyCore: 일자별 데이터
  else 기본 시간표 사용
    WeeklyCore->>API: 요일별 시간표 조회 (취소 토큰 포함)
    API-->>WeeklyCore: 일자별 데이터
  end
  WeeklyCore->>WeeklyCore: 주간 구조로 집계(weekdays/dates/periods/subjects/todayIndex)
  WeeklyCore-->>MainView: 상태 업데이트(loading/weeklyTimeTable)

  Note over MainView: 플래그 변경 시 사용자 속성 로깅
Loading
sequenceDiagram
  autonumber
  participant RemoteConfig
  participant MainView
  participant TWLog

  RemoteConfig-->>MainView: enable_weekly_time_table 값
  MainView->>TWLog: setUserProperty(enable_weekly_time_table, value)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

✨ Feature

Poem

오늘도 귀 쫑긋, 캘린더를 훑는 토끼
주간칸 줄 맞춰 시간표를 척척!
깃발이 켜지면 주간, 아니면 일간으로 휙—
월~일 칸 사이로 당근 이모지 살짝🥕
“확인!” 한 번, 새 학기엔 더 분주한 점프!
로딩 바 지나면, 수업들이 토도독 펼쳐지네.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed 제목이 PR의 핵심 변경사항인 주간 시간표 기능 추가를 간결하고 명확하게 요약하고 있으며 불필요한 단어 없이 핵심만 담고 있어 적절합니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/weekly-timetable

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Contributor

github-actions bot commented Oct 11, 2025

✅ Successful finished SwiftLint

@github-actions
Copy link
Contributor

✅ PR의 Assign 자동 지정을 성공했어요!

@baekteun

@gemini-code-assist
Copy link

Summary of Changes

Hello @baekteun, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 사용자에게 주간 시간표를 제공하는 새로운 기능을 도입합니다. 이 기능은 Firebase Remote Config를 통해 유연하게 관리될 수 있으며, 기존 시간표 모듈의 구조를 개선하고 사용자 경험을 위한 사소한 UI 텍스트 조정을 포함합니다. 전반적으로 시간표 관련 기능의 확장성과 유용성을 향상시키는 데 중점을 둡니다.

Highlights

  • 주간 시간표 기능 추가: 새로운 주간 시간표 기능이 추가되었습니다. 이 기능은 Firebase Remote Config를 통해 동적으로 활성화/비활성화될 수 있습니다. WeeklyTimeTableCore.swiftWeeklyTimeTableView.swift 파일이 새로 추가되었으며, MainCore.swiftMainView.swift에 통합되었습니다.
  • Firebase Remote Config 연동: FirebaseRemoteConfig가 앱에 통합되어 enable_weekly_time_table 키를 통해 주간 시간표 기능의 가시성을 제어합니다. 이 설정 변경은 사용자 속성으로 로깅됩니다.
  • 기존 시간표 기능 리팩토링: 기존의 일간 시간표 관련 파일들(TimeTableCore.swift, TimeTableView.swift)이 TimeTableFeature/Sources/Daily 디렉토리로 이동하여 구조화되었습니다.
  • 사소한 UI/텍스트 업데이트: SchoolSettingCore.swift에서 버튼 텍스트가 '확인'에서 '확인!'으로 변경되었고, TimeTableView.swift에서는 학기 초 시간표 안내 문구에 9월이 추가되었습니다. 또한, MainView.swift에서 리뷰 토스트와 새 버전 업데이트 버튼의 레이아웃이 조정되었습니다.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

주간 시간표 기능을 추가하는 PR이네요. 전반적으로 새로운 기능이 잘 통합되었고, 기능 플래그를 사용하여 점진적으로 기능을 배포하는 방식도 좋습니다. 몇 가지 코드 개선 사항을 제안합니다. WeeklyTimeTableCore에서 데이터를 병렬로 가져오도록 수정하여 성능을 개선하고, 일부 로직을 더 간결하게 만들어 가독성과 유지보수성을 높일 수 있습니다. 자세한 내용은 각 파일에 남긴 주석을 확인해주세요.

Comment on lines +147 to +173
for i in 0..<dayCount {
let currentDate = calendar.date(byAdding: .day, value: i, to: mondayDate) ?? mondayDate
let weekday = calendar.component(.weekday, from: currentDate)

let isOnModifiedTimeTable = userDefaultsClient.getValue(.isOnModifiedTimeTable) as? Bool ?? false

if isOnModifiedTimeTable {
let modifiedTimeTables = try? localDatabaseClient.readRecords(as: ModifiedTimeTableLocalEntity.self)
.filter { $0.weekday == weekday }

if let modifiedTimeTables, !modifiedTimeTables.isEmpty {
let timeTables = modifiedTimeTables
.sorted { $0.perio < $1.perio }
.map { TimeTable(perio: $0.perio, content: $0.content) }
weeklyData[i] = timeTables
} else {
weeklyData[i] = []
}
} else {
do {
let dayData = try await timeTableClient.fetchTimeTable(currentDate)
weeklyData[i] = dayData
} catch {
weeklyData[i] = []
}
}
}

Choose a reason for hiding this comment

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

high

for 루프 내에서 await를 사용하여 각 날짜의 시간표를 순차적으로 가져오고 있습니다. 이로 인해 네트워크 상태가 좋지 않을 경우 상당한 지연이 발생할 수 있습니다. 각 날짜의 데이터를 가져오는 작업은 서로 독립적이므로 TaskGroup을 사용하여 병렬로 처리하면 성능을 크게 향상시킬 수 있습니다.

        await withTaskGroup(of: (Int, [TimeTable]).self) { group in
            for i in 0..<dayCount {
                group.addTask {
                    let currentDate = calendar.date(byAdding: .day, value: i, to: mondayDate) ?? mondayDate
                    let weekday = calendar.component(.weekday, from: currentDate)
                    let isOnModifiedTimeTable = userDefaultsClient.getValue(.isOnModifiedTimeTable) as? Bool ?? false

                    if isOnModifiedTimeTable {
                        let modifiedTimeTables = try? localDatabaseClient.readRecords(as: ModifiedTimeTableLocalEntity.self)
                            .filter { $0.weekday == weekday }

                        if let modifiedTimeTables, !modifiedTimeTables.isEmpty {
                            let timeTables = modifiedTimeTables
                                .sorted { $0.perio < $1.perio }
                                .map { TimeTable(perio: $0.perio, content: $0.content) }
                            return (i, timeTables)
                        } else {
                            return (i, [])
                        }
                    } else {
                        do {
                            let dayData = try await timeTableClient.fetchTimeTable(currentDate)
                            return (i, dayData)
                        } catch {
                            return (i, [])
                        }
                    }
                }
            }

            for await (index, timeTables) in group {
                weeklyData[index] = timeTables
            }
        }

Comment on lines +239 to +259
if showWeekend {
switch todayWeekday {
case 2: todayIndex = 0 // 월요일
case 3: todayIndex = 1 // 화요일
case 4: todayIndex = 2 // 수요일
case 5: todayIndex = 3 // 목요일
case 6: todayIndex = 4 // 금요일
case 7: todayIndex = 5 // 토요일
case 1: todayIndex = 6 // 일요일
default: todayIndex = nil
}
} else {
switch todayWeekday {
case 2: todayIndex = 0 // 월요일
case 3: todayIndex = 1 // 화요일
case 4: todayIndex = 2 // 수요일
case 5: todayIndex = 3 // 목요일
case 6: todayIndex = 4 // 금요일
default: todayIndex = nil
}
}

Choose a reason for hiding this comment

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

medium

현재 todayIndex를 계산하는 로직이 switch 문으로 인해 길고 장황합니다. 수학적 계산을 사용하여 이 로직을 더 간결하게 만들 수 있습니다. 이렇게 하면 코드 가독성과 유지보수성이 향상됩니다.

        if showWeekend {
            // 1(일) -> 6, 2(월) -> 0, ..., 7(토) -> 5
            todayIndex = (todayWeekday + 5) % 7
        } else if (2...6).contains(todayWeekday) { // 월(2) ~ 금(6)
            todayIndex = todayWeekday - 2
        } else {
            todayIndex = nil
        }

Comment on lines +139 to +145
selectedColumnPeriodCount: {
if let todayIndex = weeklyTimeTable
.todayIndex { weeklyTimeTable.actualPeriodCount(for: todayIndex)
} else {
0
}
}()

Choose a reason for hiding this comment

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

medium

이 즉시 실행 클로저(IIFE)는 코드를 장황하게 만들고 가독성이 떨어집니다. map과 nil-coalescing 연산자(??)를 사용하여 더 간결하고 Swift다운 방식으로 표현할 수 있습니다.

                    selectedColumnPeriodCount: weeklyTimeTable.todayIndex.map { weeklyTimeTable.actualPeriodCount(for: $0) } ?? 0

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Projects/Feature/SchoolSettingFeature/Sources/SchoolSettingCore.swift (1)

44-51: nextButtonTitle 조건이 중복되어 논리 오류 및 도달 불가 코드가 있습니다

  • 두 번째 조건의 || schoolMajorList.isEmpty는 첫 번째 조건과 중복으로 의미가 없습니다.
  • 마지막 return "다음"은 실행 경로가 없습니다.

간결하고 의도에 맞게 정리해주세요.

-        public var nextButtonTitle: String {
-            if major.isEmpty || schoolMajorList.isEmpty {
-                return "이대로하기"
-            } else if !major.isEmpty || schoolMajorList.isEmpty {
-                return "확인!"
-            }
-            return "다음"
-        }
+        public var nextButtonTitle: String {
+            if major.isEmpty || schoolMajorList.isEmpty {
+                return "이대로하기"
+            } else {
+                return "확인!"
+            }
+        }
🧹 Nitpick comments (11)
Projects/App/iOS/Sources/Application/AppDelegate.swift (1)

8-8: 불필요한 import 가능성

FirebaseRemoteConfig를 이 파일에서 직접 사용하지 않습니다. 필요 없다면 제거해 주세요. Remote Config 초기화/활성화는 featureFlagClient.activate()로 처리되고 있습니다.

Projects/Feature/MainFeature/Sources/MainView.swift (1)

222-224: 최초 값 로깅 추가 제안

현재는 플래그 변경 시에만 사용자 속성을 기록합니다. 최초 진입 시 값도 기록하면 분석 일관성이 좋아집니다. 아래처럼 .onAppear에 한 줄 추가를 권장합니다.

             .onAppear {
                 viewStore.send(.onAppear, animation: .default)
+                TWLog.setUserProperty(property: .enableWeeklyTimeTable, value: enableWeeklyTimeTable.description)
             }
Projects/Feature/TimeTableFeature/Sources/Weekly/WeeklyTimeTableView.swift (2)

8-9: ColumnBorder.borderWidth 미사용 정리

borderWidth를 선언하지만 path/stroke에서 사용하지 않습니다. 혼동을 줄이기 위해 제거하거나 stroke의 lineWidth와 일치시키세요. 제거안을 제안합니다.

권장 패치:

 private struct ColumnBorder: Shape {
-    var borderWidth: CGFloat
     var cornerRadius: CGFloat
@@
                 ColumnBorder(
-                    borderWidth: 1,
                     cornerRadius: 16,
                     selectedColumnIndex: weeklyTimeTable.todayIndex,
                     headerHeight: 44,
                     firstColumnWidth: 40,
                     columnWidth: columnWidth(weeklyTimeTable: weeklyTimeTable),
                     useFlexibleWidth: shouldUseFlexibleWidth(weeklyTimeTable: weeklyTimeTable),
                     columnCount: weeklyTimeTable.weekdays.count,
                     cellHeight: 56,
                     selectedColumnPeriodCount: {
                         if let todayIndex = weeklyTimeTable
                             .todayIndex { weeklyTimeTable.actualPeriodCount(for: todayIndex)
                         } else {
                             0
                         }
                     }()
                 )
                 .stroke(Color.extraBlack.opacity(0.8), lineWidth: 2)

Also applies to: 129-148


93-94: 불필요한 .frame(alignment:) 제거

크기 지정 없는 .frame(alignment:)는 효과가 없습니다. 제거해도 동작 동일합니다.

권장 패치:

-                                timeTableGrid(weeklyTimeTable: weeklyTimeTable)
-                                    .frame(alignment: .top)
+                                timeTableGrid(weeklyTimeTable: weeklyTimeTable)
@@
-                                    timeTableGrid(weeklyTimeTable: weeklyTimeTable)
-                                        .frame(alignment: .top)
+                                    timeTableGrid(weeklyTimeTable: weeklyTimeTable)

Also applies to: 97-98

Projects/Feature/TimeTableFeature/Sources/Weekly/WeeklyTimeTableCore.swift (7)

18-18: 옵셔널의 불필요한 명시적 nil 초기화 제거

SwiftLint 경고: = nil 제거 권장.

권장 패치:

-        public var weeklyTimeTable: WeeklyTimeTable? = nil
+        public var weeklyTimeTable: WeeklyTimeTable?

130-133: 빈 연관값 와일드카드 제거

.failure(_)에서 인자 미사용 시 괄호 생략 가능합니다. SwiftLint 권고 반영.

권장 패치:

-            case .timeTableResponse(.failure(_)):
+            case .timeTableResponse(.failure):
                 state.weeklyTimeTable = nil
                 state.isLoading = false

65-71: for-where로 간결화

단일 if는 for-where로 단순화해 SwiftLint 경고 해소.

권장 패치:

-            for i in (0..<daySubjects.count).reversed() {
-                if !daySubjects[i].isEmpty {
-                    return i + 1
-                }
-            }
+            for i in (0..<daySubjects.count).reversed() where !daySubjects[i].isEmpty {
+                return i + 1
+            }
             return 0

182-187: SwiftLint blanket disable 축소

blanket disable 대신 next 사용으로 범위 최소화.

권장 패치:

-    // swiftlint: disable cyclomatic_complexity
+    // swiftlint:disable:next cyclomatic_complexity
     private func createWeeklyTableFromAPIData(

141-152: 불변 플래그 루프 외부 호이스팅

isOnModifiedTimeTable는 반복마다 동일합니다. 루프 밖으로 이동해 불필요한 호출을 줄이세요.

권장 패치:

     private func fetchWeeklyTimeTable(mondayDate: Date, showWeekend: Bool)
         async throws -> WeeklyTimeTable {
         let calendar = Calendar.current
         let dayCount = showWeekend ? 7 : 5
         var weeklyData: [Int: [TimeTable]] = [:]
+        let isOnModifiedTimeTable = userDefaultsClient.getValue(.isOnModifiedTimeTable) as? Bool ?? false
 
         for i in 0..<dayCount {
             let currentDate = calendar.date(byAdding: .day, value: i, to: mondayDate) ?? mondayDate
             let weekday = calendar.component(.weekday, from: currentDate)
 
-            let isOnModifiedTimeTable = userDefaultsClient.getValue(.isOnModifiedTimeTable) as? Bool ?? false

153-172: DB/네트워크 퍼포먼스 개선 제안

  • DB: 매일 readRecords 후 필터링은 비효율적입니다. 한 번만 읽어 weekday별로 그룹핑하거나 쿼리 단계에서 weekday로 필터링하세요.
  • 네트워크: showWeekend가 false/true에 따라 5~7회 순차 호출합니다. withTaskGroup 등으로 병렬화해 총 대기 시간을 줄이세요.

예시(개념):

// DB 한 번만 로드 후 사용
let allModified = (try? localDatabaseClient.readRecords(as: ModifiedTimeTableLocalEntity.self)) ?? []
let byWeekday = Dictionary(grouping: allModified, by: { $0.weekday })

// 네트워크 병렬화 (개념)
try await withThrowingTaskGroup(of: (Int, [TimeTable]).self) { group in
  for i in 0..<dayCount {
    group.addTask {
      let date = calendar.date(byAdding: .day, value: i, to: mondayDate) ?? mondayDate
      return (i, try await timeTableClient.fetchTimeTable(date))
    }
  }
  for try await (i, dayData) in group {
    weeklyData[i] = dayData
  }
}

183-268: 긴 함수 분리 권장(createWeeklyTableFromAPIData)

SwiftLint function_body_length 경고가 있습니다. 날짜 생성, 과목 매트릭스 구성, todayIndex 계산을 소함수로 분리해 가독성과 테스트 용이성을 높이세요.

간단한 분할 예:

  • makeWeekdays(showWeekend:)
  • makeDates(mondayDate:showWeekend:)
  • buildSubjects(weeklyData:dayCount:)
  • computeTodayIndex(mondayDate:showWeekend:)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ae4c9a0 and 1f663c9.

📒 Files selected for processing (8)
  • Projects/App/iOS/Sources/Application/AppDelegate.swift (1 hunks)
  • Projects/Feature/MainFeature/Sources/MainCore.swift (4 hunks)
  • Projects/Feature/MainFeature/Sources/MainView.swift (4 hunks)
  • Projects/Feature/SchoolSettingFeature/Sources/SchoolSettingCore.swift (1 hunks)
  • Projects/Feature/TimeTableFeature/Sources/Daily/TimeTableView.swift (1 hunks)
  • Projects/Feature/TimeTableFeature/Sources/Weekly/WeeklyTimeTableCore.swift (1 hunks)
  • Projects/Feature/TimeTableFeature/Sources/Weekly/WeeklyTimeTableView.swift (1 hunks)
  • Projects/Shared/TWLog/Sources/TWUserProperty.swift (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
Projects/Feature/TimeTableFeature/Sources/Weekly/WeeklyTimeTableView.swift (1)
Projects/Feature/TimeTableFeature/Sources/Weekly/WeeklyTimeTableCore.swift (3)
  • actualPeriodCount (62-71)
  • isToday (58-60)
  • subject (49-56)
Projects/Feature/TimeTableFeature/Sources/Weekly/WeeklyTimeTableCore.swift (1)
Projects/Shared/LocalDatabaseClient/Sources/LocalDatabaseClient.swift (1)
  • readRecords (25-32)
Projects/Feature/MainFeature/Sources/MainView.swift (1)
Projects/Shared/TWLog/Sources/TWLog.swift (3)
  • event (63-68)
  • setUserProperty (49-53)
  • setUserProperty (55-57)
🪛 GitHub Check: swiftlint
Projects/Feature/TimeTableFeature/Sources/Weekly/WeeklyTimeTableCore.swift

[warning] 18-18:
Implicit Optional Initialization Violation: Optional should be implicitly initialized without nil (implicit_optional_initialization)


[warning] 183-183:
Function Body Length Violation: Function body should span 50 lines or less excluding comments and whitespace: currently spans 68 lines (function_body_length)


[warning] 66-66:
Prefer For-Where Violation: where clauses are preferred over a single if inside a for (for_where)


[warning] 130-130:
Empty Enum Arguments Violation: Arguments can be omitted when matching enums with associated values if they are not used (empty_enum_arguments)


[warning] 182-182:
Blanket Disable Command Violation: Use 'next', 'this' or 'previous' instead to disable the 'cyclomatic_complexity' rule once, or re-enable it as soon as possible` (blanket_disable_command)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: ⚙️ Prepare for CI
🔇 Additional comments (8)
Projects/Shared/TWLog/Sources/TWUserProperty.swift (1)

12-12: 사용자 속성 케이스 추가 적절합니다

원격 플래그 로깅과 일관됩니다.

Projects/Feature/TimeTableFeature/Sources/Daily/TimeTableView.swift (1)

24-30: 학기 초 안내 문구 업데이트 LGTM

3/9월 조건과 접근성 라벨이 일치합니다.

Projects/Feature/MainFeature/Sources/MainCore.swift (2)

119-121: 주간 시간표 리프레시 처리 확인 필요

mealCore(.refresh), timeTableCore(.refresh)만 날짜 정책 재적용을 트리거합니다. 주간 시간표도 유사한 경로(예: .weeklyTimeTableCore(.refresh))가 필요하다면 함께 처리해 주세요.


237-239: WeeklyTimeTableCore 서브리듀서 연결 LGTM

상태/액션 라우팅이 올바르게 구성되었습니다.

Projects/Feature/MainFeature/Sources/MainView.swift (2)

62-78: 플래그 기반 주간/일간 전환 렌더링 LGTM

스토어 스코핑과 조건부 렌더링이 일관됩니다.


3-3: @RemoteConfigProperty 모듈 import 확인 및 초기 로깅 보강

  • @RemoteConfigProperty가 선언된 모듈(예: FeatureFlagClient)을 해당 파일에 import했는지 검증하세요. 불필요한 import FirebaseRemoteConfig는 제거를 고려해주세요.
  • 현재 onChange만 사용 중이라 뷰 최초 렌더링 시 값이 기록되지 않습니다. onAppear에서 초기 값을 로깅하도록 추가하세요.
Projects/Feature/TimeTableFeature/Sources/Weekly/WeeklyTimeTableView.swift (2)

72-79: Date().month 확장 존재 여부 확인 필요

Swift 표준에는 Date.month가 없습니다. 확장이 없다면 빌드 실패합니다. 없으면 Calendar.current.component(.month, from:)로 대체하세요.

권장 패치:

-                    if Date().month == 3 || Date().month == 9 {
+                    if Calendar.current.component(.month, from: Date()) == 3
+                        || Calendar.current.component(.month, from: Date()) == 9 {

105-107: 커스텀 .onLoad 사용 확인

.onLoad가 커스텀 수정자인지 확인해주세요. 없다면 호출되지 않습니다. iOS 15+에서는 .task로 대체할 수 있습니다.

권장 패치:

-        .onLoad {
-            viewStore.send(.onLoad)
-        }
+        .task {
+            viewStore.send(.onLoad)
+        }

Comment on lines +95 to +107
case .onAppear, .refreshData:
state.isLoading = true
state.showWeekend =
!(userDefaultsClient.getValue(.isSkipWeekend) as? Bool ?? false)

let baseDate = state.displayDate
let calendar = Calendar.current
let weekday = calendar.component(.weekday, from: baseDate)
let daysFromMonday = (weekday + 5) % 7
let mondayDate =
calendar.date(byAdding: .day, value: -daysFromMonday, to: baseDate)
?? baseDate

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Pull-to-Refresh 미동작: .refresh 액션 미처리

View에서 .refresh를 보냅니다만 reducer가 처리하지 않아 새로고침이 동작하지 않습니다. .refresh를 .refreshData와 동일하게 처리하세요.

권장 패치:

-            case .onAppear, .refreshData:
+            case .onAppear, .refresh, .refreshData:
                 state.isLoading = true
                 state.showWeekend =
                     !(userDefaultsClient.getValue(.isSkipWeekend) as? Bool ?? false)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
case .onAppear, .refreshData:
state.isLoading = true
state.showWeekend =
!(userDefaultsClient.getValue(.isSkipWeekend) as? Bool ?? false)
let baseDate = state.displayDate
let calendar = Calendar.current
let weekday = calendar.component(.weekday, from: baseDate)
let daysFromMonday = (weekday + 5) % 7
let mondayDate =
calendar.date(byAdding: .day, value: -daysFromMonday, to: baseDate)
?? baseDate
case .onAppear, .refresh, .refreshData:
state.isLoading = true
state.showWeekend =
!(userDefaultsClient.getValue(.isSkipWeekend) as? Bool ?? false)
let baseDate = state.displayDate
let calendar = Calendar.current
let weekday = calendar.component(.weekday, from: baseDate)
let daysFromMonday = (weekday + 5) % 7
let mondayDate =
calendar.date(byAdding: .day, value: -daysFromMonday, to: baseDate)
?? baseDate
🤖 Prompt for AI Agents
In Projects/Feature/TimeTableFeature/Sources/Weekly/WeeklyTimeTableCore.swift
around lines 95 to 107, the reducer handles .onAppear and .refreshData but not
the View's .refresh action, so pull-to-refresh does nothing; update the switch
case to include .refresh (e.g., case .onAppear, .refresh, .refreshData:) or add
a separate case for .refresh that performs the identical work (set
state.isLoading = true, compute/showWeekend, compute mondayDate from
state.displayDate, etc.), ensuring .refresh triggers the same flow as
.refreshData.

Comment on lines +235 to +259
let today = Date()
let todayWeekday = calendar.component(.weekday, from: today)
let todayIndex: Int?

if showWeekend {
switch todayWeekday {
case 2: todayIndex = 0 // 월요일
case 3: todayIndex = 1 // 화요일
case 4: todayIndex = 2 // 수요일
case 5: todayIndex = 3 // 목요일
case 6: todayIndex = 4 // 금요일
case 7: todayIndex = 5 // 토요일
case 1: todayIndex = 6 // 일요일
default: todayIndex = nil
}
} else {
switch todayWeekday {
case 2: todayIndex = 0 // 월요일
case 3: todayIndex = 1 // 화요일
case 4: todayIndex = 2 // 수요일
case 5: todayIndex = 3 // 목요일
case 6: todayIndex = 4 // 금요일
default: todayIndex = nil
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

오늘 하이라이트(todayIndex) 계산 오류

현재 Date()의 요일만으로 todayIndex를 계산해 표시 주와 무관하게 하이라이트됩니다. 표시 중인 주(mondayDate) 범위 내일 때만 인덱스를 계산해야 합니다.

권장 패치:

-        let today = Date()
-        let todayWeekday = calendar.component(.weekday, from: today)
-        let todayIndex: Int?
-
-        if showWeekend {
-            switch todayWeekday {
-            case 2: todayIndex = 0 // 월요일
-            case 3: todayIndex = 1 // 화요일
-            case 4: todayIndex = 2 // 수요일
-            case 5: todayIndex = 3 // 목요일
-            case 6: todayIndex = 4 // 금요일
-            case 7: todayIndex = 5 // 토요일
-            case 1: todayIndex = 6 // 일요일
-            default: todayIndex = nil
-            }
-        } else {
-            switch todayWeekday {
-            case 2: todayIndex = 0 // 월요일
-            case 3: todayIndex = 1 // 화요일
-            case 4: todayIndex = 2 // 수요일
-            case 5: todayIndex = 3 // 목요일
-            case 6: todayIndex = 4 // 금요일
-            default: todayIndex = nil
-            }
-        }
+        let startOfMonday = calendar.startOfDay(for: mondayDate)
+        let startOfToday = calendar.startOfDay(for: Date())
+        let diff = calendar.dateComponents([.day], from: startOfMonday, to: startOfToday).day ?? Int.max
+        let todayIndex: Int? = {
+            if showWeekend {
+                return (0...6).contains(diff) ? diff : nil
+            } else {
+                return (0...4).contains(diff) ? diff : nil
+            }
+        }()

@baekteun baekteun merged commit be924f6 into master Oct 14, 2025
6 checks passed
@baekteun baekteun deleted the feature/weekly-timetable branch October 14, 2025 15:03
@baekteun baekteun restored the feature/weekly-timetable branch November 21, 2025 09:33
@baekteun baekteun deleted the feature/weekly-timetable branch November 21, 2025 09:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants