From 30420052267d7a1404f6b88fde09f8af40d55082 Mon Sep 17 00:00:00 2001 From: cdricms <36056008+cdricms@users.noreply.github.com> Date: Fri, 24 Jul 2026 18:15:36 +0200 Subject: [PATCH] refactor(apple): type the merged relay/network resources Convert master's raw-string localization keys and SF Symbols in the new relay/network code to typed accessors, matching this branch's typed-resources convention: relay mode labels/descriptions, NetworkSettings strings, the endpoint id and relay-validation messages (now typed L10n functions), and SF Symbols via SFSafeSymbols. Retype the model's relayApplyErrorKey from a raw String key to String.LocalizationValue so no loose key literals remain in the settings layer. --- .../Features/Send/TransferDetailsView.swift | 4 +- .../Features/Settings/SettingsModel.swift | 22 ++++---- .../Features/Settings/SettingsScreen.swift | 18 +++---- .../Features/Settings/SettingsSections.swift | 51 ++++++++----------- 4 files changed, 43 insertions(+), 52 deletions(-) diff --git a/apple/VniDrop/Features/Send/TransferDetailsView.swift b/apple/VniDrop/Features/Send/TransferDetailsView.swift index cc86990..b5b51e1 100644 --- a/apple/VniDrop/Features/Send/TransferDetailsView.swift +++ b/apple/VniDrop/Features/Send/TransferDetailsView.swift @@ -303,9 +303,9 @@ struct TransferSharePanel: View { image.interpolation(.none).resizable().scaledToFit().padding(14) } else { VStack(spacing: 10) { - Image(systemName: "qrcode") + Image(systemSymbol: .qrcode) .font(.system(size: 36, weight: .medium)) - Text(LocalizedStringKey("transfer_qr_unavailable")) + Text(String(localized: L10n.Transfer.qrUnavailable)) .font(VniType.bodySmall) .multilineTextAlignment(.center) } diff --git a/apple/VniDrop/Features/Settings/SettingsModel.swift b/apple/VniDrop/Features/Settings/SettingsModel.swift index 308a363..b5d3c3f 100644 --- a/apple/VniDrop/Features/Settings/SettingsModel.swift +++ b/apple/VniDrop/Features/Settings/SettingsModel.swift @@ -52,7 +52,7 @@ struct SettingsState: Equatable { var isApplyingRelayConfiguration = false var hasActiveNetworkWork = false var endpointId: String? - var relayApplyErrorKey: String? + var relayApplyErrorKey: String.LocalizationValue? var deviceInfo: DeviceInfo? var appVersion = "" var isLoadingDeviceInfo = false @@ -169,7 +169,7 @@ final class SettingsModel: ObservableObject { || coreState.transfers.contains(where: { $0.status.isActiveTransfer }) self.state.hasActiveNetworkWork = hasActiveWork self.state.endpointId = coreState.status?.endpointId - if !hasActiveWork && self.state.relayApplyErrorKey == "relay_apply_active_transfers" { + if !hasActiveWork && self.state.relayApplyErrorKey == L10n.Relay.applyActiveTransfers { self.state.relayApplyErrorKey = nil } } @@ -296,8 +296,8 @@ final class SettingsModel: ObservableObject { || coreState.transfers.contains(where: { $0.status.isActiveTransfer }) guard !hasActiveWork else { state.hasActiveNetworkWork = true - state.relayApplyErrorKey = "relay_apply_active_transfers" - messages.show(UiMessage(text: .resource("relay_apply_active_transfers"), tone: .warning)) + state.relayApplyErrorKey = L10n.Relay.applyActiveTransfers + messages.show(UiMessage(text: .resource(L10n.Relay.applyActiveTransfers), tone: .warning)) return } @@ -316,16 +316,16 @@ final class SettingsModel: ObservableObject { preferences.setRelayConfiguration(configuration) state.isApplyingRelayConfiguration = false state.relayConfigurationIsDirty = false - messages.show(UiMessage(text: .resource("relay_settings_applied"), tone: .success)) + messages.show(UiMessage(text: .resource(L10n.Relay.settingsApplied), tone: .success)) case .failure(let error): if let lifecycleError = error as? CoreNetworkLifecycleError { state.isApplyingRelayConfiguration = false switch lifecycleError { case .activeNetworkWork: state.hasActiveNetworkWork = true - state.relayApplyErrorKey = "relay_apply_active_transfers" + state.relayApplyErrorKey = L10n.Relay.applyActiveTransfers case .transitionInProgress: - state.relayApplyErrorKey = "relay_apply_failed" + state.relayApplyErrorKey = L10n.Relay.applyFailed } return } @@ -335,11 +335,11 @@ final class SettingsModel: ObservableObject { ) state.isApplyingRelayConfiguration = false if case .success = rollbackResult { - state.relayApplyErrorKey = "relay_apply_failed" - messages.show(UiMessage(text: .resource("relay_apply_failed"), tone: .error)) + state.relayApplyErrorKey = L10n.Relay.applyFailed + messages.show(UiMessage(text: .resource(L10n.Relay.applyFailed), tone: .error)) } else { - state.relayApplyErrorKey = "relay_restore_failed" - messages.show(UiMessage(text: .resource("relay_restore_failed"), tone: .error)) + state.relayApplyErrorKey = L10n.Relay.restoreFailed + messages.show(UiMessage(text: .resource(L10n.Relay.restoreFailed), tone: .error)) } } } diff --git a/apple/VniDrop/Features/Settings/SettingsScreen.swift b/apple/VniDrop/Features/Settings/SettingsScreen.swift index 2fa7346..5b436fa 100644 --- a/apple/VniDrop/Features/Settings/SettingsScreen.swift +++ b/apple/VniDrop/Features/Settings/SettingsScreen.swift @@ -125,19 +125,19 @@ private struct SettingsSectionContent: View { func relayModeLabel(_ mode: RelayPreferenceMode) -> String { switch mode { - case .automatic: return String(localized: "relay_mode_automatic") - case .strictCustom: return String(localized: "relay_mode_custom") - case .customWithDirectFallback: return String(localized: "relay_mode_custom_direct_fallback") - case .localOnly: return String(localized: "relay_mode_local_only") + case .automatic: return String(localized: L10n.Relay.modeAutomatic) + case .strictCustom: return String(localized: L10n.Relay.modeCustom) + case .customWithDirectFallback: return String(localized: L10n.Relay.modeCustomDirectFallback) + case .localOnly: return String(localized: L10n.Relay.modeLocalOnly) } } -func relayModeDescriptionKey(_ mode: RelayPreferenceMode) -> String { +func relayModeDescription(_ mode: RelayPreferenceMode) -> String.LocalizationValue { switch mode { - case .automatic: return "relay_mode_automatic_description" - case .strictCustom: return "relay_mode_custom_description" - case .customWithDirectFallback: return "relay_mode_custom_direct_fallback_description" - case .localOnly: return "relay_mode_local_only_description" + case .automatic: return L10n.Relay.modeAutomaticDescription + case .strictCustom: return L10n.Relay.modeCustomDescription + case .customWithDirectFallback: return L10n.Relay.modeCustomDirectFallbackDescription + case .localOnly: return L10n.Relay.modeLocalOnlyDescription } } diff --git a/apple/VniDrop/Features/Settings/SettingsSections.swift b/apple/VniDrop/Features/Settings/SettingsSections.swift index 0e8605a..88f21af 100644 --- a/apple/VniDrop/Features/Settings/SettingsSections.swift +++ b/apple/VniDrop/Features/Settings/SettingsSections.swift @@ -93,22 +93,22 @@ struct NetworkSettings: View { } header: { Text(String(localized: L10n.Settings.networkTitle)) } footer: { - Text(LocalizedStringKey(relayModeDescriptionKey(model.state.relayMode))) + Text(String(localized: relayModeDescription(model.state.relayMode))) } Section { Label { - Text(LocalizedStringKey("relay_privacy_description")) + Text(String(localized: L10n.Relay.privacyDescription)) .fixedSize(horizontal: false, vertical: true) } icon: { - Image(systemName: "lock.shield") + Image(systemSymbol: .lockShield) } .foregroundStyle(.secondary) } if let endpointId = model.state.endpointId, !endpointId.isEmpty { Section { - Text(String(format: String(localized: "approval_endpoint_id"), endpointId)) + Text(L10n.Approval.endpointId(deviceId: endpointId)) .font(.footnote.monospaced()) .textSelection(.enabled) } @@ -118,10 +118,10 @@ struct NetworkSettings: View { Section { if model.state.relayMode == .strictCustom { Label { - Text(LocalizedStringKey("relay_strict_warning")) + Text(String(localized: L10n.Relay.strictWarning)) .fixedSize(horizontal: false, vertical: true) } icon: { - Image(systemName: "exclamationmark.shield.fill") + Image(systemSymbol: .exclamationmarkShieldFill) } .foregroundStyle(.orange) } @@ -154,10 +154,10 @@ struct NetworkSettings: View { Button(role: .destructive) { model.removeRelayURL(at: index) } label: { - Image(systemName: "minus.circle.fill") + Image(systemSymbol: .minusCircleFill) } .buttonStyle(.borderless) - .accessibilityLabel(Text(LocalizedStringKey("relay_remove_url"))) + .accessibilityLabel(Text(String(localized: L10n.Relay.removeUrl))) .disabled(model.state.isApplyingRelayConfiguration) } @@ -170,16 +170,16 @@ struct NetworkSettings: View { } Button(action: model.addRelayURL) { - Label(String(localized: "relay_add_url"), systemImage: "plus.circle") + Label(String(localized: L10n.Relay.addUrl), systemSymbol: .plusCircle) } .disabled( model.state.relayURLs.count >= RelayConfigurationValidator.maximumRelayCount || model.state.isApplyingRelayConfiguration ) } header: { - Text(LocalizedStringKey("relay_custom_urls_label")) + Text(String(localized: L10n.Relay.customUrlsLabel)) } footer: { - Text(LocalizedStringKey("relay_custom_urls_help")) + Text(String(localized: L10n.Relay.customUrlsHelp)) } } @@ -188,7 +188,7 @@ struct NetworkSettings: View { Label { Text(relayValidationMessage(error)) } icon: { - Image(systemName: "exclamationmark.triangle.fill") + Image(systemSymbol: .exclamationmarkTriangleFill) } .foregroundStyle(.red) } @@ -197,13 +197,9 @@ struct NetworkSettings: View { if model.state.hasActiveNetworkWork || model.state.relayApplyErrorKey != nil { Section { Label { - Text(LocalizedStringKey( - model.state.hasActiveNetworkWork - ? "relay_apply_active_transfers" - : model.state.relayApplyErrorKey ?? "relay_apply_failed" - )) + Text(String(localized: model.state.hasActiveNetworkWork ? L10n.Relay.applyActiveTransfers : (model.state.relayApplyErrorKey ?? L10n.Relay.applyFailed))) } icon: { - Image(systemName: "exclamationmark.triangle.fill") + Image(systemSymbol: .exclamationmarkTriangleFill) } .foregroundStyle(.red) } @@ -212,9 +208,7 @@ struct NetworkSettings: View { Section { Button(action: model.applyRelayConfiguration) { HStack { - Text(LocalizedStringKey( - model.state.isApplyingRelayConfiguration ? "relay_applying" : "relay_apply" - )) + Text(String(localized: model.state.isApplyingRelayConfiguration ? L10n.Relay.applying : L10n.Relay.apply)) if model.state.isApplyingRelayConfiguration { Spacer() ProgressView() @@ -227,7 +221,7 @@ struct NetworkSettings: View { || model.state.hasActiveNetworkWork ) } footer: { - Text(LocalizedStringKey("relay_apply_restart_description")) + Text(String(localized: L10n.Relay.applyRestartDescription)) } } } @@ -235,18 +229,15 @@ struct NetworkSettings: View { private func relayValidationMessage(_ error: RelayConfigurationValidationError) -> String { switch error { case .missingURL: - return String(localized: "relay_validation_missing_url") + return String(localized: L10n.Relay.validationMissingUrl) case .tooManyURLs: - return String( - format: String(localized: "relay_validation_too_many_urls"), - RelayConfigurationValidator.maximumRelayCount - ) + return L10n.Relay.validationTooManyUrls(maximum: RelayConfigurationValidator.maximumRelayCount) case .httpsRequired(let index): - return String(format: String(localized: "relay_validation_https_required"), index + 1) + return L10n.Relay.validationHttpsRequired(line: index + 1) case .invalidURL(let index): - return String(format: String(localized: "relay_validation_invalid_url"), index + 1) + return L10n.Relay.validationInvalidUrl(line: index + 1) case .duplicateURL(let index): - return String(format: String(localized: "relay_validation_duplicate_url"), index + 1) + return L10n.Relay.validationDuplicateUrl(line: index + 1) } }