-
Notifications
You must be signed in to change notification settings - Fork 1
Capabilities pt BR
🌐 Esta página em: English · Português
O que a máquina consegue fazer (câmera, localização, fotos, biometria, a rede) como serviços tomados por construtor, realizados por host. Um componente pede uma capacidade e nunca aprende qual alvo respondeu.
public sealed class ScanShell(ICamera? camera, ILocation? location) : StatefulComponent
{
private async void LocateAsync()
{
if (location is null) return; // este host não tem
var here = await location.GetCurrentAsync();
}
}Anulável, sempre. Um host que não consegue fazer algo não registra nada, então o serviço resolve
para null e o app mostra aquilo que ele já sabe mostrar. Essa é a resposta do framework em todo
lugar: reportar a ausência em vez de fingir com um stub que falha mais tarde, num momento pior.
| Capacidade | O que responde | Desde |
|---|---|---|
IPhotoLibrary |
GetPermissionAsync(), PickImageAsync(), uma foto que o usuário escolheu |
0.2.0-preview.1 |
ICamera |
Capture() para uma foto, StartPreviewAsync() para uma ICameraSession (textura ao vivo) |
0.2.0-preview.1 |
ILocation |
GetCurrentAsync(), e Subscribe(…) para o fluxo de mudanças |
0.2.0-preview.1 |
IBiometrics |
IsAvailable, AuthenticateAsync(reason): Face ID / Touch ID / o da plataforma |
0.2.0-preview.1 |
IMotionSensor |
Subscribe(…) para leituras de movimento do dispositivo |
0.2.0-preview.1 |
INetworkStatus |
Current, e Subscribe(…): alcançabilidade e o que a transporta |
0.2.0-preview.1 |
ITextClipboard |
Read() / Write(text), para o botão de copiar de uma página |
0.2.0-preview.1 |
IThemeController |
O interruptor claro/escuro. Veja ServerIntegration | 0.2.0-preview.1 |
IAppStorage / ISecretStore
|
Preferências duráveis e segredos. Veja Storage | 0.2.0-preview.10 |
ILocation, IMotionSensor e INetworkStatus são as que MUDAM de resposta, então devolvem um
IDisposable do Subscribe em vez de um valor. Descarte-o quando o componente sair, ou a assinatura
sobrevive àquilo que ela atualizava.
O INetworkStatus reporta alcançabilidade como a plataforma a enxerga, não uma promessa de que
um host específico vai responder. Um app mostrando um "online" velho é pior que um que não diz nada.
PermissionState tem quatro respostas, e a terceira é a que os apps esquecem:
-
NotDetermined: nunca perguntado. Perguntar mostra o prompt do sistema. -
Granted. -
Denied: recusado, e perguntar de novo não faz nada. Só os ajustes do sistema podem mudar isso, então um botão que repergunta é um botão que não faz nada; mande a pessoa para os ajustes. - A plataforma também pode reportar uma restrição que o usuário não consegue levantar de jeito nenhum.
No nativo, os textos que o SO mostra no prompt vêm do assembly:
[assembly: PhotonCapability("camera", "Escaneie um código de barras para adicionar um item.")]O build lê isso para o manifesto da plataforma (Info.plist, o manifesto do Android), então a razão
que o usuário lê é declarada uma vez, no app, ao lado do código que precisa dela.
Cruza por NOME, nunca por ordinal. O manifesto casa pela string da capacidade. Inserir um valor no meio de um enum uma vez transformou Location em Motion num manifesto publicado: o build ficou verde e o app pediu a permissão errada.
O browser realiza o que honestamente consegue (fotos por um file input; câmera, localização e rede pelas APIs web deles) e registra o resto como indisponível em vez de ausente, para que uma página que tome uma delas ainda receba um objeto e mostre o fallback, em vez de falhar ao construir.
Uma página toma o que precisa pelo construtor em todos os alvos:
public sealed class ProfilePage(IPhotoLibrary photos) : StatefulComponent
{
public override VisualNode Build(ComponentContext context) =>
photos.IsAvailable ? Button(label: "Escolher uma foto", onPressed: Pick) : Text("Sem biblioteca aqui");
}No nativo o ActivatorUtilities resolve. No browser o construtor transpilado resolve sozinho, pelo
NOME da interface: um tipo C# não existe em tempo de execução lá, mas IPhotoLibrary como string
existe, e os dois lados concordam nisso. A regra é perguntada ao modelo, não adivinhada de um nome:
um parâmetro de construtor cujo tipo é uma INTERFACE é uma dependência, todo o resto é dado que quem
chama passa. Um componente toma aquilo que desenha (um rótulo, uma variante, um callback) e nenhum
desses é jamais uma interface. (IReadOnlyList<T> e IEnumerable<T> são dado, explicitamente.)
Desde 0.2.0-preview.18
public sealed class CopyButton : StatelessComponent
{
public override VisualNode Build(ComponentContext context) =>
context.GetService<ITextClipboard>() is { } clipboard
? IconButton(Icons.Copy, onPressed: () => clipboard.Write(_code))
: new Box(); // sem área de transferência aqui: desenhe nada, não um botão morto
}A injeção por construtor continua sendo a melhor resposta onde ela cabe: explícita, testável, legível
na assinatura. Mas ela só alcança a PÁGINA. Tudo abaixo precisava receber a mesma coisa na mão: um
cartão com um botão de copiar precisa de um ITextClipboard, então o artigo acima dele carregava um
que nunca usava, e a seção acima daquela também carregava. Um componente que ganhava uma necessidade
forçava uma edição em cada ancestral entre ele e a página.
O context.GetService<T>() responde nulo quando o alvo não tem a capacidade, que é a resposta que
quem chama qualquer capacidade tem que tratar de qualquer forma.
De ONDE ele resolve é assunto do host: o SSR usa o container da REQUISIÇÃO (então um registro scoped funciona e os registros da própria página vencem), o browser usa o que o boot registrou, um app Photon usa o do shell. O componente faz a mesma pergunta em todo lugar.
No WEB isso só chegou funcionando na 0.2.0-preview.21. O argumento de tipo era descartado na transpilação: a estratégia que transforma a chamada numa chave reconhecia o
RenderContextantigo do Core e umIServiceProvider, eComponentContextnão é nenhum dos dois, então a chamada caía no caminho de invocação comum, que descarta argumentos de tipo. Toda página pedia uma capacidade sem nome nenhum e recebia nulo de volta, justamente no alvo para o qual a feature existe.
Desde 0.2.0-preview.13
A mesma página tem que ser renderizável no servidor, e o servidor não tem câmera. Toda capacidade resolve lá para uma realização AUSENTE: ela se reporta indisponível e não devolve nada.
Sem isso a página não poderia nem ser construída: nenhum construtor que o container conseguisse satisfazer, e a requisição terminava em 500. A única página que faz algo era a única página que um crawler nunca via, e o visitante esperava o JavaScript só para ouvir que a página existe.
Ausente, deliberadamente, e não simulado. Não há câmera num datacenter e o localStorage do visitante
está na máquina do visitante, então um dublê no servidor seria pior que a falha: a página renderizaria
uma coisa, o browser hidrataria outra, e a divergência seria creditada ao reconciliador. A página toma
o ramo de disponibilidade que ela já precisa ter, e a capacidade real do boot do cliente substitui o
fallback no primeiro render do cliente.
O INetworkStatus é a exceção, e a exceção é o ponto: reportar offline gravaria o banner de offline
no markup que todo crawler e todo primeiro paint veem, para visitantes que acabaram de provar que
estão online ao buscar a página. Ele responde online.
Registrado com
TryAdd, então um app com uma resposta genuína no servidor (um armazenamento respaldado pela sessão do usuário) registra o dele e vence.
🌐 English · Português
🏁 Comece aqui
📱 Write-once
- Componentes write-once
- Superfície declarativa
- Motor Photon
- Design System
- Capacidades
- Armazenamento
- Formulários
- Editor de código
- Markdown
- Mermaid
- Renderização de Email
🏗️ Arquitetura
⚙️ Compilação
- Compilador
- Avaliação em tempo de compilação
- Recursos C# suportados
- Resolução de tipos externos
- Fluxo de build
- Diagnósticos
⚡ Runtime
🔌 Servidor
🎨 Ecossistema
🚀 Desenvolvimento