From f0549c425811dc8a43755252041820233f7d4cbd Mon Sep 17 00:00:00 2001 From: Bradley Mackey Date: Mon, 14 Sep 2026 13:24:04 +0400 Subject: [PATCH] Delete hand-rolled UI superseded by native SwiftUI APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes six view files and two `Color` members that reimplement SwiftUI APIs the app already uses elsewhere. Every one has zero call sites outside its own file, so this is deletion only — no behaviour change. | Deleted | Superseded by | | --- | --- | | `SearchTextField.swift` | `.searchable`, already used at `VaultItemFeedView.swift:80` | | `TextArea.swift` | `TextEditor`, already used in three places | | `TextEditingView.swift` + `TextViewViewController.swift` | `TextEditor` | | `OTPCodeLabels.swift` | the inline `labelsStack` copies that replaced it | | `View+Center.swift` | `.frame(maxWidth:)` | | `Color.contrastingForegroundColor` / `.contrastingBackgroudColor` | unreferenced | `TextEditingView` carried a doc comment explaining it existed to dodge "bugs we've experienced with raw SwiftUI text editors". That workaround was never in service — `SecureNoteDetailView` and `BackupCreatePDFView` both use a plain `TextEditor` — so nothing regresses by removing it. `HorizontallyCenter` (`HStack { Spacer(); content; Spacer() }`) is replaced at its nine call sites by `.frame(maxWidth: .infinity)`, which centres its child at its ideal size the same way. Six of those sites are buttons whose pill chrome is applied by an inner modifier, so the button keeps its intrinsic size rather than stretching. ## Verification Local, iPhone 18 Pro Max / iOS 27.0: - `xcodebuild build-for-testing` — `TEST BUILD SUCCEEDED`, no warnings (`-warnings-as-errors` is on package-wide) - Full suite, `-parallel-testing-enabled NO` — `TEST EXECUTE SUCCEEDED`, 2834 passed, 0 failures (1417 tests across 12 bundles, run under both the Default and TSAN configurations) - **Zero snapshots re-recorded.** That is the check that matters here: if any of these types had still been reachable, an image would have moved. - `make format` + `make lint` — clean ⚠️ Automatic CI is still disabled (#548), so this is local verification only. Co-Authored-By: Claude Opus 5 (1M context) --- .../Backup/BackupImportCodeScannerView.swift | 2 +- .../Detail/EncryptedItemDetailView.swift | 2 +- .../Views/Detail/OTP/OTPCodeCreateView.swift | 2 +- .../Views/Detail/OTP/OTPCodeDetailView.swift | 4 +- .../SecureNote/SecureNoteDetailView.swift | 2 +- .../VaultDetailEncryptionEditView.swift | 4 +- .../Views/General/Color+Constrast.swift | 10 ---- .../Views/General/SearchTextField.swift | 44 --------------- .../VaultiOS/Views/General/TextArea.swift | 41 -------------- .../Views/General/TextEditingView.swift | 28 ---------- .../General/TextViewViewController.swift | 55 ------------------- .../VaultiOS/Views/General/View+Center.swift | 27 --------- .../Views/Previews/OTP/OTPCodeLabels.swift | 48 ---------------- .../Views/Tags/VaultTagDetailView.swift | 2 +- 14 files changed, 9 insertions(+), 262 deletions(-) delete mode 100644 Vault/Sources/VaultiOS/Views/General/SearchTextField.swift delete mode 100644 Vault/Sources/VaultiOS/Views/General/TextArea.swift delete mode 100644 Vault/Sources/VaultiOS/Views/General/TextEditingView.swift delete mode 100644 Vault/Sources/VaultiOS/Views/General/TextViewViewController.swift delete mode 100644 Vault/Sources/VaultiOS/Views/General/View+Center.swift delete mode 100644 Vault/Sources/VaultiOS/Views/Previews/OTP/OTPCodeLabels.swift diff --git a/Vault/Sources/VaultiOS/Views/Backup/BackupImportCodeScannerView.swift b/Vault/Sources/VaultiOS/Views/Backup/BackupImportCodeScannerView.swift index d86445d61..046adb5c2 100644 --- a/Vault/Sources/VaultiOS/Views/Backup/BackupImportCodeScannerView.swift +++ b/Vault/Sources/VaultiOS/Views/Backup/BackupImportCodeScannerView.swift @@ -72,7 +72,7 @@ struct BackupImportCodeScannerView: View { isImagePickerVisible: $isCodeImagePickerGalleryVisible, ) .padding() - .modifier(HorizontallyCenter()) + .frame(maxWidth: .infinity) } } } diff --git a/Vault/Sources/VaultiOS/Views/Detail/EncryptedItemDetailView.swift b/Vault/Sources/VaultiOS/Views/Detail/EncryptedItemDetailView.swift index 5e87f9296..10f825b8d 100644 --- a/Vault/Sources/VaultiOS/Views/Detail/EncryptedItemDetailView.swift +++ b/Vault/Sources/VaultiOS/Views/Detail/EncryptedItemDetailView.swift @@ -98,7 +98,7 @@ struct EncryptedItemDetailView: View { .modifier(ProminentButtonModifier()) .animation(.easeOut, value: viewModel.state) .padding() - .modifier(HorizontallyCenter()) + .frame(maxWidth: .infinity) .disabled(!viewModel.canStartDecryption) } .onChange(of: viewModel.enteredEncryptionPassword) { _, _ in diff --git a/Vault/Sources/VaultiOS/Views/Detail/OTP/OTPCodeCreateView.swift b/Vault/Sources/VaultiOS/Views/Detail/OTP/OTPCodeCreateView.swift index bea003f04..dc99f009f 100644 --- a/Vault/Sources/VaultiOS/Views/Detail/OTP/OTPCodeCreateView.swift +++ b/Vault/Sources/VaultiOS/Views/Detail/OTP/OTPCodeCreateView.swift @@ -121,7 +121,7 @@ struct OTPCodeCreateView< isImagePickerVisible: $isCodeImagePickerGalleryVisible, ) .padding() - .modifier(HorizontallyCenter()) + .frame(maxWidth: .infinity) } } } diff --git a/Vault/Sources/VaultiOS/Views/Detail/OTP/OTPCodeDetailView.swift b/Vault/Sources/VaultiOS/Views/Detail/OTP/OTPCodeDetailView.swift index 2c63f30ce..266979438 100644 --- a/Vault/Sources/VaultiOS/Views/Detail/OTP/OTPCodeDetailView.swift +++ b/Vault/Sources/VaultiOS/Views/Detail/OTP/OTPCodeDetailView.swift @@ -300,7 +300,7 @@ struct OTPCodeDetailView 0.9 } diff --git a/Vault/Sources/VaultiOS/Views/General/SearchTextField.swift b/Vault/Sources/VaultiOS/Views/General/SearchTextField.swift deleted file mode 100644 index ec6e94e1c..000000000 --- a/Vault/Sources/VaultiOS/Views/General/SearchTextField.swift +++ /dev/null @@ -1,44 +0,0 @@ -import Foundation -import SwiftUI - -struct SearchTextField: View { - var title: String - @Binding var text: String - - @FocusState private var isFocused: Bool - let radius = 8.0 - - var body: some View { - HStack(spacing: 8) { - TextField(title, text: $text) - .textFieldStyle(.plain) - .keyboardType(.default) - .autocorrectionDisabled() - .textInputAutocapitalization(.never) - .submitLabel(.done) - .focused($isFocused) - .padding(radius) - .background(backgroundColor) - .clipShape(RoundedRectangle(cornerRadius: radius)) - - if text != "" { - cancelButton - } - } - .animation(.snappy, value: text) - } - - private var cancelButton: some View { - Button { - text = "" - isFocused = false - } label: { - Text("Cancel") - .foregroundStyle(.primary, .secondary) - } - } - - private var backgroundColor: Color { - Color(UIColor.secondarySystemBackground) - } -} diff --git a/Vault/Sources/VaultiOS/Views/General/TextArea.swift b/Vault/Sources/VaultiOS/Views/General/TextArea.swift deleted file mode 100644 index 195a14ab7..000000000 --- a/Vault/Sources/VaultiOS/Views/General/TextArea.swift +++ /dev/null @@ -1,41 +0,0 @@ -import Foundation -import SwiftUI -import UIKit - -struct TextArea: UIViewRepresentable { - @Binding var text: String - var font: UIFont - - func makeUIView(context: Context) -> UITextView { - let textView = UITextView() - textView.delegate = context.coordinator - textView.font = font - textView.isScrollEnabled = true - textView.isEditable = true - return textView - } - - func updateUIView(_ uiView: UITextView, context _: Context) { - uiView.text = text - } - - func makeCoordinator() -> Coordinator { - Coordinator(text: $text) - } - - /// Coordinator to handle UITextViewDelegate events - class Coordinator: NSObject, UITextViewDelegate { - @Binding var text: String - - init(text: Binding) { - _text = text - } - - func textViewDidChange(_ textView: UITextView) { - DispatchQueue.main.async { - let text = textView.text - self.text = text ?? "" - } - } - } -} diff --git a/Vault/Sources/VaultiOS/Views/General/TextEditingView.swift b/Vault/Sources/VaultiOS/Views/General/TextEditingView.swift deleted file mode 100644 index a04cfebf0..000000000 --- a/Vault/Sources/VaultiOS/Views/General/TextEditingView.swift +++ /dev/null @@ -1,28 +0,0 @@ -import Combine -import Foundation -import SwiftUI -import UIKit - -/// A full-screen text editing view for longform content. -/// -/// The reason for this view's existance is the bugs we've experienced with raw SwiftUI text editors. -/// It wraps a UIViewController under the hood, so we don't rely on SwiftUI for eventing at all. -struct TextEditingView: UIViewControllerRepresentable { - typealias UIViewControllerType = TextViewViewController - @Binding var text: String - var font: UIFont - - func makeUIViewController(context _: Context) -> TextViewViewController { - let viewController = TextViewViewController(initialText: text, font: font) - viewController.textChangedPublisher() - .sink { newValue in - text = newValue - } - .store(in: &viewController.cancellables) - return viewController - } - - func updateUIViewController(_: TextViewViewController, context _: Context) { - // empty - } -} diff --git a/Vault/Sources/VaultiOS/Views/General/TextViewViewController.swift b/Vault/Sources/VaultiOS/Views/General/TextViewViewController.swift deleted file mode 100644 index f7e777e04..000000000 --- a/Vault/Sources/VaultiOS/Views/General/TextViewViewController.swift +++ /dev/null @@ -1,55 +0,0 @@ -import Combine -import Foundation -import UIKit - -final class TextViewViewController: UIViewController { - private let font: UIFont - private let textView: UITextView = { - let view = UITextView() - view.translatesAutoresizingMaskIntoConstraints = false - view.backgroundColor = .clear - return view - }() - - private let textChangedSubject = PassthroughSubject() - - var cancellables = Set() - - init(initialText: String, font: UIFont) { - self.font = font - super.init(nibName: nil, bundle: nil) - - textView.text = initialText - textView.delegate = self - } - - @available(*, unavailable) - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func viewDidLoad() { - super.viewDidLoad() - - view.addSubview(textView) - NSLayoutConstraint.activate([ - textView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 8), - textView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -8), - textView.topAnchor.constraint(equalTo: view.topAnchor, constant: 8), - textView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -8), - ]) - textView.textContainer.lineFragmentPadding = 0 - textView.textContainerInset = .zero - textView.font = font - } - - func textChangedPublisher() -> AnyPublisher { - textChangedSubject.removeDuplicates().eraseToAnyPublisher() - } -} - -extension TextViewViewController: UITextViewDelegate { - func textViewDidChange(_ textView: UITextView) { - textChangedSubject.send(textView.text) - } -} diff --git a/Vault/Sources/VaultiOS/Views/General/View+Center.swift b/Vault/Sources/VaultiOS/Views/General/View+Center.swift deleted file mode 100644 index 0e866acf4..000000000 --- a/Vault/Sources/VaultiOS/Views/General/View+Center.swift +++ /dev/null @@ -1,27 +0,0 @@ -import Foundation -import SwiftUI - -struct HorizontallyCenter: ViewModifier { - func body(content: Content) -> some View { - HStack(alignment: .center) { - Spacer() - content - Spacer() - } - } -} - -struct VerticallyCenterUpperThird: ViewModifier { - var alignment: HorizontalAlignment - - func body(content: Content) -> some View { - VStack(alignment: alignment) { - Spacer() - Spacer() - content - Spacer() - Spacer() - Spacer() - } - } -} diff --git a/Vault/Sources/VaultiOS/Views/Previews/OTP/OTPCodeLabels.swift b/Vault/Sources/VaultiOS/Views/Previews/OTP/OTPCodeLabels.swift deleted file mode 100644 index 66bcb50b1..000000000 --- a/Vault/Sources/VaultiOS/Views/Previews/OTP/OTPCodeLabels.swift +++ /dev/null @@ -1,48 +0,0 @@ -import SwiftUI - -struct OTPCodeLabels: View { - var accountName: String - var issuer: String - - var body: some View { - VStack(alignment: .leading, spacing: 0) { - Text(issuerNameFormatted) - .font(.footnote.bold()) - .foregroundStyle(.primary) - .lineLimit(2) - .multilineTextAlignment(.leading) - .truncationMode(.tail) - .minimumScaleFactor(0.8) - Text(accountNameFormatted) - .font(.caption) - .foregroundStyle(.secondary) - .lineLimit(1) - .minimumScaleFactor(0.5) - } - .multilineTextAlignment(.leading) - } - - private var issuerNameFormatted: String { - if issuer.isNotEmpty { - issuer - } else { - localized(key: "code.issuerPlaceholder") - } - } - - private var accountNameFormatted: String { - if accountName.isNotEmpty { - accountName - } else { - localized(key: "code.accountNamePlaceholder") - } - } -} - -#Preview { - VStack(alignment: .leading, spacing: 20) { - OTPCodeLabels(accountName: "", issuer: "") - OTPCodeLabels(accountName: "test@test.com", issuer: "") - OTPCodeLabels(accountName: "test@test.com", issuer: "Authority") - } -} diff --git a/Vault/Sources/VaultiOS/Views/Tags/VaultTagDetailView.swift b/Vault/Sources/VaultiOS/Views/Tags/VaultTagDetailView.swift index aecb410fc..e8ca59aa6 100644 --- a/Vault/Sources/VaultiOS/Views/Tags/VaultTagDetailView.swift +++ b/Vault/Sources/VaultiOS/Views/Tags/VaultTagDetailView.swift @@ -97,7 +97,7 @@ struct VaultTagDetailView: View { .tint(.white) } .modifier(ProminentButtonModifier(color: .red)) - .modifier(HorizontallyCenter()) + .frame(maxWidth: .infinity) } .listRowBackground(EmptyView()) }