Na web aberta, tocar um Universal Link dá ao sistema operacional a chance de abrir seu app; um App Link no Android faz o mesmo. Dentro de um WebView, esse handshake morre. Quando um WebView carrega uma URL por conta própria, o sistema operacional nunca pergunta qual app deve tratá-la, então os arquivos de associação que você publicou (apple-app-site-association / assetlinks.json) nunca são consultados. Para um app fintech, isso significa que o redirecionamento que deveria levar o usuário ao app do banco para a autenticação forte 3DS/SCA, ou a uma wallet para confirmar um pagamento, cai em silêncio em uma página web — ou em lugar nenhum. Um runtime de propriedade do host faz a ponte com a abertura nativa para que o handshake funcione em todos os motores.
Os Universal Links nunca disparam dentro de um WebView
Universal Links e Android App Links são um mecanismo em nível de sistema. Quando um usuário toca um link no Safari, Chrome ou no navegador do sistema, iOS ou Android verifica o arquivo de associação do domínio e, se houver um app instalado que corresponda, o abre. A interceptação acontece antes de qualquer navegador renderizar a página. Mas quando um WebView carrega essa mesma URL, o app host já tem uma view web aberta e simplesmente a renderiza dentro dela. O sistema nunca é consultado, o arquivo de associação é irrelevante e o app nunca é lançado. A Apple documenta isso diretamente: instanciar um WKWebView para tratar um Universal Link faz o iOS abrir o site no Safari em vez do seu app; o link só abre o app se o usuário tocá-lo a partir de uma view já embutida — e mesmo assim o host deve agir. Redirecionamentos via JavaScript (window.location.href) não contam como navegação iniciada pelo usuário, então também são ignorados pelo resolvedor do sistema.
Esquemas URI personalizados também são suprimidos
A saída antiga — um esquema URI personalizado como myapp:// — também não é confiável dentro do WebView. No iOS não existe uma forma suportada para uma página dentro de um WKWebView forçar o host a ceder o controle; um esquema personalizado é suprimido na maioria dos contextos de webview e falha em silêncio sem fallback. No Android, um WebView puro também não resolve App Links; apenas as Android Custom Tabs (uma instância de navegador real) o fazem. Um host pode sobrescrever shouldOverrideUrlLoading no Android ou implementar decidePolicyFor navigationAction no iOS para chamar UIApplication.open, mas a maioria dos apps —especialmente redes sociais e super-apps que envolvem conteúdo de terceiros— não faz, porque manter o usuário dentro da view web é o ponto. O resultado é o mesmo em todo lugar: o salto entre apps do qual a página depende não ocorre.
O que quebra quando o handshake morre
- O redirecionamento 3DS/SCA para o app do banco falha. A autenticação forte frequentemente delega ao app do banco emissor. Dentro de um WebView esse salto é engolido, então a autenticação trava ou volta em silêncio a um fallback web que o banco nunca terminou.
- A confirmação de wallet e pagamentos quebra. Links "confirme no seu wallet" que deveriam deep-linkar no Google Pay, Apple Pay ou um banco simplesmente não fazem nada.
- Callbacks de vinculação de conta e OAuth falham. Fluxos "abra o app autenticador/banco para aprovar" caem em uma página em branco ou sem saída.
- Atribuição e handoffs de instalação são perdidos. Deep links diferidos que deveriam sobreviver a uma instalação e levar o usuário à tela correta quebram, prejudicando funis de ativação.
A solução do runtime
Um runtime de propriedade do host é dono do handshake entre apps. A página chama uma API uniforme —runtime.openApp(url)— e o host a resolve via mecanismo nativo da plataforma: UIApplication.open no iOS (com o Universal Link ou um esquema que o host valida) e um Intent / App Link no Android, com fallback web quando não há app instalado. O runtime também pode detectar que está dentro de uma view embutida e mostrar um botão explícito "Abrir no navegador", em vez de um link que parece não fazer nada. Uma integração, todos os motores, um handshake real no fim.
| Dimensão | WebView | Runtime de propriedade do host |
|---|---|---|
| Resolução de link pelo SO | Nunca consultado | Host chama abertura nativa |
| Universal / App Links | Engolidos | Respeitados via abertura nativa |
| Esquema URI personalizado | Suprimido / falha silenciosa | Handoff validado pelo host |
| Saída no iOS | Nenhuma confiável | UIApplication.open |
| Saída no Android | Apenas Custom Tabs | Intent / App Link |
| Handoff 3DS / wallet | Quebra | Funciona |
| Fallback sem app | Nenhum | Fallback web explícito |
Um app nativo consegue abrir outro app em todos os dispositivos. Um WebView puro, em quase nenhum. Um runtime dá à página uma única chamada — e um handshake que realmente aterriza.
Fontes
- Apple Developer — Suporte a Universal Links — instanciar um WKWebView para tratar um Universal Link abre o site no Safari em vez do app; o resolvedor do sistema é contornado.
- Apple Developer — UIApplication.open — a API nativa que o host deve chamar para rotear uma URL para fora do WebView para outro app.
- Deeplinkly — Deep links em navegadores in-app — o WebView carrega a URL por conta própria, então o sistema nunca resolve; Universal Links e App Links nunca chegam ao app que os declara.
- LinkRunner — Por que Universal/App Links quebram em navegadores in-app — WKWebView não dispara a interceptação de Universal Links; redirecionamentos JS não são iniciados pelo usuário e são ignorados.
- Tolinku — URI Schemes vs Universal Links — dentro do seu app (WKWebView / SFSafariViewController) os Universal Links estão desabilitados por padrão; você deve usar
UIApplication.shared.open().