Repository navigation
Conversation
<native:sign-in-with-apple-button> draws Apple's own button on iOS, so apps don't have to fake it with a <native:button> and an Apple logo. A tap only calls @press. The app starts the sign-in from there with whatever auth plugin it uses, so the element doesn't own any of it. Props: label (sign-in, continue, sign-up), button-style (auto, black, white, white-outline), corner-radius (or a rounded-* class), height (44 by default) and disabled. iOS wraps UIKit's ASAuthorizationAppleIDButton rather than SwiftUI's SignInWithAppleButton. The SwiftUI button's only initialiser runs the Apple ID request itself on every tap, so it can't be used as a plain tap target. The UIKit button only draws: the tap goes through a SwiftUI Button, because core's keyboard-dismiss tap gestures cancel touches in UIKit controls, so a target-action never fires. Android has no Apple button, so it draws nothing unless the tag has children, which render there as a fallback. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 16, 2026
The element drew UIKit's ASAuthorizationAppleIDButton and only fired @press, because SwiftUI's button always runs the Apple ID request itself. Now it is SwiftUI's button, and the element runs the request. New props feed the request: scopes (email, full-name), nonce (raw; the button sends Apple its SHA-256 hex) and state. The answer goes to PHP as a native event, Native\Mobile\UI\Events\SignInWithAppleCompleted or SignInWithAppleFailed, which components listen for with #[On]. @press now fires as the request starts. SwiftUI's button is UIKit underneath, so inside an EDGE screen it needs NativePHP/mobile-air#545, which stops core's keyboard-dismiss taps cancelling UIKit controls. Without it a tap does nothing. The corner radius is now a clip, so it can round the corners more than the system does but can't square them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The button hashed a raw nonce itself, so the raw value had to sit in the rendered tree. Apps now pass the SHA-256 of a raw nonce they keep in memory, the button sends it to Apple unchanged and the completed event echoes it back. The raw value stays out of the UI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
<native:sign-in-with-apple-button>, which is SwiftUI's ownSignInWithAppleButtonon iOS. Without it, apps fake the button with a<native:button>and theapple.logosymbol, and App Review is strict about the logo, title, font and proportions.Tapping it runs the Apple ID request, as SwiftUI's button always does. The answer comes back as a native event, which a component listens for:
This needs NativePHP/mobile-air#545. SwiftUI's button is UIKit underneath, and core's keyboard-dismiss tap gestures cancelled the touch in any UIKit control, so without #545 the button draws but a tap does nothing.
Props
label:sign-in(default),continueorsign-up, Apple's three titles. Anything else throws, solabel="Sign in with Apple"fails loudly instead of quietly drawing the default.button-style:auto(default),black,whiteorwhite-outline.autois black in light mode and white in dark mode. It's named like the date picker'spicker-styleso it doesn't read as a CSSstyleattribute.nonce: sent to Apple as given and echoed on the completed event. Apps pass the SHA-256 of a raw nonce they keep, so the raw value never sits in the rendered tree.scopes: any ofemailandfull-name. None by default.state: echoed on both events.corner-radius, or arounded-*class. SwiftUI's button can only be clipped, so this rounds the corners more than Apple does, up to a capsule, but can't square them.heightorh-*, 44 by default. Apple scales the title and logo with it.disabled,a11y-labelanda11y-hint. The system button looks the same when disabled, so the renderer dims it to 40%.@pressfires as the request starts, for clearing an earlier error.Colours, borders and padding are dropped, as they are for
<native:button>.Events
Native\Mobile\UI\Events\SignInWithAppleCompletedhasuserId,identityToken,authorizationCode,email,givenName,familyName,nonce,stateandrealUserStatus.SignInWithAppleFailedhascode(canceled,failed,invalid_response,not_handled,not_interactiveorunknown),messageandstate. The renderer builds the payloads by hand and core binds them to the constructors by name, so a test reads the Swift and checks every key against the constructor.The app still needs the
com.apple.developer.applesigninentitlement. Core only takes entitlements from plugins, and mobile-ui shouldn't add it to every app, so it's left to the app.Android
Apple has no button on Android, so the renderer draws nothing. If the tag has children, they render instead as an Android-only fallback. iOS ignores them.
Stacked on #132
This is based on
feat/custom-iconsbecause the devnull app tracks that branch. Nothing here uses custom icons, so it can move onto main if #132 changes course.Screenshots
iOS 26.2 simulator, with #545 applied.
Taller with
rounded-full, the system corners at 30pt (a radius of 0 can't square them), disabled, andwhite-outlinewithrounded-full:The devnull app's onboarding after a tap. The button ran Apple's request, the simulator answered with error 1000 (simulator builds get no entitlements), and the app showed its message from
SignInWithAppleFailed. The GitHub button above is a<native:pressable>drawn to match Apple's.The test app on an API 35 emulator. Only the fallback child draws, and its press reaches PHP:
Testing
tests/SignInWithAppleButtonTest.phphas 27 tests: Blade attributes reach the wire, the press handler rides in the props rather than on the node, every spelling of each label, style and scope, the errors for unknown values, nonce and state, the radius from a class and from the attribute, layout and dropped styling, children kept for Android, the fluent builder, the manifest entry, and the event payload keys against the event constructors. The suite gives 311 passed.@pressand runs the request,SignInWithAppleFailedarrives with thestateechoed, and a disabled button ignores taps. A successful sign-in needs a real device with the entitlement, which I haven't done.Docs: NativePHP/nativephp.com#562
🤖 Generated with Claude Code