-
Notifications
You must be signed in to change notification settings - Fork 0
[#124] 캐시 디렉토리의 용량을 보여주고 제거하는 UI를 구성한다 #129
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
Changes from all commits
d275314
b038dd6
a4ad50c
bc02512
c33f277
c32f62a
e1695be
1b98bae
8d4574c
6d9b5e3
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 |
|---|---|---|
|
|
@@ -10,6 +10,7 @@ import Foundation | |
| final class SettingViewModel: Store { | ||
| struct State { | ||
| var theme = "" | ||
| var dirSize: Int64 = 0 | ||
| var isLoading = false | ||
| var showAlert: Bool = false | ||
| var alertTitle: String = "" | ||
|
|
@@ -23,8 +24,11 @@ final class SettingViewModel: Store { | |
| case setAlert(isPresented: Bool, type: AlertType? = nil) | ||
| case setLoading(Bool) | ||
| case setTheme(String) | ||
| case updateDirSize | ||
| case tapDeleteAuthButton | ||
| case tapSignOutButton | ||
| case tapRemoveCacheButton | ||
| case confirmRemoveCache | ||
| } | ||
|
|
||
| enum SideEffect { | ||
|
|
@@ -33,15 +37,14 @@ final class SettingViewModel: Store { | |
| } | ||
|
|
||
| enum AlertType { | ||
| case signOut, delete, error | ||
| case signOut, deleteAuth, error, removeCache | ||
| } | ||
|
|
||
| @Published private(set) var state = State() | ||
| private let deleteAuthuseCase: DeleteAuthUseCase | ||
| private let signOutUseCase: SignOutUseCase | ||
| private let sessionUseCase: AuthSessionUseCase | ||
|
|
||
| @Published private(set) var state = State() | ||
|
|
||
| init( | ||
| deleteAuthUseCase: DeleteAuthUseCase, | ||
| signOutUseCase: SignOutUseCase, | ||
|
|
@@ -60,10 +63,22 @@ final class SettingViewModel: Store { | |
| state.isLoading = value | ||
| case .setTheme(let value): | ||
| state.theme = value | ||
| case .updateDirSize: | ||
| state.dirSize = dirSizeInBytes() | ||
| case .tapDeleteAuthButton: | ||
| return [.deleteAuth] | ||
| case .tapSignOutButton: | ||
| return [.signOut] | ||
| case .tapRemoveCacheButton: | ||
| setAlert(&state, isPresented: true, type: .removeCache) | ||
| case .confirmRemoveCache: | ||
| do { | ||
| setAlert(&state, isPresented: false) | ||
| try clearCacheDirectory() | ||
| state.dirSize = dirSizeInBytes() | ||
| } catch { | ||
| setAlert(&state, isPresented: true, type: .error) | ||
| } | ||
| } | ||
| return [] | ||
| } | ||
|
|
@@ -101,23 +116,81 @@ private extension SettingViewModel { | |
| func setAlert( | ||
| _ state: inout State, | ||
| isPresented: Bool, | ||
| type: AlertType? | ||
| type: AlertType? = nil | ||
| ) { | ||
| switch type { | ||
| case .signOut: | ||
| state.alertTitle = "로그아웃" | ||
| state.alertMessage = "로그아웃 하시겠습니까?" | ||
| case .delete: | ||
| case .deleteAuth: | ||
| state.alertTitle = "정말 탈퇴하시겠습니까?" | ||
| state.alertMessage = "회원 탈퇴가 진행되면 모든 데이터가 지워지고 복구할 수 없습니다." | ||
| case .error: | ||
| state.alertTitle = "오류" | ||
| state.alertMessage = "문제가 발생했습니다. 잠시 후 다시 시도해주세요." | ||
| case .removeCache: | ||
| state.alertTitle = "임시 데이터 삭제" | ||
| state.alertMessage = "임시 데이터를 삭제하고 정리합니다.\n계속하시겠습니까?" | ||
| case .none: | ||
| state.alertTitle = "" | ||
| state.alertMessage = "" | ||
| } | ||
| state.showAlert = isPresented | ||
| state.alertType = type | ||
| } | ||
|
|
||
| func dirSizeInBytes() -> Int64 { | ||
| do { | ||
| let cachesDir = try FileManager.default.url( | ||
| for: .cachesDirectory, | ||
| in: .userDomainMask, | ||
| appropriateFor: nil, | ||
| create: false | ||
| ) | ||
| guard FileManager.default.fileExists(atPath: cachesDir.path) else { return 0 } | ||
| return directorySize(at: cachesDir) | ||
| } catch { | ||
| return 0 | ||
| } | ||
| } | ||
|
|
||
| private func directorySize(at url: URL) -> Int64 { | ||
| guard FileManager.default.fileExists(atPath: url.path) else { return 0 } | ||
| guard let enumerator = FileManager.default.enumerator( | ||
| at: url, | ||
| includingPropertiesForKeys: [.isRegularFileKey, .fileSizeKey], | ||
| options: [.skipsHiddenFiles] | ||
| ) else { | ||
| return 0 | ||
| } | ||
|
|
||
| var total: Int64 = 0 | ||
| for case let fileURL as URL in enumerator { | ||
| guard let resourceValues = try? fileURL.resourceValues(forKeys: [.isRegularFileKey, .fileSizeKey]), | ||
| resourceValues.isRegularFile == true, | ||
| let fileSize = resourceValues.fileSize else { | ||
| continue | ||
| } | ||
| total += Int64(fileSize) | ||
| } | ||
| return total | ||
|
Comment on lines
+157
to
+176
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.
|
||
| } | ||
|
|
||
| private func clearCacheDirectory() throws { | ||
| let cachesDir = try FileManager.default.url( | ||
| for: .cachesDirectory, | ||
| in: .userDomainMask, | ||
| appropriateFor: nil, | ||
| create: false | ||
| ) | ||
| guard FileManager.default.fileExists(atPath: cachesDir.path) else { return } | ||
| let contents = try FileManager.default.contentsOfDirectory( | ||
| at: cachesDir, | ||
| includingPropertiesForKeys: nil, | ||
| options: [.skipsHiddenFiles] | ||
| ) | ||
| for url in contents { | ||
| try FileManager.default.removeItem(at: url) | ||
| } | ||
| } | ||
|
Comment on lines
+179
to
+195
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.
private func clearCacheDirectory() throws {
let cachesDir = try FileManager.default.url(
for: .cachesDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: true
)
try FileManager.default.removeItem(at: cachesDir)
// 필요하다면 디렉토리를 다시 생성할 수 있습니다.
// try FileManager.default.createDirectory(at: cachesDir, withIntermediateDirectories: true, attributes: nil)
}
Owner
Author
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 |
|---|---|---|
|
|
@@ -358,6 +358,9 @@ | |
| }, | ||
| "읽지 않음" : { | ||
|
|
||
| }, | ||
| "임시 데이터 삭제" : { | ||
|
|
||
| }, | ||
| "작년" : { | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -12,7 +12,6 @@ struct SettingView: View { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Environment(\.diContainer) var container: DIContainer | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @StateObject var viewModel: SettingViewModel | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @EnvironmentObject var router: NavigationRouter | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @State private var navigationPath: Path? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var body: some View { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Form { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -28,16 +27,27 @@ struct SettingView: View { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .foregroundStyle(Color.gray) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .onAppear { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| viewModel.send(.setTheme(theme.localizedName)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Button { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| router.push(Path.pushNotification) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } label: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Text("알림") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .foregroundStyle(Color.primary) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let dirSize = viewModel.state.dirSize | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Button { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| viewModel.send(.tapRemoveCacheButton) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } label: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| HStack { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Text("임시 데이터 삭제") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .foregroundStyle(dirSize == 0 ? Color.secondary : .primary) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Spacer() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Text(formatFileSize(bytes: dirSize)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .foregroundStyle(Color.secondary.opacity(dirSize == 0 ? 0 : 1)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .disabled(dirSize == 0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+38
to
+50
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.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Section { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -79,7 +89,6 @@ struct SettingView: View { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Section { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Button { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| router.push(Path.account) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } label: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Text("계정 연동") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -93,7 +102,7 @@ struct SettingView: View { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| HStack { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Spacer() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Button(role: .destructive, action: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| viewModel.send(.setAlert(isPresented: true, type: .delete)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| viewModel.send(.setAlert(isPresented: true, type: .deleteAuth)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Text("회원 탈퇴") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .font(.headline) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -139,6 +148,10 @@ struct SettingView: View { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| LoadingView() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .onAppear { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| viewModel.send(.setTheme(theme.localizedName)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| viewModel.send(.updateDirSize) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private enum Path: Hashable { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -155,17 +168,41 @@ struct SettingView: View { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Button("확인", role: .destructive) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| viewModel.send(.tapSignOutButton) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case .delete: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case .deleteAuth: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Button("취소", role: .cancel) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| viewModel.send(.setAlert(isPresented: false)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Button("탈퇴", role: .destructive) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| viewModel.send(.tapDeleteAuthButton) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case .removeCache: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Button("취소", role: .cancel) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| viewModel.send(.setAlert(isPresented: false)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Button("확인", role: .destructive) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| viewModel.send(.confirmRemoveCache) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case .error, .none: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Button("확인", role: .cancel) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| viewModel.send(.setAlert(isPresented: false)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private func formatFileSize(bytes: Int64) -> String { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let units = ["B", "KB", "MB", "GB"] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var value = Double(max(bytes, 0)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var unitIndex = 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| while 1024.0 <= value && unitIndex < units.count - 1 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value /= 1024.0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| unitIndex += 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let truncated = floor(value * 100.0) / 100.0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let numberString = truncated.formatted( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .number.precision(.fractionLength(0...2)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "\(numberString)\(units[unitIndex])" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+192
to
+207
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.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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.
dirSizeInBytes()함수에서FileManager.default.url호출 시create: true로 설정되어 있습니다. 캐시 디렉토리가 존재하지 않을 경우 생성하는 것은 좋지만, 단순히 크기를 측정하는 함수에서 디렉토리를 생성하는 부수 효과를 가지는 것은 함수의 단일 책임 원칙에 위배될 수 있습니다. 디렉토리 생성은 캐시를 사용하기 전에 한 번만 수행하는 것이 더 적절합니다.