Repository navigation
[Feat/#473] 11시 학식 알림을 서버 푸시로 전환 #476
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Hrepay
wants to merge
5
commits into
develop
Choose a base branch
from
feat/#473
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
1786ac4
[#473] 푸시 토큰을 서버에 등록·삭제하고 기존 11시 로컬 알림 제거
Hrepay 6d504ce
[#473] 학식 알림을 누르면 해당 날짜·시간대의 학식 화면으로 이동
Hrepay f55c630
[#473] 알림 토글을 빠르게 바꿔도 푸시 토큰 등록 상태가 어긋나지 않도록 요청을 순서대로 처리
Hrepay 4a0f4a9
[#473] 로그아웃 시 진행 중인 등록 요청 뒤에 토큰을 삭제하고, 이전 계정 응답이 다음 계정 상태를 바꾸지 않도록 수정
Hrepay 2b89d44
[#473] 일반 학식 알림은 현재 시간대로 이동하고, 오래된 알림 이동과 떠 있는 다이얼로그를 정리
Hrepay File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
54 changes: 54 additions & 0 deletions
54
EATSSU/App/Sources/Data/Network/Router/PushTokenRouter.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| // | ||
| // PushTokenRouter.swift | ||
| // EATSSU | ||
| // | ||
| // Created by 황상환 on 10/17/26. | ||
| // | ||
|
|
||
| import Foundation | ||
|
|
||
| import Moya | ||
|
|
||
| /// 푸시 토큰 API (로그인 필요). 서버는 토큰 등록 여부로 알림 허용 여부를 판단한다 | ||
| enum PushTokenRouter { | ||
| /// 등록·갱신. 다른 계정에 등록된 토큰이면 현재 계정으로 옮겨진다 | ||
| case register(token: String) | ||
| /// 삭제. 등록되지 않은 토큰이어도 성공 | ||
| case unregister(token: String) | ||
| } | ||
|
|
||
| extension PushTokenRouter: TargetType { | ||
| var baseURL: URL { | ||
| URL(string: Config.baseURL)! | ||
| } | ||
|
|
||
| var path: String { | ||
| "/users/push-token" | ||
| } | ||
|
|
||
| var method: Moya.Method { | ||
| switch self { | ||
| case .register: | ||
| .put | ||
| case .unregister: | ||
| .delete | ||
| } | ||
| } | ||
|
|
||
| var task: Moya.Task { | ||
| switch self { | ||
| case let .register(token): | ||
| .requestParameters(parameters: ["token": token, "deviceType": "IOS"], encoding: JSONEncoding.default) | ||
| case let .unregister(token): | ||
| .requestParameters(parameters: ["token": token], encoding: JSONEncoding.default) | ||
| } | ||
| } | ||
|
|
||
| var headers: [String: String]? { | ||
| ["Content-Type": "application/json"] | ||
| } | ||
|
|
||
| var validationType: ValidationType { | ||
| .successCodes | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| // | ||
| // PushRoute.swift | ||
| // EATSSU | ||
| // | ||
| // Created by 황상환 on 10/17/26. | ||
| // | ||
|
|
||
| import UIKit | ||
|
|
||
| /// 학식 알림을 눌렀을 때 이동할 학식 화면 (날짜 + 시간대) | ||
| struct PushRoute { | ||
| /// 학식 탭 시간대 순서 (아침·점심·저녁) | ||
| enum MealTime: String { | ||
| case morning = "MORNING" | ||
| case lunch = "LUNCH" | ||
| case dinner = "DINNER" | ||
|
|
||
| var tabIndex: Int { | ||
| switch self { | ||
| case .morning: 0 | ||
| case .lunch: 1 | ||
| case .dinner: 2 | ||
| } | ||
| } | ||
| } | ||
|
|
||
| let date: Date | ||
| /// 찜 알림에만 있다. 없으면(일반 학식 알림) 현재 시각 기준 시간대를 그대로 쓴다 | ||
| let mealTime: MealTime? | ||
|
|
||
| /// 서버 푸시 data: `type`(MENU_FAVORITE / DAILY_MENU), `date`(yyyyMMdd), `timePart`(찜 알림만) | ||
| init?(userInfo: [AnyHashable: Any]) { | ||
| guard let type = userInfo["type"] as? String, | ||
| Self.supportedTypes.contains(type), | ||
| let dateString = userInfo["date"] as? String, | ||
| let date = Self.dateFormatter.date(from: dateString) else { return nil } | ||
| self.date = date | ||
| self.mealTime = (userInfo["timePart"] as? String).flatMap(MealTime.init(rawValue:)) | ||
| } | ||
|
|
||
| private static let supportedTypes: Set<String> = ["MENU_FAVORITE", "DAILY_MENU"] | ||
|
|
||
| private static let dateFormatter: DateFormatter = { | ||
| let formatter = DateFormatter() | ||
| formatter.calendar = Calendar(identifier: .gregorian) | ||
| formatter.locale = Locale(identifier: "en_US_POSIX") | ||
| formatter.timeZone = .current | ||
| formatter.dateFormat = "yyyyMMdd" | ||
| return formatter | ||
| }() | ||
| } | ||
|
|
||
| /// 알림을 누른 시점에 화면이 준비돼 있지 않을 수 있어(앱 실행 직후 스플래시·로그인 등) 이동할 곳을 보관했다가 적용한다 | ||
| final class PushRouter { | ||
|
|
||
| static let shared = PushRouter() | ||
|
|
||
| private init() {} | ||
|
|
||
| private var pendingRoute: (route: PushRoute, receivedAt: Date)? | ||
|
|
||
| /// 보관한 이동의 유효 시간. 로그인 화면에서 알림을 누른 뒤 한참 지나 로그인하면 예전 날짜로 튀지 않게 한다 | ||
| private static let pendingRouteLifetime: TimeInterval = 5 * 60 | ||
|
|
||
| /// 알림 응답을 받으면 호출. 앱이 이미 활성 상태면 바로, 아니면 `sceneDidBecomeActive`에서 적용한다 | ||
| func handle(userInfo: [AnyHashable: Any]) { | ||
| guard let route = PushRoute(userInfo: userInfo) else { return } | ||
| pendingRoute = (route, Date()) | ||
| if UIApplication.shared.applicationState == .active { | ||
| applyPendingRoute() | ||
| } | ||
| } | ||
|
|
||
| /// 보관된 이동을 적용. 탭바가 아직 없으면(스플래시·로그인 중) 그대로 두고, 탭바가 나타날 때 다시 시도한다 | ||
| /// | ||
| /// 다음 런루프에서 실행해, 같은 시점에 예약된 "새로운 날 → 오늘로 초기화"(`didEnterNewDay`) 처리보다 뒤에 적용되게 한다 | ||
| func applyPendingRoute() { | ||
| DispatchQueue.main.async { [weak self] in | ||
| guard let self, let pending = self.pendingRoute else { return } | ||
| guard Date().timeIntervalSince(pending.receivedAt) < Self.pendingRouteLifetime else { | ||
| self.pendingRoute = nil | ||
| return | ||
| } | ||
| guard let tabBarController = Self.currentTabBarController() else { return } | ||
| self.pendingRoute = nil | ||
| tabBarController.showHomeMenu(on: pending.route.date, mealTime: pending.route.mealTime) | ||
| } | ||
| } | ||
|
|
||
| private static func currentTabBarController() -> CustomTabBarContainerController? { | ||
| let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene | ||
| let window = windowScene?.windows.first { $0.isKeyWindow } | ||
| return window?.rootViewController as? CustomTabBarContainerController | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: EAT-SSU/iOS
Length of output: 5319
🌐 Web query:
Apple UNUserNotificationCenterDelegate didReceive response withCompletionHandler callback queue main thread contract💡 Result:
pendingRoute접근을 메인 큐로 통일해 주세요.Apple 문서는
userNotificationCenter(_:didReceive:withCompletionHandler:)의 메인 큐 실행을 보장하지 않습니다. 현재handle은pendingRoute를 직접 변경하지만applyPendingRoute는 메인 큐에서 값을 읽고 지웁니다. 두 호출이 겹치면 새 경로를 지우고 이전 경로를 표시할 수 있습니다.🤖 Prompt for AI Agents
Source: Path instructions