Commit 808a063
Release v1.0.0 (#63)
* Add Fastlane configuration and fix app icons
- Add Fastlane beta lane for TestFlight uploads
- Add GitHub Actions workflow for automated TestFlight deployment
- Fix app icons to remove alpha channel (App Store requirement)
- Update icon generation script to add white background
- Add Gemfile for Fastlane dependency management
- Update .gitignore for Fastlane and Ruby artifacts
* Remove ensure_xcode_version check from Fastfile
* Add validation for APPLE_TEAM_ID in Fastfile
* Use secrets instead of vars for APP_IDENTIFIER and APPLE_TEAM_ID
* Add Fastlane Match for code signing
* Update project configuration for Match signing
* Add automatic code signing configuration for all targets
* Trigger workflow with updated provisioning profiles
* Add explicit provisioning profile names to code signing settings
* Add temporary keychain for CI to fix signing hang
* Fix TestFlight parameter name: submit_beta_review
* Add export compliance settings to skip timeout
* Auto-increment build number from TestFlight
* Handle 'another build in review' error gracefully
* Switch to nightly builds with change detection
* Add automated PR tests workflow
* test: enable reliable PR workflow and fix unit test configuration
* Test: trigger PR workflow
* Fix working directory in PR tests workflow
* Add debug output and fix working directory
* Fix working directory path
* Remove working-directory, project is in root
* Fix test import: remove non-existent lowercase module
* Fix Swift Testing syntax: remove unsupported file and line parameters
* Configure test target to run unit tests
- Add Wurstfinger keyboard extension as dependency of WurstfingerTests target
- Enable ENABLE_TESTABILITY for keyboard extension in Debug configuration
- Include keyboard source files in test target via file system synchronized groups
- Exclude Info.plist, KeyboardViewController, and KeyboardRootView from test target
- Update PR tests workflow to use correct simulator destination and run unit tests only
- Tests now execute successfully with 14/16 passing
* Disable 2 failing tests temporarily
- Disable letterLayerProvidesAdditionalSymbols test
- Disable composeSwipeEmitsComposeAction test
- Both tests require additional debugging for initialization context
- 14 of 16 tests now passing successfully
- Tests can be re-enabled and fixed in a future PR
* Add haptic feedback configuration (#6)
* Add haptic feedback configuration
* Add unit tests for haptic configuration
* Add unit tests for haptic configuration
* Silence warning in KeyboardViewController
* Fix PR test workflow simulator destination
- Remove OS=latest parameter that causes simulator lookup issues
- Use iPhone 16 Pro which is available on GitHub Actions macos-latest
- Add CODE_SIGNING_ALLOWED=NO to skip signing for test-only builds
* Fix simulator selection - use generic iOS Simulator
GitHub Actions runners don't have specific simulator models installed.
Use 'Any iOS Simulator Device' which is always available.
* Fix simulator - use explicit OS version
xcodebuild automatically adds OS:latest which doesn't exist.
Use iPhone 16 with explicit OS=18.6 which is available on GitHub Actions.
* Refactor Keyboard Buttons With Performance Optimizations (#8)
* Restructure one button type
* Fix
* Optimize Button Refactoring For Performance
Remove type erasure and AnyView usage for better SwiftUI performance:
- Replace AnyKeyboardButtonBehavior with generic KeyboardButton<Label, Overlay>
- Eliminate all AnyView wrappers (major performance win)
- Move state from behavior objects to @State in views
- Replace configuration closures with direct value storage
- Simplify view builder chains by removing nested type erasure
- Remove unnecessary utilities (@inline, eraseToAnyGestureVoid, Combine import)
Performance improvements:
- SwiftUI can now maintain view identity across renders
- Reduced heap allocations (structs instead of classes where possible)
- Cleaner diffing algorithm for view updates
- Better compiler optimization opportunities
Code is now ~200 lines shorter and more maintainable while preserving
all functionality. All tests pass.
* Fix simulator selection - use generic iOS Simulator
GitHub Actions runners don't have specific simulator models installed.
Use 'Any iOS Simulator Device' which is always available.
* Fix simulator - use explicit OS version
xcodebuild automatically adds OS:latest which doesn't exist.
Use iPhone 16 with explicit OS=18.6 which is available on GitHub Actions.
* Trigger CI
---------
Co-authored-by: Sven Willner <sven.willner@gmail.com>
* Complete German MessagEase Layout With Symbols and Gestures (#7)
* Add MessagEase German layout and cycle accents feature
- Import complete MessagEase German layout from CSV
- All 9 keys (a,n,i,h,d,r,t,e,s) with directional swipes
- Return-swipe gestures for alternate characters
- New typographic symbols (†, ‰, ÷, ¡, ×, –, —, ±, etc.)
- Implement cycle accents feature
- New MessagEaseOutput.cycleAccents and KeyboardAction.cycleAccents
- ComposeEngine.cycleAccent() generates variant cycles from compose rules
- Swipe up-left-return on 'a' key to cycle through accent variants
- E.g., e → é → è → ê → ë → e (cycles through all variants)
- Fix multi-line string literal compilation errors
- Use Unicode escapes for typographic quotes (U+201C, U+201D)
* Show cycle accents key label on a-key upLeft position
* Change cycle accents label to reversed c (Ↄ)
* Change cycle accents label to 🅒 (U+1F152)
* Add tab symbol (⇥) as label for tab character
* Use diaeresis (¨) from t-key up instead of quotation mark for umlauts
* Add acute accent compose trigger (') to e-key up-right
* Remove fallback to tap for undefined swipe gestures
- Undefined swipe directions now do nothing instead of typing center character
- Fixes issue where swiping left on e-key incorrectly typed 'e'
- Only affects swipe gestures; center tap still works normally
* Remove apostrophe compose trigger from e-key
- Compose triggers are on n-key (up-left: `, up: ^, up-right: ´)
- e-key up-right now unassigned as per original layout
* Add # to s-key left and remove automatic uppercase fallbacks
- s-key left now outputs # (was missing)
- s-key left-return outputs £ (already present)
- Remove automatic uppercase fallback for all letter keys
- Return-swipes now only work when explicitly defined in returnOverrides
- Fixes unwanted character output on unassigned return-swipe positions
* Add Caps-Lock Support With Double Up Gesture
- Implement caps-lock by pressing up twice on r-key (no time limit)
- Add down gesture on r-key to deactivate shift/caps-lock
- Change icon from ⇧ to ⇪ when caps-lock is active
- Show down icon (⇩) only when caps/shift is active
- Caps-lock stays active after typing, temporary shift deactivates
* Add Complete Numbers Layer With Math Symbols
Numbers Layer:
- Copy structure from letters layer but replace centers with digits 7-9-4-5-6-1-2-3-0
- Remove all letter gestures, keep only symbols and special characters
- Add ≤ on 7-left and ≥ on 9-right
Circular gestures:
- 7: ∫ (integral), 8: ∏ (product), 9: ∑ (sum)
- 1: ¹, 2: ², 3: ³ (superscripts)
- 4: ¼ (quarter), 5: a, 6: ⁿ (superscript n)
Number cycling with combine key:
- Cycle through superscripts and fractions for each digit
- 1 → ¹ → ½ → ⅓ → ¼ → ⅕ → ⅙ → ⅐ → ⅛ → ⅑ → ⅒
- 2 → ² → ⅔ → ⅖, 3 → ³ → ¾ → ⅜ → ⅗, etc.
Layer toggle simplified:
- Remove third empty layer
- Direct toggle between ABC (letters) and 123 (numbers)
* Fix Unit Tests For Layout Changes
Update tests to reflect the changes made in the MessagEase layout:
1. composeEngineProducesReplacement: Changed compose trigger from " to ¨
2. symbolsLayerFollowsNumericLayer: Updated for simplified 2-layer toggle (lower ⟷ numbers)
3. returnSwipesProduceTypographicVariants: Updated expected characters for return swipes:
- n-key downLeft: ÷ → –
- a-key right: – → ÷
- e-key downLeft: „ → ,
- e-key upRight: updated to match "
- t-key left: « → ‹
- s-key right: » → ›
All tests now pass successfully.
* Refactor Keyboard Buttons With Unified Architecture (#9)
* Restructure one button type
* Fix
* Optimize Button Refactoring For Performance
Remove type erasure and AnyView usage for better SwiftUI performance:
- Replace AnyKeyboardButtonBehavior with generic KeyboardButton<Label, Overlay>
- Eliminate all AnyView wrappers (major performance win)
- Move state from behavior objects to @State in views
- Replace configuration closures with direct value storage
- Simplify view builder chains by removing nested type erasure
- Remove unnecessary utilities (@inline, eraseToAnyGestureVoid, Combine import)
Performance improvements:
- SwiftUI can now maintain view identity across renders
- Reduced heap allocations (structs instead of classes where possible)
- Cleaner diffing algorithm for view updates
- Better compiler optimization opportunities
Code is now ~200 lines shorter and more maintainable while preserving
all functionality. All tests pass.
* Fix simulator selection - use generic iOS Simulator
GitHub Actions runners don't have specific simulator models installed.
Use 'Any iOS Simulator Device' which is always available.
* Fix simulator - use explicit OS version
xcodebuild automatically adds OS:latest which doesn't exist.
Use iPhone 16 with explicit OS=18.6 which is available on GitHub Actions.
* Trigger CI
* Fix GitHub Actions iOS Simulator Destination
Remove OS version specification to allow xcodebuild to use the latest available iOS version for iPhone 16 simulator on GitHub Actions runners.
---------
Co-authored-by: Sven Willner <sven.willner@gmail.com>
* Implement dynamic label scaling and MessagEase-style hint priority (#11)
* Add Multi-Language Support With 14 Languages (#10)
Implements comprehensive multi-language support for the MessagEase-style
keyboard, supporting 14 languages with complete character sets and
real-time language switching.
## Features
### Supported Languages (14)
Alphabetically sorted by display name:
- Català (Catalan) - ca_ES
- Deutsch (German) - de_DE
- Eesti-Suomi (Estonian-Finnish) - et_EE
- English - en_US
- Español (Spanish) - es_ES
- Français (French) - fr_FR
- Hrvatski (Croatian) - hr_HR
- Italiano (Italian) - it_IT
- Polski (Polish) - pl_PL
- Suomi (Finnish) - fi_FI
- Svenska (Swedish) - sv_SE
- Tagalog - tl_PH
- עברית (Hebrew) - he_IL
- Русский (Russian) - ru_RU
### Key Capabilities
- **Automatic language detection** on first launch based on system locale
- **Real-time language switching** without restarting the app or keyboard
- **Complete character sets** for each language, including all letters
accessible via swipe gestures (not just center tap characters)
- **Cross-process synchronization** via App Group UserDefaults
- **Language-specific layouts** similar to thumb-key MessagEase keyboards
## Implementation Details
### Architecture
- **LanguageConfig**: Defines language layouts with center characters
and directional swipe mappings
- **LanguageSettings**: Singleton managing language selection with
automatic system language detection
- **KeyboardLayout**: Generates MessagEase key grid using language configs
- **Cross-process updates**: UserDefaults observer detects changes from
host app in real-time
### Technical Changes
1. **KeyboardLayout.swift**
- Made character mappings configurable via specialCharacters dict
- Previously hardcoded Latin characters now use language-specific chars
- Fixes Hebrew, Russian, and other non-Latin scripts
2. **KeyboardViewModel.swift**
- Added UserDefaults observer for real-time language updates
- Calls objectWillChange.send() to trigger SwiftUI updates
- Locale-aware uppercasing for each language
3. **LanguageSettings.swift** (new)
- Auto-detects system language on first launch
- Falls back to English for unsupported languages
- Matches language variants (e.g., en-GB → en_US)
- Persists selection in App Group UserDefaults
4. **LanguageConfig.swift** (new)
- 14 complete language configurations
- Each includes 3x3 center grid + directional swipe characters
- Extracted complete letter sets (not just special characters)
5. **LanguageSelectionView.swift** (new)
- SwiftUI picker for language selection in settings
- Shows checkmark for currently selected language
6. **Xcode Project Configuration**
- Fixed keyboard extension target to include all source files
- Only Info.plist excluded from sync
### Testing
- Added 21 unit tests for language detection logic
- Tests exact matches, language-only matches, variants, and fallbacks
- All tests passing
## Notes
- MessagEase layout structure similar to thumb-key implementation
- Maintains existing German layout as default for backwards compatibility
- Symbols and punctuation remain consistent across languages
* Fix Keyboard Background Not Filling Entire Area (#12)
* Implement dynamic label scaling and MessagEase-style hint priority
Add adaptive font sizing for both main labels and hint labels that
scales proportionally with key height (controlled by aspect ratio):
Main character labels:
- Base size: 26pt (up from fixed 22pt)
- Scales from 20pt to 34pt based on key height
- Ensures primary characters remain clearly visible at all sizes
Hint labels (swipe gestures):
- Base size: 14pt
- Scales from 10pt to 22pt based on key height
- Uses MessagEase-style three-tier visual hierarchy:
1. Center character: primary color (highest priority)
2. Letter hints: primary.opacity(0.65) + medium weight
3. Symbol hints: secondary.opacity(0.55) + regular weight
Benefits:
- Better readability across different aspect ratios
- Letters stand out more than symbols for easier scanning
- Proportional scaling maintains visual balance
- Works for all alphabets (Latin, Hebrew, Arabic, etc.)
Implementation uses Character.isLetter for language-agnostic
letter detection.
* Fix keyboard background not filling entire area
Wrap keyboard grid in ZStack with separate background layer that
always fills the entire available space, independent of scale and
offset transformations.
Also removes duplicate scaledMainLabelSize function.
* Persist Onboarding Toggle States (#14)
* Persist Onboarding Toggle States
Replace @State with @AppStorage to persist setup assistant progress.
When users mark setup steps as complete, the toggles now persist
across app launches using App Group UserDefaults.
- Replace @State with @AppStorage for three onboarding toggles
- Use shared App Group UserDefaults (group.de.akator.wurstfinger.shared)
- Add keys: onboarding.keyboardInstalled, onboarding.fullAccessEnabled, onboarding.practiced
* Improve Open Settings button formatting
Center button and add vertical padding for better visual appearance.
* Fix Open Settings button label centering
Make button full-width with centered label and icon.
* Remove Space Key Text Selection Mode (#15)
* Remove Space Key Text Selection Mode
The text selection mode on space key (vertical swipe) was not working well.
Simplified space key to only handle horizontal swipe for cursor movement.
- Remove selection state and gesture handling from SpaceKeyButton
- Remove KeyboardAction cases for selection (startSelection, updateSelection, endSelection)
- Remove selection state variables from KeyboardViewModel (isSpaceSelecting, spaceSelectionResidual)
- Remove selection handler functions from KeyboardViewController
- Simplify space drag functions to only handle cursor movement
* Remove test for deleted space selection feature
The spaceSelectionEmitsSelectionActions test was testing functionality
that was removed in this PR.
* Optimize CI test performance with caching and parallel execution
Add DerivedData caching to speed up subsequent builds, pre-boot simulator to reduce startup time, enable parallel testing, and remove xcpretty overhead. Expected speedup: first run 2-3 min (vs 7+ min), cached runs 30-60 sec.
* Revert to conservative CI optimization approach
Keep working xcpretty configuration from develop, only add safe optimizations (DerivedData caching and simulator pre-boot). Previous aggressive changes (-quiet, -parallel-testing-enabled) may have caused hanging.
* Fix GitHub Actions workflow paths
Add working-directory specification to fix hanging test runs
* Fix workflow paths - remove incorrect working-directory
The repository root contains Wurstfinger.xcodeproj directly, not in a nested wurstfinger/ subdirectory. Removing working-directory specifications to fix "Wurstfinger.xcodeproj does not exist" error.
* Update README with automated screenshot system (#16)
* Fix screenshot workflow - use macos-latest and remove Xcode version selector
* Fix test workflow - add explicit simulator boot and timeout
* Disable PR tests on push to develop - only run on pull requests
* Add numpad style switcher with phone and classic layouts (#18)
Implements a MessagEase-compatible feature to switch between two numpad
layouts: phone style (1-2-3 on top, default) and classic calculator
style (7-8-9 on top).
Key changes:
- Add NumpadStyle enum (phone/classic) in KeyboardLayout
- Swap center numbers and circular gestures while keeping swipe gestures
at their physical positions
- Add numpad style setting in SettingsView with picker UI
- Store preference in shared UserDefaults for cross-app access
- Update tests to expect phone layout as default
The phone layout is now the default, matching typical phone keypads.
All number-related circular gestures (¹, ², ³, etc.) move with their
numbers, while directional swipes remain at their physical locations.
* Add public TestFlight beta link and auto-distribute to Public Beta group (#19)
- Add "Join the Beta" section to README with TestFlight link
- Configure Fastlane to automatically distribute nightly builds to "Public Beta" group
- Include beta features description and iOS requirements
- Note: First build must be manually added to Public Beta group in App Store Connect, then all future builds will auto-distribute
The public beta allows early testers to try nightly builds from the develop branch.
* Code cleanup and optimization refactoring (#20)
* Refactor keyboard code structure and remove duplication
Major refactoring to improve code organization and maintainability:
**Code Structure (Phase 1):**
- Add SharedDefaults utility for centralized UserDefaults access
- Move magic numbers to KeyboardConstants (15+ new constants)
- Split KeyboardRootView.swift (698→120 lines, -83% reduction)
- Extract 7 focused components:
- KeyHintOverlay (157 lines)
- KeyboardButton (134 lines)
- DeleteKeyButton (133 lines)
- SpaceKeyButton (66 lines)
- KeyboardButtonComponents (shared types)
- ViewExtensions (conditional modifiers)
- SharedDefaults (for both targets)
**Remove Duplication (Phase 2):**
- Extract utility buttons into reusable utilityButton(forRow:) function
- Centralize repeated calculations in KeyboardConstants.Calculations:
- keyHeight(aspectRatio:) - removed 3 duplicates
- baseHeight(aspectRatio:) - removed 2 duplicates
All 33 tests passing. No functional changes.
* Convert accessibility labels to English and improve test comments
**Consistency improvements (Phase 3):**
- Convert German accessibility labels to English:
- DeleteKeyButton: "Löschen" → "Delete"
- SpaceKeyButton: "Leerzeichen" → "Space"
- Documentation already complete through earlier file extraction
**Test improvements (Phase 4):**
- Update disabled test comments with accurate descriptions
- Replace "Requires UI context" with "Test assertions failing"
- Tests remain disabled but with clearer context for future fixes
All tests passing. No functional changes.
* Fix CI: Add working-directory for xcodebuild
* Fix CI: Remove incorrect working-directory
The Wurstfinger.xcodeproj is at the repository root, not in a subdirectory.
The working-directory: wurstfinger was causing xcodebuild to look in the wrong location.
* Increase test timeout to 15 minutes
* Fix swipe direction detection for non-square button aspect ratios (#21)
* Fix swipe direction detection for non-square button aspect ratios
When button aspect ratio differs from 1:1 (e.g., 1.5:1 for wider buttons),
swipe gestures were incorrectly interpreted because the angle calculation
assumed square buttons.
This commit compensates for non-square buttons by dividing horizontal
translation by the aspect ratio before calculating swipe angles. For
example, with a 1.5:1 aspect ratio (wider buttons), horizontal swipes
now require proportionally more horizontal movement to trigger,
maintaining consistent directional behavior across different button shapes.
Changes:
- Add aspectRatio parameter to KeyboardDirection.direction() with default 1.0
- Add aspectRatio property to KeyboardButton struct
- Update all KeyboardButton instantiations to pass viewModel.keyAspectRatio
- Apply horizontal translation compensation: dxCorrected = dx / aspectRatio
* Increase CI test timeout to 20 minutes
GitHub Actions simulators sometimes experience slow boot times due to
data migration issues, causing tests to exceed the 15-minute timeout.
* Refactor: Standardize settings UI, fix landscape layout, and refine haptics (#22)
* Fix swipe direction detection for non-square button aspect ratios
When button aspect ratio differs from 1:1 (e.g., 1.5:1 for wider buttons),
swipe gestures were incorrectly interpreted because the angle calculation
assumed square buttons.
This commit compensates for non-square buttons by dividing horizontal
translation by the aspect ratio before calculating swipe angles. For
example, with a 1.5:1 aspect ratio (wider buttons), horizontal swipes
now require proportionally more horizontal movement to trigger,
maintaining consistent directional behavior across different button shapes.
Changes:
- Add aspectRatio parameter to KeyboardDirection.direction() with default 1.0
- Add aspectRatio property to KeyboardButton struct
- Update all KeyboardButton instantiations to pass viewModel.keyAspectRatio
- Apply horizontal translation compensation: dxCorrected = dx / aspectRatio
* Increase CI test timeout to 20 minutes
GitHub Actions simulators sometimes experience slow boot times due to
data migration issues, causing tests to exceed the 15-minute timeout.
* Refactor: Standardize settings UI, fix landscape layout, and refine haptics
- Fix landscape layout distortion by using shortest screen side as base width
- Standardize Haptic Settings UI to match other settings views
- Implement numeric input fields for precise setting adjustments
- Fix haptic feedback reliability on keyboard extension
- Remove redundant settings views and cleanup code
- Fix preview flickering
* Fix: Make screenshot generation script robust for CI (#23)
- Remove specific OS version from simulator destination to support various runners
- Make xcpretty usage conditional to avoid failures when missing
* Fix: Add scripts and workflow file to trigger paths (#24)
- Ensure screenshot automation runs when scripts or workflow configuration changes
* Fix: Dynamically detect available iPhone simulator (#25)
- Replace hardcoded device name with dynamic detection using xcrun simctl
- Adds fallback to iPhone 16 if detection fails
- Resolves CI failures where specific devices might be missing
* Fix: Use Python virtual environment in CI (#26)
- Create and activate a virtual environment for Python dependencies
- Resolves 'externally-managed-environment' error (PEP 668) on macos-latest runner
* Fix: Enable screenshot workflow on main branch and add manual trigger
- Add main branch to trigger conditions (previously only develop)
- Add workflow_dispatch to allow manual triggering from GitHub UI
* Perf: Speed up screenshot workflow
- Remove fetch-depth: 0 (not needed, saves time)
- Add Python venv caching
- Pre-boot iOS Simulator while other steps run
* Fix: Apply aspect ratio to button width on iPad
The aspect ratio setting was only affecting key height calculation
and gesture recognition, but not the visual width of buttons.
This caused buttons to stretch too wide on iPad since they used
maxWidth: .infinity.
Now KeyCap accepts an aspectRatio parameter and constrains
maxWidth to height * aspectRatio, ensuring buttons maintain
their configured width-to-height ratio across all devices.
* Fix: Optimize TestAreaView layout for iPad
- Replace deprecated NavigationView with NavigationStack
- Add ScrollView for better content handling
- Limit content width to 600pt max for iPad readability
- Center content on wider screens
* Fix: Add write permissions for GITHUB_TOKEN
* Fix: Improve screenshot workflow robustness
- Skip non-image files (mp4 videos) in Python script
- Wait for simulator to be fully booted with bootstatus
- Better UDID extraction for simulator
* Add Privacy Manifest for iOS 17+ App Store compliance
Add PrivacyInfo.xcprivacy to both the main app and keyboard extension
to declare privacy practices required by Apple since iOS 17.
- NSPrivacyTracking: false (no tracking)
- NSPrivacyCollectedDataTypes: empty (no data collected)
- NSPrivacyTrackingDomains: empty
- NSPrivacyAccessedAPITypes: UserDefaults with CA92.1 reason (App Group)
* Fix: Better error handling and debug output for screenshot workflow (#30)
* Debug: Add simulator status output and increase timeout to 25min
* Fix: Create PR instead of pushing directly to protected branch (#32)
* Fix: Add missing entitlements file for Keyboard Extension
- Create wurstfingerKeyboard.entitlements with App Group entitlement
- Fix CODE_SIGN_ENTITLEMENTS path in project.pbxproj (was referencing
non-existent Wurstfinger/Wurstfinger.entitlements)
- App Group: group.de.akator.wurstfinger.shared
This fixes the build error that prevented app signing.
* Add App Store metadata for English localization
Add fastlane metadata files for en-US locale including:
- App description highlighting gesture-based keyboard features
- Keywords for App Store search optimization
- Subtitle, app name, and support/privacy URLs
* Add privacy policy for App Store requirements
Document that Wurstfinger collects no user data and stores
preferences only locally via UserDefaults.
* Add App Store screenshot generation infrastructure (#36)
* Update screenshots (#37)
* Remove WIP banner, add tagline
Replace the orange "Work in Progress" development notice with a clean
tagline "The Keyboard for Fat Fingers" for App Store release.
Closes #41
* Fix privacy URL to point to develop branch
The default branch is develop, not main. The privacy policy URL was
returning 404 because PRIVACY.md doesn't exist on main.
Closes #40
* Add German App Store metadata and missing en-US files
Add complete German localization for App Store:
- description.txt
- keywords.txt
- name.txt
- subtitle.txt
- release_notes.txt
- promotional_text.txt
- privacy_url.txt
- support_url.txt
Also add missing en-US files:
- release_notes.txt
- promotional_text.txt
Closes #39
* Improve App Store screenshot generation
Enhance UI tests to capture full app experience instead of just keyboard:
New screenshots (testGenerateAppStoreScreenshots):
- 01: Home screen
- 02: Test area (light mode)
- 03: Test area (dark mode)
- 04: Settings view
- 05: Setup/Onboarding view
Additional keyboard showcase (testGenerateKeyboardShowcaseScreenshots):
- 06-09: Keyboard layers (lower, numbers, symbols) in light/dark
Update generate-appstore-screenshots.sh to run both test methods.
Related to #38
* Fix App Store screenshot tests to show full app
The SCREENSHOT_MODE launch argument was causing the app to show
KeyboardShowcaseView instead of the normal ContentView with TabBar.
Changes:
- Remove SCREENSHOT_MODE from setUp (now set per-test)
- testGenerateAppStoreScreenshots: Don't use SCREENSHOT_MODE to show
normal app with Home, Test, Settings, Setup tabs
- testGenerateKeyboardShowcaseScreenshots: Use SCREENSHOT_MODE for
keyboard-only screenshots
- Add descriptive assertion messages for debugging
This fixes the "XCTAssertTrue failed" error when looking for tabs.
* Add App Store keyboard screenshot view with chat UI
Add a new AppStoreScreenshotView that shows the keyboard in a realistic
chat context with sample messages. This is much better for App Store
marketing than isolated keyboard screenshots.
New files:
- AppStoreScreenshotView.swift: Chat UI with keyboard, configurable via
environment variables (FORCE_LAYER, FORCE_APPEARANCE, FORCE_TEXT)
Changes:
- wurstfingerApp.swift: Add APPSTORE_SCREENSHOT_MODE launch argument
- ScreenshotTests.swift: Add testGenerateAppStoreKeyboardScreenshots()
that generates 4 screenshots:
- Letters keyboard (light/dark) with "Hello Wurstfinger!"
- Numbers keyboard (light/dark) with "Call me: 0800 123456"
Related to #38
* Fix keyboard layout regressions from PR #28
- Remove aspectRatio from SpaceKeyButton since it spans multiple columns
and should fill available width
- Replace Grid with VStack/HStack for consistent row spacing
- Split verticalPadding into top (4pt) and bottom (10pt) for better
alignment with text input fields
* Improve App Store screenshots and keyboard layout
Screenshots:
- Add chat UI with status bar (9:41, signal, battery)
- Use full-size keyboard (scale=1.0) for screenshots
- Update dialog text to highlight speed and precision
Keyboard layout:
- Reduce spacing from 8pt to 5pt
- Reduce corner radius from 12pt to 8pt
- Calculate spacebar width based on column span
- Use VStack/HStack for consistent row spacing
- Asymmetric vertical padding (4pt top, 10pt bottom)
* Add App Store screenshots to fastlane metadata
Add keyboard screenshots with chat UI for en-US locale:
- 01-lower-light.png: Light mode lowercase keyboard
- 02-lower-dark.png: Dark mode lowercase keyboard
- 03-numbers-light.png: Light mode numbers keyboard
- 04-numbers-dark.png: Dark mode numbers keyboard
Closes #38
* Fix and re-enable disabled unit tests
Update test assertions to match current keyboard layout:
- letterLayerProvidesAdditionalSymbols: Test correct swipe outputs
- composeSwipeEmitsComposeAction: Test compose trigger on N-key
Both tests were disabled due to outdated expectations that no
longer matched the keyboard layout after refactoring.
Closes #44
* Set iOS deployment target to 17.0 for all targets
Update all 6 deployment target settings from 18.5 to 17.0:
- Project-level (Debug/Release)
- WurstfingerTests (Debug/Release)
- Wurstfinger keyboard extension (Debug/Release)
This matches the documented iOS 17.0 requirement in README
and increases user reach.
Closes #45
* Add App Store screenshots for multiple device sizes
Organize screenshots by device size for App Store submission:
- iPhone 6.1" (1206x2622) - standard iPhone
- iPhone 6.7" (1290x2796) - Plus/Max models
- iPad Pro 13" (2064x2752) - iPad support
Each size includes 4 screenshots:
- Lower case keyboard (light/dark)
- Numbers keyboard (light/dark)
Closes #61
* Add Privacy Policy
Create PRIVACY.md documenting that Wurstfinger:
- Does not collect, store, or transmit any personal data
- Does not use analytics or tracking
- Does not require network access
- Only uses Full Access permission for haptic feedback
Required for App Store submission.
Closes #60
* Add App Review notes for keyboard activation
Create fastlane/metadata/review_information/notes.txt with:
- Step-by-step keyboard activation instructions
- Full Access explanation (only for haptic feedback)
- Basic gesture tips for reviewers
The app already has an in-app onboarding flow (OnboardingView.swift)
that guides users through the same process.
Closes #62
* Expand UI tests for critical flows
Add comprehensive UI tests covering:
- Tab navigation (Home, Setup, Test, Settings)
- Onboarding flow (setup steps, toggles, Open Settings button)
- Settings navigation (Language, Haptic Feedback)
- Settings toggles (Utility Keys on Left)
- Test area text input field
- App launch performance
10 UI tests now verify critical user flows before release.
Closes #46
* Defer iPad support to v1.1
The MessagEase-style keyboard is optimized for one-handed/thumb
typing which doesn't translate well to iPad's larger form factor.
Changes:
- Keep keyboard extension iPhone-only (TARGETED_DEVICE_FAMILY = 1)
- Remove iPad screenshots from fastlane metadata
- Update issue #47 with v1.1 label and revised scope
The host app still supports iPad for settings/onboarding.
* Add fastlane release lane for App Store submission
Add new release lane that builds and uploads to App Store with metadata
and screenshots, then submits for review with automatic release enabled.
---------
Co-authored-by: Sven Willner <sven.willner@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>1 parent 3d5a65f commit 808a063
File tree
104 files changed
+5243
-1396
lines changed- .github/workflows
- .idea
- inspectionProfiles
- docs/images
- fastlane
- metadata
- de-DE
- en-US
- review_information
- screenshots/en-US
- iPhone 6.1
- iPhone 6.7
- scripts
- tools
- wurstfinger.xcodeproj
- wurstfingerKeyboard
- wurstfingerTests
- wurstfingerUITests
- wurstfinger
- Assets.xcassets/AppIcon.appiconset
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
104 files changed
+5243
-1396
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
0 commit comments