diff --git a/config/docs.php b/config/docs.php index fa3fabd09..f5e38f4cb 100644 --- a/config/docs.php +++ b/config/docs.php @@ -86,7 +86,7 @@ 'mobile-ui' => [ 'name' => 'nativephp/mobile-ui', 'baseline' => '0.3', - 'released_versions' => ['0.1', '0.2', '0.3', '0.4', '0.5', '0.6', '0.7'], + 'released_versions' => ['0.1', '0.2', '0.3', '0.4', '0.5', '0.6', '0.7', '0.8', '0.9'], ], ], diff --git a/public/img/docs/edge-sign-in-with-apple-button-ios-dark.png b/public/img/docs/edge-sign-in-with-apple-button-ios-dark.png new file mode 100644 index 000000000..94f05d689 Binary files /dev/null and b/public/img/docs/edge-sign-in-with-apple-button-ios-dark.png differ diff --git a/public/img/docs/edge-sign-in-with-apple-button-ios-light.png b/public/img/docs/edge-sign-in-with-apple-button-ios-light.png new file mode 100644 index 000000000..15bf14e0a Binary files /dev/null and b/public/img/docs/edge-sign-in-with-apple-button-ios-light.png differ diff --git a/resources/views/docs/mobile/4/edge-components/sign-in-with-apple-button.md b/resources/views/docs/mobile/4/edge-components/sign-in-with-apple-button.md new file mode 100644 index 000000000..4f5e86bfa --- /dev/null +++ b/resources/views/docs/mobile/4/edge-components/sign-in-with-apple-button.md @@ -0,0 +1,187 @@ +--- +title: Sign in with Apple Button +order: 375 +--- + +## Overview + + + +
+ +![](/img/docs/edge-sign-in-with-apple-button-ios-light.png) + +![](/img/docs/edge-sign-in-with-apple-button-ios-dark.png) + +
+ +Apple's own Sign in with Apple button. On iOS it is SwiftUI's `SignInWithAppleButton`, so the logo, title, font and +proportions are always the ones [Apple's guidelines](https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple) +ask for. App Review checks these, so use this element rather than a `` with an Apple logo. + +Tapping the button runs the Apple ID request, as the system button always does. Apple's answer comes back to your +component as a native event, so you don't call anything to start the sign-in. + +@verbatim +```blade + +``` +@endverbatim + +```php +use Native\Mobile\Attributes\On; +use Native\Mobile\UI\Events\SignInWithAppleCompleted; +use Native\Mobile\UI\Events\SignInWithAppleFailed; + +#[On(SignInWithAppleCompleted::class)] +public function signedInWithApple(string $userId, ?string $identityToken = null, ?string $nonce = null): void +{ + // Send the identity token and your raw nonce to your server to verify. +} + +#[On(SignInWithAppleFailed::class)] +public function appleSignInFailed(string $code, string $message = ''): void +{ + if ($code === SignInWithAppleFailed::Canceled) { + return; + } + + // Show the person what went wrong. +} +``` + +On iOS the button needs `nativephp/mobile` 4.7 or later. Signing in also needs the Sign in with Apple capability on +your App ID and the `com.apple.developer.applesignin` entitlement in the app. NativePHP adds entitlements from plugins, +so use a plugin that declares it. + +## Props + +- `label` - The button's title: `sign-in` (default) for "Sign in with Apple", `continue` for "Continue with Apple", or + `sign-up` for "Sign up with Apple". Apple translates the title into the device's language. These are the only + titles Apple allows, so any other value throws an `InvalidArgumentException`. +- `button-style` - The button's colors: `auto` (default), `black`, `white` or `white-outline`. `auto` is black in light + mode and white in dark mode. Use `white-outline` on white or very light backgrounds. +- `nonce` - Sent to Apple as you give it, and echoed back on the completed event (optional). Pass the SHA-256 of a + random nonce you keep in memory, as hex. Apple's identity token carries the hash, and your server checks it against + the raw nonce. The raw value never needs to be in your view. +- `scopes` - What to ask for besides the user ID: any of `email` and `full-name`, as an array or a comma-separated + string (optional, default: none). Apple only sends them the first time someone signs in to your app. +- `state` - Any string, echoed back on both events (optional) +- `corner-radius` - Corner radius in points. A `rounded-*` class sets it too, and the attribute wins if you use both. + It can round the corners more than Apple does, up to a capsule at half the height or more, so `rounded-full` works. + It can't make them squarer than Apple's. +- `height` - Height in points, or an `h-*` class. Defaults to 44. The title and logo scale with the height. Apple asks + for at least 30. +- `disabled` - Dims the button and ignores taps (optional, boolean, default: `false`) +- `a11y-label` - Accessibility label. VoiceOver reads Apple's title when it's not set (optional) +- `a11y-hint` - Accessibility hint (optional) + +The button takes the width its parent gives it, like other controls. Use `w-full` to fill the row, or `w-[240]` for a +fixed width. Apple asks for at least 140 points. + +Background, border, shadow and padding classes are dropped, since Apple's guidelines fix how the button looks. Margins +and other layout attributes work as usual. + +## Events + +- `@press` - Component method called when the button is tapped, as Apple's request starts. Useful for clearing an + earlier error. + +The result arrives as one of two native events, which you listen for with `#[On(...)]` as above. + +`Native\Mobile\UI\Events\SignInWithAppleCompleted`: + +- `userId` - Apple's stable ID for this person in your app +- `identityToken` - The JWT Apple signed. Send it to your server to verify +- `authorizationCode` - A short-lived code your server can exchange with Apple +- `email`, `givenName`, `familyName` - Only on the first sign-in, and only for the scopes you asked for +- `nonce` - The `nonce` the button was given +- `state` - The `state` the button was given +- `realUserStatus` - `likely_real`, `unknown` or `unsupported` + +`Native\Mobile\UI\Events\SignInWithAppleFailed`: + +- `code` - `canceled` when the person closed Apple's sheet. Otherwise `failed`, `invalid_response`, `not_handled`, + `not_interactive` or `unknown` +- `message` - Apple's description of the error +- `state` - The `state` the button was given + +`unknown` is Apple's catch-all error 1000. On a phone it usually means the phone isn't signed in to an Apple Account, or +the app is missing the entitlement. Builds for the simulator always get it, since they don't carry entitlements, so test +a real sign-in on a device. + +## Android + +Apple has no Sign in with Apple button on Android, so this element draws nothing there. If you want something in its +place on Android, put it between the tags. Children only render on Android, and iOS ignores them. + +@verbatim +```blade + + Sign in with Apple + +``` +@endverbatim + +## Examples + +### A fresh nonce for every attempt + +Keep the raw nonce on the component and give the button its hash. Make a new one after every answer, so a stale +answer can't be used again. + +```php +public string $appleNonce = ''; + +public function mount(): void +{ + $this->appleNonce = bin2hex(random_bytes(32)); +} + +#[On(SignInWithAppleCompleted::class)] +public function signedInWithApple(string $userId, ?string $identityToken = null, ?string $nonce = null): void +{ + $raw = $this->appleNonce; + $this->appleNonce = bin2hex(random_bytes(32)); + + if ($nonce !== hash('sha256', $raw) || $identityToken === null) { + return; + } + + // Send $identityToken and $raw to your server. +} +``` + +@verbatim +```blade + +``` +@endverbatim + +### Titles and styles + +@verbatim +```blade + + + + + +``` +@endverbatim + +### Building it in PHP + +@verbatim +```php +use Native\Mobile\UI\Elements\SignInWithAppleButton; + +SignInWithAppleButton::make() + ->label('continue') + ->buttonStyle('black') + ->scopes(['email']) + ->nonce(hash('sha256', $this->appleNonce)) + ->cornerRadius(12) + ->fillWidth(); +``` +@endverbatim