From b53c4807649c45691f081649a2b86f5dfad07f31 Mon Sep 17 00:00:00 2001 From: Matt Kiazyk Date: Mon, 1 Feb 2021 21:18:49 -0600 Subject: [PATCH] Adds a DownloadFileSize when using Apple Data Source --- Xcodes.xcodeproj/project.pbxproj | 4 ++++ Xcodes/Backend/AppState+Update.swift | 2 +- Xcodes/Backend/AppState.swift | 3 ++- Xcodes/Backend/AvailableXcode.swift | 5 ++++- Xcodes/Backend/Double+formatBytes.swift | 24 +++++++++++++++++++++++ Xcodes/Backend/Downloads.swift | 1 + Xcodes/Backend/Xcode.swift | 9 ++++++++- Xcodes/Frontend/XcodeList/InfoPane.swift | 25 ++++++++++++++++++++++-- 8 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 Xcodes/Backend/Double+formatBytes.swift diff --git a/Xcodes.xcodeproj/project.pbxproj b/Xcodes.xcodeproj/project.pbxproj index 88c61ec..7e67ee0 100644 --- a/Xcodes.xcodeproj/project.pbxproj +++ b/Xcodes.xcodeproj/project.pbxproj @@ -100,6 +100,7 @@ CAFE4ABC25B7D54B0064FE51 /* UpdatesPreferencePane.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFE4ABB25B7D54B0064FE51 /* UpdatesPreferencePane.swift */; }; CAFFFED8259CDA5000903F81 /* XcodeListViewRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFFFED7259CDA5000903F81 /* XcodeListViewRow.swift */; }; E8977EA325C11E1500835F80 /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8977EA225C11E1500835F80 /* PreferencesView.swift */; }; + E8C891DA25C8DC6D00F1D3C4 /* Double+formatBytes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8C891D925C8DC6D00F1D3C4 /* Double+formatBytes.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -261,6 +262,7 @@ CAFFFED7259CDA5000903F81 /* XcodeListViewRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodeListViewRow.swift; sourceTree = ""; }; CAFFFEEE259CEAC400903F81 /* RingProgressViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RingProgressViewStyle.swift; sourceTree = ""; }; E8977EA225C11E1500835F80 /* PreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesView.swift; sourceTree = ""; }; + E8C891D925C8DC6D00F1D3C4 /* Double+formatBytes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Double+formatBytes.swift"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -411,6 +413,7 @@ CABFAA482593162500380FEE /* Bundle+InfoPlistValues.swift */, CA9FF87A2595293E00E47BAF /* DataSource.swift */, CABFA9BA2592EEEA00380FEE /* DateFormatter+.swift */, + E8C891D925C8DC6D00F1D3C4 /* Double+formatBytes.swift */, CABFA9B92592EEEA00380FEE /* Downloads.swift */, CAA858C325A2BE4E00ACF8C0 /* Downloader.swift */, CABFA9B22592EEEA00380FEE /* Entry+.swift */, @@ -792,6 +795,7 @@ CABFA9C72592EEEA00380FEE /* Entry+.swift in Sources */, CAE424B4259A764700B8B246 /* AppState+Install.swift in Sources */, CAE42487259A68A300B8B246 /* XcodeListCategory.swift in Sources */, + E8C891DA25C8DC6D00F1D3C4 /* Double+formatBytes.swift in Sources */, CAA858C425A2BE4E00ACF8C0 /* Downloader.swift in Sources */, E8977EA325C11E1500835F80 /* PreferencesView.swift in Sources */, CA9FF87B2595293E00E47BAF /* DataSource.swift in Sources */, diff --git a/Xcodes/Backend/AppState+Update.swift b/Xcodes/Backend/AppState+Update.swift index 660d2f0..dd5b7d7 100644 --- a/Xcodes/Backend/AppState+Update.swift +++ b/Xcodes/Backend/AppState+Update.swift @@ -133,7 +133,7 @@ extension AppState { else { return nil } let url = urlPrefix.appendingPathComponent(xcodeFile.remotePath) - return AvailableXcode(version: version, url: url, filename: String(xcodeFile.remotePath.suffix(fromLast: "/")), releaseDate: download.dateModified) + return AvailableXcode(version: version, url: url, filename: String(xcodeFile.remotePath.suffix(fromLast: "/")), releaseDate: download.dateModified, fileSize: xcodeFile.fileSize) } return xcodes } diff --git a/Xcodes/Backend/AppState.swift b/Xcodes/Backend/AppState.swift index 59f3089..ed465f8 100644 --- a/Xcodes/Backend/AppState.swift +++ b/Xcodes/Backend/AppState.swift @@ -459,7 +459,8 @@ class AppState: ObservableObject { requiredMacOSVersion: availableXcode.requiredMacOSVersion, releaseNotesURL: availableXcode.releaseNotesURL, sdks: availableXcode.sdks, - compilers: availableXcode.compilers + compilers: availableXcode.compilers, + downloadFileSize: availableXcode.fileSize ) } diff --git a/Xcodes/Backend/AvailableXcode.swift b/Xcodes/Backend/AvailableXcode.swift index c1810ba..92bef4f 100644 --- a/Xcodes/Backend/AvailableXcode.swift +++ b/Xcodes/Backend/AvailableXcode.swift @@ -13,6 +13,7 @@ public struct AvailableXcode: Codable { public let releaseNotesURL: URL? public let sdks: SDKs? public let compilers: Compilers? + public let fileSize: Double? public init( version: Version, @@ -22,7 +23,8 @@ public struct AvailableXcode: Codable { requiredMacOSVersion: String? = nil, releaseNotesURL: URL? = nil, sdks: SDKs? = nil, - compilers: Compilers? = nil + compilers: Compilers? = nil, + fileSize: Double? = nil ) { self.version = version self.url = url @@ -32,5 +34,6 @@ public struct AvailableXcode: Codable { self.releaseNotesURL = releaseNotesURL self.sdks = sdks self.compilers = compilers + self.fileSize = fileSize } } diff --git a/Xcodes/Backend/Double+formatBytes.swift b/Xcodes/Backend/Double+formatBytes.swift new file mode 100644 index 0000000..64bd843 --- /dev/null +++ b/Xcodes/Backend/Double+formatBytes.swift @@ -0,0 +1,24 @@ + +import Foundation + +extension Double { + func formatAsString() -> String { + guard self > 0 else { + return "0 bytes" + } + + // Adapted from http://stackoverflow.com/a/18650828 + let suffixes = ["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"] + let k: Double = 1000 + let i = floor(log(self) / log(k)) + + // Format number with thousands separator and everything below 1 GB with no decimal places. + let numberFormatter = NumberFormatter() + numberFormatter.maximumFractionDigits = i < 3 ? 0 : 1 + numberFormatter.numberStyle = .decimal + + let numberString = numberFormatter.string(from: NSNumber(value: self / pow(k, i))) ?? "Unknown" + let suffix = suffixes[Int(i)] + return "\(numberString) \(suffix)" + } +} diff --git a/Xcodes/Backend/Downloads.swift b/Xcodes/Backend/Downloads.swift index 45e2bb7..8694bd8 100644 --- a/Xcodes/Backend/Downloads.swift +++ b/Xcodes/Backend/Downloads.swift @@ -13,5 +13,6 @@ public struct Download: Codable { public struct File: Codable { public let remotePath: String + public let fileSize: Double } } diff --git a/Xcodes/Backend/Xcode.swift b/Xcodes/Backend/Xcode.swift index 481eb78..a8e448c 100644 --- a/Xcodes/Backend/Xcode.swift +++ b/Xcodes/Backend/Xcode.swift @@ -13,6 +13,7 @@ struct Xcode: Identifiable, CustomStringConvertible { let releaseNotesURL: URL? let sdks: SDKs? let compilers: Compilers? + let downloadFileSize: Double? init( version: Version, @@ -22,7 +23,8 @@ struct Xcode: Identifiable, CustomStringConvertible { requiredMacOSVersion: String? = nil, releaseNotesURL: URL? = nil, sdks: SDKs? = nil, - compilers: Compilers? = nil + compilers: Compilers? = nil, + downloadFileSize: Double? = nil ) { self.version = version self.installState = installState @@ -32,6 +34,7 @@ struct Xcode: Identifiable, CustomStringConvertible { self.releaseNotesURL = releaseNotesURL self.sdks = sdks self.compilers = compilers + self.downloadFileSize = downloadFileSize } var id: Version { version } @@ -39,4 +42,8 @@ struct Xcode: Identifiable, CustomStringConvertible { var description: String { version.appleDescription } + + var downloadFileSizeString: String? { + return downloadFileSize?.formatAsString() + } } diff --git a/Xcodes/Frontend/XcodeList/InfoPane.swift b/Xcodes/Frontend/XcodeList/InfoPane.swift index 6e0b6b7..99fddd8 100644 --- a/Xcodes/Frontend/XcodeList/InfoPane.swift +++ b/Xcodes/Frontend/XcodeList/InfoPane.swift @@ -56,7 +56,8 @@ struct InfoPane: View { compatibility(for: xcode) sdks(for: xcode) compilers(for: xcode) - + downloadFileSize(for: xcode) + Spacer() } } else { @@ -162,6 +163,24 @@ struct InfoPane: View { } } + @ViewBuilder + private func downloadFileSize(for xcode: Xcode) -> some View { + // if we've downloaded it no need to show the download size + if let downloadFileSize = xcode.downloadFileSizeString, case .notInstalled = xcode.installState { + VStack(alignment: .leading) { + Text("Download Size") + .font(.headline) + .frame(maxWidth: .infinity, alignment: .leading) + Text("\(downloadFileSize)") + .font(.subheadline) + .frame(maxWidth: .infinity, alignment: .leading) + } + } else { + EmptyView() + } + } + + @ViewBuilder private var empty: some View { VStack { @@ -199,7 +218,9 @@ struct InfoPane_Previews: PreviewProvider { llvm: .init(number: "2.3"), clang: .init(number: "7.3"), swift: .init(number: "5.3.2") - )) + ), + downloadFileSize: 242342424 + ) ] }) .previewDisplayName("Populated, Installed, Selected")