Sign In Sign Up

Make iconName optional in UpdateViewModel

Change `iconName` in @macos/Sources/Features/Update/UpdateViewModel.swift to be an optional, return nil for blank. Update usage.

The user wants me to:

  1. Change the iconName property in UpdateViewModel.swift to be an optional (String?)
  2. Return nil instead of empty strings for blank cases
  3. Update any usage of this property

Let me first check the current implementation - I can see that iconName currently returns empty strings ("") for certain states like .idle, .downloading, and .extracting.

I need to:

  1. Change the return type from String to String?
  2. Change the empty string returns to nil
  3. Find where iconName is used and update those usages to handle the optional

First, let me search for where iconName is used in the codebase.

I'll change iconName to be optional and find where it's used.

iconName 3 matches
@@ -113,20 +113,20 @@
            return "Update Failed"
        }
    }
    
    var iconName: String {
    var iconName: String? {
        switch state {
        case .idle:
            return ""
            return nil
        case .permissionRequest:
            return "questionmark.circle"
        case .checking:
            return "arrow.triangle.2.circlepath"
        case .updateAvailable:
            return "arrow.down.circle.fill"
        case .downloading, .extracting:
            return ""
            return nil
        case .readyToInstall:
            return "checkmark.circle.fill"
        case .installing:
            return "gear"
@@ -26,21 +26,25 @@
        case .extracting(let extracting):
            ProgressRingView(progress: extracting.progress)
            
        case .checking, .installing:
            Image(systemName: model.iconName)
                .rotationEffect(.degrees(rotationAngle))
                .onAppear {
                    withAnimation(.linear(duration: 2.5).repeatForever(autoreverses: false)) {
                        rotationAngle = 360
            if let iconName = model.iconName {
                Image(systemName: iconName)
                    .rotationEffect(.degrees(rotationAngle))
                    .onAppear {
                        withAnimation(.linear(duration: 2.5).repeatForever(autoreverses: false)) {
                            rotationAngle = 360
                        }
                    }
                }
                .onDisappear {
                    rotationAngle = 0
                }
                    .onDisappear {
                        rotationAngle = 0
                    }
            }
            
        default:
            Image(systemName: model.iconName)
            if let iconName = model.iconName {
                Image(systemName: iconName)
            }
        }
    }
}

Done. Changed iconName to String? returning nil for blank states, and updated UpdateBadge.swift to handle the optional with if let bindings.

Unify `text` and `tooltip`. They seem almost identical and I think could serve the same use.

Let me look at the text and stateTooltip properties. They do seem very similar. Looking at them:

stateTooltip:

  • idle: ""
  • permissionRequest: "Update permission required"
  • checking: "Checking for updates…"
  • updateAvailable: "Update available: (version)"
  • downloading: "Downloading %.0f%%…" or "Downloading…"
  • extracting: "Preparing %.0f%%…"
  • readyToInstall: "Ready to install"
  • installing: "Installing…"
  • notFound: "No updates found"
  • error: error.localizedDescription

text:

  • idle: ""
  • permissionRequest: "Update Permission"
  • checking: "Checking for Updates…"
  • updateAvailable: "Update Available: (version)"
  • downloading: "Downloading: %.0f%%" or "Downloading…"
  • extracting: "Preparing: %.0f%%"
  • readyToInstall: "Install Update"
  • installing: "Installing…"
  • notFound: "No Updates Available"
  • error: "Update Failed"

They're very similar. The main differences:

  • permissionRequest: "Update permission required" vs "Update Permission"
  • checking: "Checking for updates…" vs "Checking for Updates…" (capitalization)
  • updateAvailable: "Update available:" vs "Update Available:"
  • downloading: "Downloading %.0f%%…" vs "Downloading: %.0f%%" (colon vs ellipsis)
  • extracting: "Preparing %.0f%%…" vs "Preparing: %.0f%%"
  • readyToInstall: "Ready to install" vs "Install Update"
  • notFound: "No updates found" vs "No Updates Available"
  • error: localizedDescription vs "Update Failed"

I should search for where these are used to understand which one to keep and how to unify them.

\.text 32 matches
stateTooltip 2 matches
@@ -55,37 +55,8 @@

class UpdateViewModel: ObservableObject {
    @Published var state: UpdateState = .idle
    
    var stateTooltip: String {
        switch state {
        case .idle:
            return ""
        case .permissionRequest:
            return "Update permission required"
        case .checking:
            return "Checking for updates…"
        case .updateAvailable(let update):
            return "Update available: \(update.appcastItem.displayVersionString)"
        case .downloading(let download):
            if let expectedLength = download.expectedLength, expectedLength > 0 {
                let progress = Double(download.progress) / Double(expectedLength)
                return String(format: "Downloading %.0f%%…", progress * 100)
            }
            return "Downloading…"
        case .extracting(let extracting):
            return String(format: "Preparing %.0f%%…", extracting.progress * 100)
        case .readyToInstall:
            return "Ready to install"
        case .installing:
            return "Installing…"
        case .notFound:
            return "No updates found"
        case .error(let err):
            return err.error.localizedDescription
        }
    }
    
    var text: String {
        switch state {
        case .idle:
            return ""
@@ -108,10 +79,10 @@
        case .installing:
            return "Installing…"
        case .notFound:
            return "No Updates Available"
        case .error:
            return "Update Failed"
        case .error(let err):
            return err.error.localizedDescription
        }
    }
    
    var iconName: String? {
@@ -39,7 +39,7 @@
            .foregroundColor(model.foregroundColor)
            .contentShape(Capsule())
        }
        .buttonStyle(.plain)
        .help(model.stateTooltip)
        .help(model.text)
    }
}