From a79fb7d346506356e8e6398de17014827980d4f2 Mon Sep 17 00:00:00 2001 From: Luke Hogan Date: Tue, 21 Apr 2026 23:14:57 -0500 Subject: [PATCH] chore(ios): clear swift-format debt blocking iOS CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit main's iOS workflow has been failing swift-format --strict since PR #1 merged (auto-merge bypassed the check since no branch protection). Every Phase 0 code review flagged "pre-existing swift-format issues in unrelated files" and deferred. This clears the debt so CI goes green for PR #2 and future iOS changes. Changes: - Ran `swift-format format --in-place` which auto-fixed 17 whitespace, indentation, and AddLines errors across RootView, AppleSignInService, and SignUpWallView. - Hand-added 4 docblocks swift-format required: - MWButtonPress.body(content:) full param/return doc - MWScreen.body one-line /// - MWAccentKey.mwAccent(_:) — Returns: section - Borders.mwStroke(...) — Returns: section - Rewrote the 164-char string in SignUpWallView as a 3-part concatenation so no disable pragma is needed. Compiler folds the concatenation at compile time. - Added `opening_brace` to .swiftlint.yml's disabled_rules. swift-format wraps long protocol-conformance lists onto multiple lines with the opening brace on its own line (see AppleSignInService:81-86); SwiftLint's opening_brace rule fights that. Same precedent as the existing trailing_comma disable — swift-format is authoritative. Verified: - `swift-format lint --strict Flashcards` → exit 0 - `swiftlint --strict Flashcards` → 0 violations, 38 files - `xcodebuild test` → TEST SUCCEEDED (19 FlashcardsTests + 1 UI test) Co-Authored-By: Claude Opus 4.7 (1M context) --- ios/.swiftlint.yml | 5 ++++ ios/Flashcards/App/RootView.swift | 30 ++++++++++--------- .../Components/Layout/MWScreen.swift | 2 ++ .../EnvironmentKeys/MWAccentKey.swift | 2 ++ .../Modifiers/MWButtonPress.swift | 6 ++++ .../DesignSystem/Tokens/Borders.swift | 1 + .../Features/Auth/AppleSignInService.swift | 3 +- .../Features/Onboarding/SignUpWallView.swift | 17 +++++++---- 8 files changed, 46 insertions(+), 20 deletions(-) diff --git a/ios/.swiftlint.yml b/ios/.swiftlint.yml index 13d1cb2..3c7d377 100644 --- a/ios/.swiftlint.yml +++ b/ios/.swiftlint.yml @@ -10,6 +10,11 @@ disabled_rules: # collection literals. SwiftLint's `trailing_comma` rule forbids them by # default, which conflicts. swift-format is authoritative — disable here. - trailing_comma + # swift-format wraps declarations with long protocol conformance lists + # onto multiple lines, placing the opening brace on its own line. + # SwiftLint's `opening_brace` insists the brace stay on the declaration + # line. Same precedent as `trailing_comma` — swift-format is authoritative. + - opening_brace # Design-system token names (MWSpacing.s, MWRadius.m, …) intentionally use # one- and two-character identifiers to match the canonical t-shirt scale. diff --git a/ios/Flashcards/App/RootView.swift b/ios/Flashcards/App/RootView.swift index 4b210d5..05b8a4d 100644 --- a/ios/Flashcards/App/RootView.swift +++ b/ios/Flashcards/App/RootView.swift @@ -60,22 +60,24 @@ struct RootView: View { .font(MWType.headingM).foregroundStyle(MWColor.ink) default: switch step { - case .splash: SplashView().task { - try? await Task.sleep(nanoseconds: 1_200_000_000) - step = .intro1 - } + case .splash: + SplashView().task { + try? await Task.sleep(nanoseconds: 1_200_000_000) + step = .intro1 + } case .intro1: Intro1View { step = .intro2 } case .intro2: Intro2View { step = .signup } - case .signup: SignUpWallView( - onAppleSignIn: { - try? await auth.signInWithApple() - }, - onRequestMagicLink: { email in - try? await auth.requestMagicLink(email: email) - emailSent = email - step = .magicLinkSent - } - ) + case .signup: + SignUpWallView( + onAppleSignIn: { + try? await auth.signInWithApple() + }, + onRequestMagicLink: { email in + try? await auth.requestMagicLink(email: email) + emailSent = email + step = .magicLinkSent + } + ) case .magicLinkSent: MagicLinkSentView(email: emailSent ?? "") } diff --git a/ios/Flashcards/DesignSystem/Components/Layout/MWScreen.swift b/ios/Flashcards/DesignSystem/Components/Layout/MWScreen.swift index e6cf308..333614d 100644 --- a/ios/Flashcards/DesignSystem/Components/Layout/MWScreen.swift +++ b/ios/Flashcards/DesignSystem/Components/Layout/MWScreen.swift @@ -35,6 +35,8 @@ public struct MWScreen: View { self.content = content } + /// Renders the canvas background, the optional grid overlay, and the + /// supplied `content()` stacked in a `ZStack`. public var body: some View { ZStack { MWColor.canvas.ignoresSafeArea() diff --git a/ios/Flashcards/DesignSystem/EnvironmentKeys/MWAccentKey.swift b/ios/Flashcards/DesignSystem/EnvironmentKeys/MWAccentKey.swift index 282b935..bfcb161 100644 --- a/ios/Flashcards/DesignSystem/EnvironmentKeys/MWAccentKey.swift +++ b/ios/Flashcards/DesignSystem/EnvironmentKeys/MWAccentKey.swift @@ -32,5 +32,7 @@ public extension EnvironmentValues { public extension View { /// Scopes the `mwAccent` environment value for the receiver and its descendants. /// - Parameter color: The accent color (usually an `MWAccent.color`). + /// - Returns: A view whose subtree reads the supplied accent colour via + /// `@Environment(\.mwAccent)`. func mwAccent(_ color: Color) -> some View { environment(\.mwAccent, color) } } diff --git a/ios/Flashcards/DesignSystem/Modifiers/MWButtonPress.swift b/ios/Flashcards/DesignSystem/Modifiers/MWButtonPress.swift index ad5b75f..a21ea0b 100644 --- a/ios/Flashcards/DesignSystem/Modifiers/MWButtonPress.swift +++ b/ios/Flashcards/DesignSystem/Modifiers/MWButtonPress.swift @@ -26,6 +26,12 @@ public struct MWButtonPress: ViewModifier { @Environment(\.accessibilityReduceMotion) private var reduceMotion + /// Applies the press-scale + reduce-motion-safe animation to `content`. + /// + /// - Parameter content: The view being decorated. + /// - Returns: A view that scales to `pressedScale` while `isPressed` is + /// true, returning to `1.0` otherwise, with the transition collapsed + /// to an instantaneous change when Reduce Motion is enabled. public func body(content: Content) -> some View { content .scaleEffect(isPressed ? Self.pressedScale : 1.0) diff --git a/ios/Flashcards/DesignSystem/Tokens/Borders.swift b/ios/Flashcards/DesignSystem/Tokens/Borders.swift index d8fc5fb..411c3da 100644 --- a/ios/Flashcards/DesignSystem/Tokens/Borders.swift +++ b/ios/Flashcards/DesignSystem/Tokens/Borders.swift @@ -55,6 +55,7 @@ public extension View { /// - cornerRadius: Corner radius for the overlay shape. Defaults to `0` /// (sharp rectangle). Pass the same value used in an upstream /// `mwCornerRadius` so the stroke hugs the rounded clip. + /// - Returns: A view overlaid with the configured stroke. func mwStroke( color: Color = MWColor.ink, width: CGFloat = MWBorder.defaultWidth, diff --git a/ios/Flashcards/Features/Auth/AppleSignInService.swift b/ios/Flashcards/Features/Auth/AppleSignInService.swift index 2a5a024..0f39e3b 100644 --- a/ios/Flashcards/Features/Auth/AppleSignInService.swift +++ b/ios/Flashcards/Features/Auth/AppleSignInService.swift @@ -82,7 +82,8 @@ public final class AppleSignInService: NSObject, ObservableObject, ASAuthorizationControllerDelegate, - ASAuthorizationControllerPresentationContextProviding { + ASAuthorizationControllerPresentationContextProviding +{ /// Pending continuation for the in-flight `signIn()` call. Lives on /// the instance because the delegate callbacks can't capture it /// directly. Cleared the moment it is resumed. diff --git a/ios/Flashcards/Features/Onboarding/SignUpWallView.swift b/ios/Flashcards/Features/Onboarding/SignUpWallView.swift index 5ffc779..8f277cf 100644 --- a/ios/Flashcards/Features/Onboarding/SignUpWallView.swift +++ b/ios/Flashcards/Features/Onboarding/SignUpWallView.swift @@ -54,18 +54,25 @@ struct SignUpWallView: View { MWEyebrow("Save your progress") Text("No password required.") .font(MWType.headingL).foregroundStyle(MWColor.ink) - // swiftlint:disable:next line_length - Text("We ask for your email so your flashcards live in your account, not just this device. If you lose your phone, you won't lose a single card.") - .font(MWType.bodyL).foregroundStyle(MWColor.inkMuted) + Text( + "We ask for your email so your flashcards live in your account, " + + "not just this device. If you lose your phone, you won't " + + "lose a single card." + ) + .font(MWType.bodyL).foregroundStyle(MWColor.inkMuted) MWButton("Continue with Apple") { - Task { isSubmitting = true; await onAppleSignIn(); isSubmitting = false } + Task { + isSubmitting = true; await onAppleSignIn(); isSubmitting = false + } } MWTextField(label: "Email", text: $email, contentType: .emailAddress, keyboard: .emailAddress) MWButton("Continue with email", kind: .secondary) { - Task { isSubmitting = true; await onRequestMagicLink(email); isSubmitting = false } + Task { + isSubmitting = true; await onRequestMagicLink(email); isSubmitting = false + } }.disabled(email.isEmpty || isSubmitting) Text("Free to use. No payment needed. We won't sell your data or email you marketing.")