Skip to content

text_field: secure: true for masked password input - #12

Closed
C-Sinclair wants to merge 1 commit into
GenericJam:masterfrom
C-Sinclair:text-field-secure
Closed

text_field: secure: true for masked password input#12
C-Sinclair wants to merge 1 commit into
GenericJam:masterfrom
C-Sinclair:text-field-secure

Conversation

@C-Sinclair

@C-Sinclair C-Sinclair commented May 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a secure: true prop to text_field. iOS renders the field as a SwiftUI SecureField (masked input) instead of TextField. Cleartext still reaches the BEAM via on_change — apps hash/store the value as normal.

%{type: :text_field, props: %{
  value: assigns.password,
  placeholder: "Password",
  secure: true,
  on_change: {self(), :password_changed}
}}

The prop flows through Mob.Renderer.prepare_props/4's generic pass-through; no renderer changes were needed. Native side: MobNode.isSecure BOOL, parsed in mob_nif.m, branched on in MobRootView.swift via a @ViewBuilder field switch.

Version / CHANGELOG

CHANGELOG entry under ## [Unreleased]; no mix.exs bump. Matches the repo's pattern of feature-commits-without-bumps and a separate ride-along Bump to X.Y.Z commit at release-cut time (e.g. b974398).

Android

This PR is iOS-only. Mob's library doesn't own Android Compose UI — that lives per-app, generated from mob_new's MobBridge.kt.eex template. A companion PR against GenericJam/mob_new adds PasswordVisualTransformation + KeyboardType.Password to the template.

Until that ships, secure: true is a graceful no-op on Android: the prop travels through the renderer but the Compose TextField ignores it. No breakage for unpatched apps.

Why no reveal-toggle ("eye" button) yet

Worth its own PR. The iOS implementation needs ZStack-and-opacity to retain focus across SecureField/TextField swaps — the current @ViewBuilder if/else (and any naive toggle) tears the view down on every flip, dropping focus and dismissing the keyboard. Pairs with a trailingIcon slot in Compose. Out of scope here.

Test plan

  • mix test — 794 passed (27 doctests, 767 tests), 43 excluded. Two new renderer tests cover secure: true serialising and absence-by-default.
  • mix format --check-formatted — clean
  • mix credo --strict — clean (949 mods/funs, 0 issues)
  • mix compile --warnings-as-errors — clean
  • mix erlfmt --check src/ — clean
  • xcrun clang-format --dry-run -Werror ios/*.m ios/*.h — clean
  • Verified on iOS sim (iPhone 17, iOS 26.2): SecureField renders dots; BEAM :password assign holds cleartext ("hunter2"); Length: 7 label updates live.
  • Verified Android contract end-to-end with the matching MobBridge.kt patch applied locally: dots render, cleartext reaches BEAM, length label updates. (Template patch lands in the mob_new companion PR.)
  • swiftlint ios/ — not installed locally; the Swift diff is mechanical (single expression swapped for an @ViewBuilder computed property).

Files

  • ios/MobNode.h@property(nonatomic, assign) BOOL isSecure
  • ios/mob_nif.m — parses props[@"secure"] alongside keyboard / return_key
  • ios/MobRootView.swift@ViewBuilder var field picks SecureField or TextField; existing modifier chain applies uniformly to both
  • test/mob/renderer_test.exs — pass-through and default-absent tests
  • CHANGELOG.md — entry under ## [Unreleased]

Adds a `secure: true` prop to `text_field`. iOS swaps the underlying
SwiftUI control from `TextField` to `SecureField` when the prop is
set; cleartext still reaches the BEAM via `on_change` so apps can
hash/store the value normally. The prop is a plain pass-through
through `Mob.Renderer.prepare_props/4` — unknown atoms-as-values
already serialise as-is — so no renderer changes were needed.

Android consumes the same prop via `PasswordVisualTransformation` once
mob_new's `MobBridge.kt.eex` template is updated in a companion PR.
Until then `secure: true` is a graceful no-op on Android — the prop
travels but the Compose `TextField` ignores it (regular field, no
breakage).

Reveal-toggle ("eye" button) is intentionally deferred. The iOS
implementation needs `ZStack`-and-opacity to retain focus across
SecureField/TextField swaps (a plain if/else tears down the field,
dropping focus + dismissing the keyboard on every toggle). Worth its
own PR with the matching Compose `trailingIcon` slot wired up.

CHANGELOG entry lands under [Unreleased] — version bump rides along
with the next release commit per the repo's pattern.
@GenericJam

Copy link
Copy Markdown
Owner

Thanks @C-Sinclair — landed as 290254b in mob 0.6.15 with your authorship preserved. Paired with mob_new#6 (Android Compose side) landed as 4a41f47 in mob_new 0.3.8, so secure: true now works on both platforms in the same release.

CHANGELOG entries merged into the new [0.6.15] block (and mob_new [0.3.8]). Closing since the commit is on master.

@GenericJam GenericJam closed this May 20, 2026
pshoukry pushed a commit to pshoukry/mob that referenced this pull request Sep 9, 2026
…ixed (mob_new + mob_dev companion commits)

Four small lost-in-the-shuffle items closed in this batch. All four
were held up by Phase 2 work touching the same files (GenericJam#1/GenericJam#2/GenericJam#4 in
live_view_patcher.ex; GenericJam#5 in native_build.ex).

  GenericJam#1 — Phoenix LiveReload mac_listener warnings: code_reloader/watchers/
       live_reload disabled in on-device endpoint config.
  GenericJam#2 — esbuild/tailwind version-not-configured warnings: versions set
       via Application.put_env in mob_app.ex before ensure_all_started.
  GenericJam#4 — port 4200 collisions across multiple Mob LV apps: per-app hash
       into 4200..4999 via :erlang.phash2(:<app>, 800).
  GenericJam#5 — deploy auto-pick of iPhone over sim was silent: prints the
       --device <short-id> alternative when both are connected.

GenericJam#3 (WS→longpoll fallback in WKWebView) is investigation, not a fix —
deferred. GenericJam#6-GenericJam#11 are larger work (OTP rebuild, AX modifiers,
Compose semantics walker, Android 17 SELinux patch). GenericJam#12, GenericJam#13 already
fixed earlier. GenericJam#14 is moderate — sim node naming reconciliation
between mob_dev's connect.ex and mob_beam.m, deferred.
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.

2 participants