Skip to content

[Masterclass] Derive autocapitalization from the keyboard type, and add an autocapitalize override - #47

Merged
shanerbaner82 merged 1 commit into
mainfrom
fix/masterclass-304-autocapitalize
Aug 11, 2026
Merged

[Masterclass] Derive autocapitalization from the keyboard type, and add an autocapitalize override#47
shanerbaner82 merged 1 commit into
mainfrom
fix/masterclass-304-autocapitalize

Conversation

@shanerbaner82

Copy link
Copy Markdown
Contributor

Fixes NativePHP/mobile-air#304. cc @shrutibalasawebdev

Lives here rather than in mobile-air: the text input renderer is a plugin component. The issue is filed on mobile-air, so the cross-repo Fixes reference won't auto-close it — worth closing by hand on merge.

Demo: /masterclass/autocapitalize in the super-native demo app, under the Masterclass Fixes group.


Cause

NativeUITextInputCore applied .keyboardType(keyboard) and nothing else.

On iOS, keyboardType sets the key layout only — it says nothing about capitalization, and an untouched SwiftUI TextField defaults to .sentences.

That's exactly why @shrutibalasawebdev's report was so precise: the email keyboard did appear, so keyboard="email" was demonstrably being applied — it just doesn't carry capitalization with it. Nothing was broken here; the behaviour was simply never wired.

Fix — the two halves the issue asked for

1. The field type carries its typing behaviour.

Every keyboard kind whose content is case-sensitive or non-alphabetic — email, url, number, decimal, phone, password — now resolves to .never.

Autocorrect is disabled for the same set. A slight extension of the report, but it's the identical bug wearing a different hat: iOS will otherwise happily "correct" an email local part into a dictionary word, and the issue's own framing was that "declaring the field type should carry the capitalization behaviour with it, the same way it carries the keyboard layout."

2. A new autocapitalize attribute — the issue noted "there does not appear to be a separate attribute to turn autocapitalization off."

none | sentences | words | characters — HTML's vocabulary. It covers what a keyboard type can't imply: a name field wanting words, a booking reference wanting characters. It overrides the derived value, and unknown values fall back to the derived behaviour rather than erroring (same policy as resolveKeyboardType).

Available as an attribute (autocapitalize / autoCapitalize) and as a fluent setter.

Android

Android never had the bug — Compose's KeyboardOptions defaults to no capitalization — but it got the right answer by accident, and autocapitalize had nowhere to land at all. It now resolves the same prop through the same rules, so the platforms agree by construction rather than coincidence.

One deliberate asymmetry

For a plain text field with nothing specified, iOS capitalizes sentences and Android does not.

resolveCapitalization returns null for that case rather than Sentences, leaving Compose's default alone. Matching iOS would unify the platforms, but it would also silently start capitalizing every unclassified text field in every existing Android app — that default deserves its own decision, not a side effect of this fix.

So Android behaviour changes only when the author sets autocapitalize, or uses a keyboard type that already implied None. It's the one ⚠️ section in the demo.

Testing

  • Full suite green: 217 passed, Pint clean
  • 4 new tests, including one asserting the prop is omitted entirely when unset — an empty string must not read as an explicit choice, or the derivation in both native resolvers is bypassed
  • Covers both attribute spellings and the lower-casing normalisation

⚠️ The Swift and Kotlin changes are compile-unverified — no iOS or Android build has been run against this yet.

🤖 Generated with Claude Code

`NativeUITextInputCore` applied `.keyboardType(keyboard)` and nothing else.
On iOS `keyboardType` sets the KEY LAYOUT only — it says nothing about
capitalization, and an untouched SwiftUI TextField defaults to `.sentences`.
So a `keyboard="email"` field showed the email keyboard (which is why the
report correctly ruled out the class being ignored) and still capitalized the
first letter. Nothing was broken; the behaviour was simply never wired.

Two halves, matching what the issue asked for:

1. The field type carries its typing behaviour. Every keyboard kind whose
   content is case-sensitive or non-alphabetic — email, url, number, decimal,
   phone, password — resolves to `.never`. Autocorrect is disabled for the
   same set: iOS will otherwise "correct" an email local part into a
   dictionary word, which is the same bug wearing a different hat.

2. A new `autocapitalize` attribute — "none" | "sentences" | "words" |
   "characters", HTML's vocabulary — for what a keyboard type cannot imply (a
   name field wanting words, a reference code wanting characters). It
   overrides the derived value. Unknown values fall back to the derived
   behaviour rather than erroring, matching `resolveKeyboardType`.

Android never had the bug — Compose's KeyboardOptions defaults to no
capitalization — but it got there by accident, and `autocapitalize` had
nowhere to land. It now resolves the same prop through the same rules.

One deliberate asymmetry: for a plain text field with nothing specified, iOS
capitalizes sentences and Android does not. `resolveCapitalization` returns
NULL for that case rather than `Sentences`, leaving Compose's default alone —
matching iOS there would silently start capitalizing every unclassified text
field in every existing Android app, and that default is a separate decision.
Android behaviour therefore changes only when the author sets
`autocapitalize`, or uses a keyboard type that already implied None.

Fixes NativePHP/mobile-air#304. Native changes are compile-unverified.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@shanerbaner82
shanerbaner82 merged commit c311666 into main Aug 11, 2026
3 checks passed
@shanerbaner82
shanerbaner82 deleted the fix/masterclass-304-autocapitalize branch August 11, 2026 02:01
simonhamp pushed a commit that referenced this pull request Aug 22, 2026
`resolveAutocapitalization(explicit:keyboard:)` takes the explicit
`autocapitalize` prop and the keyboard type, and nothing else. `secure` is a
separate prop, so a field declared `<outlined-text-input secure>` with no
`keyboard` set misses every case in the derived switch and lands on
`default: .sentences` — iOS then shifts the first character of every password
the user types. `allowsAutocorrection(keyboard:)` has the same shape and the
same hole, so the same field also gets autocorrect and a candidate bar.

Both are wrong in a way the user cannot see. The field is masked, so the
stray capital is invisible until the login is rejected, and the natural
diagnosis is "I mistyped it" rather than "the field did that".

`secure` now short-circuits both resolvers. Note where the check sits: BEFORE
the explicit `autocapitalize` prop, which is the only place in this resolver
where the author's word is not final. There is no reading of
`autocapitalize="sentences"` on a secret under which shifting its first
character is what was meant — the two props are simply in contradiction, and
one of them is about a password. `BaseTextInput` already suppresses
`@selectionChange` for secure inputs at the source on exactly that reasoning,
so the precedent for `secure` overriding rather than merging is already set.

Autocorrect is the stronger case of the two. A password is by definition not
a dictionary word, so every suggestion iOS offers is a wrong one, and the
candidate bar renders the secret in the clear above a field that was
deliberately masked.

Android is covered for parity, though it never showed the bug: Compose's
KeyboardOptions defaults to no capitalization, so an unannotated secure field
was already right. What it fixes there is the hole the `autocapitalize` prop
opened in #47 — an author setting `words` on a form and inheriting it onto
the password field. Autocorrect is left alone on Android; `KeyboardOptions`
gates it behind an API this repo doesn't use yet, and it is a separate change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Masterclass] Text input autocapitalizes first letter on iOS even with keyboard="email"

1 participant