-
Notifications
You must be signed in to change notification settings - Fork 2
Feat/#331 FileManagerService 생성 및 github json 다운로드 구현 #333
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
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,24 +8,30 @@ | |
|
|
||
| import UIKit | ||
|
|
||
| import Core | ||
| import Domain | ||
| import FeatureDependency | ||
| import MainFeature | ||
| import BusStopFeature | ||
| import RxSwift | ||
|
|
||
| final class AppCoordinator: Coordinator { | ||
| var parent: Coordinator? | ||
| var childs: [Coordinator] = [] | ||
| var navigationController: UINavigationController | ||
| public var coordinatorType: CoordinatorType = .app | ||
| private let coordinatorProvider = DefaultCoordinatorProvider() | ||
| private let disposeBag = DisposeBag() | ||
|
|
||
| init(navigationController: UINavigationController) { | ||
| self.navigationController = navigationController | ||
| } | ||
|
|
||
| func start() { | ||
| checkAndDownloadBusStationList() | ||
|
|
||
| let tabBarCoordinator = TabBarCoordinator( | ||
| navigationController: navigationController, | ||
| navigationController: navigationController, | ||
| coordinatorProvider: coordinatorProvider | ||
| ) | ||
| childs.append(tabBarCoordinator) | ||
|
|
@@ -43,4 +49,19 @@ final class AppCoordinator: Coordinator { | |
| childs.append(busStopCoordinator) | ||
| busStopCoordinator.start() | ||
| } | ||
|
|
||
| private func checkAndDownloadBusStationList() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| @Injected var useCase: UpdateBusStationListUseCase | ||
|
|
||
| useCase.execute() | ||
| .subscribe( | ||
| onError: { error in | ||
| print("🚏❌ bus_station_list.json 업데이트 실패: \(error)") | ||
| }, | ||
| onCompleted: { | ||
| print("🚏✅ bus_station_list.json 업데이트 확인 및 처리 완료") | ||
| } | ||
| ) | ||
| .disposed(by: disposeBag) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -34,6 +34,15 @@ public extension String { | |||||||||||||||||||||||||||||
| return serverKey | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| static var githubAccessToken: Self { | ||||||||||||||||||||||||||||||
| guard let any = Bundle.main.object(forInfoDictionaryKey: "GITHUB_ACCESS_TOKEN"), | ||||||||||||||||||||||||||||||
| let githubAccessToken = any as? String | ||||||||||||||||||||||||||||||
| else { | ||||||||||||||||||||||||||||||
| return "" | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| return githubAccessToken | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
Comment on lines
+37
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 에러 처리 방식의 일관성을 개선해주세요.
현재 구현에서는 토큰이 없어도 네트워크 요청이 시도되어 인증 실패가 발생할 수 있습니다: - static var githubAccessToken: Self {
- guard let any = Bundle.main.object(forInfoDictionaryKey: "GITHUB_ACCESS_TOKEN"),
- let githubAccessToken = any as? String
- else {
- return ""
- }
- return githubAccessToken
- }
+ static var githubAccessToken: Self {
+ guard let any = Bundle.main.object(forInfoDictionaryKey: "GITHUB_ACCESS_TOKEN"),
+ let githubAccessToken = any as? String
+ else { fatalError("GitHub Access Token이 설정되지 않았습니다") }
+ return githubAccessToken
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Comment on lines
+37
to
+44
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 토큰을 Core에 위치하게 되었을 때 다른 레이어에서도 접근할 수 있게 될 것 같은데, 특정 레이어에서만 필요한 데이터라서 여기에 위치시키는게 맞을지 고민이 되네요 |
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| /// domain url | ||||||||||||||||||||||||||||||
| static var domainURL: Self { | ||||||||||||||||||||||||||||||
| guard let any = Bundle.main.object( | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import Foundation | ||
|
|
||
| import Domain | ||
| import NetworkService | ||
| import Core | ||
|
|
||
| import RxSwift | ||
|
|
||
| public final class DefaultBusStationVersionRepository: BusStationVersionRepository { | ||
| @Injected private var networkService: NetworkService | ||
|
|
||
| @UserDefaultsWrapper(key: "busStationDataVersion", defaultValue: nil) | ||
| private var localVersion: String? | ||
|
|
||
| public init() { } | ||
|
|
||
| public func fetchRemoteVersion() -> Observable<BusStationVersion> { | ||
| let endPoint = GithubFileDownloadEndPoint( | ||
| repo: "BusStationData", | ||
| filePath: "bus_station_version.json" | ||
| ) | ||
| return networkService.request(endPoint: endPoint) | ||
| .decode(type: BusStationVersion.self, decoder: JSONDecoder()) | ||
| } | ||
|
|
||
| public func fetchLocalVersion() -> String? { | ||
| return localVersion | ||
| } | ||
|
|
||
| public func save(version: String) { | ||
| localVersion = version | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| import Foundation | ||
|
|
||
| import Domain | ||
| import FileManagerService | ||
| import NetworkService | ||
| import Core | ||
|
|
||
| import RxSwift | ||
|
|
||
| public final class DefaultGithubFileDownloadRepository: GithubFileDownloadRepository { | ||
| @Injected private var networkService: NetworkService | ||
| @Injected private var fileManagerService: FileManagerService | ||
|
|
||
| public init() { } | ||
|
|
||
| public func downloadFile( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 다운로드 받은 파일을 디바이스에 저장하는 의사결정은 UseCase에서 하는게 적합하지 않을까 생각이 드는데 어떻게 생각하시나요? |
||
| repo: String, | ||
| filePath: String, | ||
| directoryName: String, | ||
| fileName: String | ||
| ) -> Observable<Void> { | ||
| let endPoint = GithubFileDownloadEndPoint( | ||
| repo: repo, | ||
| filePath: filePath | ||
| ) | ||
| return networkService.request(endPoint: endPoint) | ||
| .flatMap { [weak self] data -> Observable<Void> in | ||
| guard let self = self else { | ||
| return .error(RxError.unknown) // Or a custom error | ||
| } | ||
| return self.fileManagerService.save( | ||
| data: data, | ||
| directoryName: directoryName, | ||
| fileName: fileName | ||
| ) | ||
| } | ||
|
Comment on lines
+27
to
+36
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| import Foundation | ||
|
|
||
| public struct BusStationVersion: Decodable { | ||
| public let busStationVersion: String | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| import Foundation | ||
|
|
||
| import RxSwift | ||
|
|
||
| public protocol BusStationVersionRepository { | ||
| /// 원격 저장소에서 최신 버스 정류장 데이터의 버전 정보를 가져옵니다. | ||
| func fetchRemoteVersion() -> Observable<BusStationVersion> | ||
|
|
||
| /// 로컬에 저장된 버스 정류장 데이터의 버전 정보를 가져옵니다. | ||
| func fetchLocalVersion() -> String? | ||
|
|
||
| /// 로컬에 새로운 버스 정류장 데이터의 버전 정보를 저장합니다. | ||
| func save(version: String) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 함수명이 위의 fetch 메서드들을 보았을 때 불명확한부분이 있는 것 같습니다. |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import Foundation | ||
|
|
||
| import RxSwift | ||
|
|
||
| public protocol GithubFileDownloadRepository { | ||
| func downloadFile( | ||
| repo: String, | ||
| filePath: String, | ||
| directoryName: String, | ||
| fileName: String | ||
| ) -> Observable<Void> | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| import Foundation | ||
| import Core | ||
| import RxSwift | ||
|
|
||
| public protocol UpdateBusStationListUseCase { | ||
| func execute() -> Observable<Void> | ||
| } | ||
|
Comment on lines
+5
to
+7
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 인터페이스는 인터페이스 폴더로 따로 작성해주시면 좋을 것 같습니다! |
||
|
|
||
| public final class DefaultUpdateBusStationListUseCase: UpdateBusStationListUseCase { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 객체와 인터페이스도 SwiftConcurrency 형태로 수정 부탁드립니다! |
||
| @Injected private var versionRepository: BusStationVersionRepository | ||
| @Injected private var fileDownloadRepository: GithubFileDownloadRepository | ||
|
|
||
| public init() { } | ||
|
|
||
| public func execute() -> Observable<Void> { | ||
| return versionRepository.fetchRemoteVersion() | ||
| .do(onNext: { remoteVersion in | ||
| print("🚏 버스정류장 원격 버전 확인: \(remoteVersion.busStationVersion)") | ||
| }, onError: { error in | ||
| print("🚏 버스정류장 원격 버전 확인 중 에러 발생: \(error)") | ||
| }) | ||
| .flatMap { [weak self] remoteVersion -> Observable<Void> in | ||
| guard let self = self else { return .error(RxError.unknown) } | ||
|
|
||
| let localVersion = self.versionRepository.fetchLocalVersion() | ||
| print("🚏 버스정류장 로컬 버전 확인: \(localVersion ?? "기존 파일 없음")") | ||
|
|
||
| let needsUpdate = localVersion != remoteVersion.busStationVersion | ||
| print("🚏 [버스정류장 버전 비교]") | ||
| print("로컬: \(localVersion ?? "파일 없음")") | ||
| print("원격: \(remoteVersion.busStationVersion)") | ||
|
|
||
| if needsUpdate { | ||
| return self.downloadAndSave( | ||
| newVersion: remoteVersion.busStationVersion | ||
| ) | ||
| } else { | ||
| print("🚏 버스정류장 정보가 이미 최신 버전입니다.") | ||
| return .just(()) | ||
| } | ||
| } | ||
| } | ||
|
Comment on lines
+15
to
+42
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 디버깅용 print는 없애거나 형태를 고정하는 걸 같이 의논해보면 어떨까요? 지금도 로깅 자체가 많이 깔리고 있어서 프린트가 많이 찍히는게 고민이 됩니다! 그리고 이전 코멘트랑 동일하게 withUnretained를 사용하면 약한 참조 관련해서 보일러 플레이트가 줄 것 같습니다. |
||
|
|
||
| private func downloadAndSave(newVersion: String) -> Observable<Void> { | ||
| print("🚏 downloadAndSave 호출. bus_station_list.json 다운로드") | ||
| return fileDownloadRepository.downloadFile( | ||
| repo: "BusStationData", | ||
| filePath: "bus_station_list.json", | ||
| directoryName: "jsons", | ||
| fileName: "bus_station_list.json" | ||
| ) | ||
|
Comment on lines
+46
to
+51
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 하드코딩된 저장소와 파일 정보를 설정 가능하게 만드는 것을 고려해보세요.
구성 객체나 의존성 주입을 통해 이 값들을 외부에서 설정할 수 있도록 개선하는 것을 권장합니다: public struct BusStationConfig {
let repository: String
let filePath: String
let directoryName: String
let fileName: String
}🤖 Prompt for AI Agents |
||
| .do(onNext: { _ in | ||
|
|
||
| self.versionRepository.save(version: newVersion) | ||
| print("🚏 로컬에 최신 버스정류장 정보 저장") | ||
| }, onError: { error in | ||
| print("🚏 파일 다운로드 중 에러 발생: \(error)") | ||
| }) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import ProjectDescription | ||
| import ProjectDescriptionHelpers | ||
|
|
||
| let project = Project(name: "FileManagerService") { | ||
| FileManagerService { | ||
| Domain() | ||
| FrameworkInfoPlist(marketingVersion: .marketingVersion) | ||
| } | ||
| } |

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.
다운로드를 하고 탭을 시작하게 되어있는데, 다운로드 실패 성공에 따라 시작을 다르게 되어야 원활하게 앱 사용이 될 것 같은데 의견 궁금하고 관련해서는 기획까지 이야기가 되어야할 것 같습니다.
retry를 해줄 수 있는 화면을 만든다던가 안내 화면을 띄운다던가, 혹시 다운로드되는 화면에서 프로그레스 뷰를 보여준다던가..!