← Todos os Posts

canOpenURL foi descontinuado: o que chamar no lugar

Em três frases, a Apple aposentou um método que existe desde o iOS 3.0: “canOpenURL: está descontinuado. Tente abrir a URL e trate qualquer falha em vez de validá-la antes. Usar universal links em vez de esquemas de URL personalizados elimina completamente a necessidade dessa validação.”1

A entrada leva o radar 179874781 e aparece em UIKit, Deprecations, logo abaixo da exigência de ciclo de vida baseado em cenas que impede apps de abrir.1 A entrada vizinha nomeia uma consequência. A do canOpenURL não nomeia nenhuma.

Cada uma das três frases é uma instrução separada, e só a segunda responde à pergunta que o desenvolvedor realmente tem: o que eu escrevo no lugar? A resposta da Apple muda o formato da chamada, não apenas o nome.

Resumo

  • A Apple descontinuou canOpenURL(_:) na versão 27.0 do iOS, iPadOS, Mac Catalyst, tvOS e visionOS, com a mensagem “Prefer attempting to open URLs and handling any failures.” (“Prefira tentar abrir URLs e tratar as falhas.”)2 Sem versão de remoção, sem consequência em tempo de execução.
  • A mudança que realmente morde é um número, e ele está na própria discussão do método descontinuado, não em nenhuma nota de versão: “Apps compilados com o SDK do iOS 27 ou posterior estão limitados a no máximo 25 entradas na chave LSApplicationQueriesSchemes”, contra 50 antes.2 O gatilho é o SDK com o qual você compila.
  • A troca mecânica é open(_:options:completionHandler:) com seu booleano, que não exige entrada na lista de esquemas permitidos: “o método open(_:options:completionHandler:) não está sujeito à exigência de LSApplicationQueriesSchemes.”2
  • Um padrão fica sem substituto, porque o canOpenURL responde antes de você desenhar qualquer coisa, enquanto a tentativa responde por consequência: um sucesso coloca outro app em primeiro plano.2 O que sobrevive é universalLinksOnly, uma opção de open presente desde o iOS 10 que a Apple não descontinuou: ela “abre a URL somente se ela for um universal link válido e houver um app instalado capaz de abri-la.”3
  • O SwiftUI nunca teve o método, e o booleano do completion já significa “consegue abrir”, não “abriu”.415 Em sete dos meus apps publicados e 463 arquivos Swift de primeira parte, canOpenURL e LSApplicationQueriesSchemes aparecem zero vezes cada um.6

A descontinuação sem consequência, e o número que tem uma

A Apple escreveu uma descontinuação, não uma remoção. A página do símbolo traz deprecatedAt 27.0 nas cinco plataformas em que UIApplication existe, e o resumo, a discussão, o contrato do valor de retorno e a declaração continuam todos no lugar.2 Nenhuma página da Apple nomeia uma versão em que o método deixa de funcionar, e o precedente mais próximo aponta para o outro lado: openURL(_:), descontinuado no iOS 10.0, ainda tem página, e sua nota hoje diz “Chamar este método não tem efeito.”7 Uma década de descontinuação produziu um método inerte, não ausente — o que é precedente, não cronograma.

O anúncio também é mais estreito que a descontinuação. A nota de versão vive numa página que a Apple intitula “iOS & iPadOS 27 Beta 4 Release Notes” e não aparece em nenhum outro lugar, enquanto os metadados do símbolo descontinuam o método igualmente no tvOS e no visionOS, e nem a página de atualizações do UIKit de junho de 2026 nem as notas de versão do Xcode 27 mencionam o assunto.1289 A página do símbolo é a metade durável desse registro.

É isso que faz do número enterrado a história de verdade. Dentro da própria discussão do método descontinuado, no mesmo aparte que exige a declaração em primeiro lugar: “Apps compilados com o SDK do iOS 15 ou posterior estão limitados a no máximo 50 entradas na chave LSApplicationQueriesSchemes. Apps compilados com o SDK do iOS 27 ou posterior estão limitados a no máximo 25 entradas na chave LSApplicationQueriesSchemes.”2

A lista de esquemas permitidos sobrevive à descontinuação do único método que ela serve, e o teto cai pela metade para apps compilados com o novo SDK. A Apple declara o teto e para por aí. Nada diz o que acontece depois da entrada 25, e ler as duas metades do aparte em conjunto dá uma resposta provável, não documentada: esquemas não declarados sempre retornam false, então um app que declara 40 esquemas e é recompilado provavelmente passa a tratar 15 deles como ausentes. A Apple nunca diz quais 15, nem que o truncamento segue a ordem do array. Trate o mecanismo como inferência e o risco como real, porque um false tem a mesma cara quando falta o app e quando falta a declaração.

O parágrafo seguinte da mesma discussão traz um segundo limite, e os dois são alternativas, não somas. A Apple os condiciona ao SDK com o qual você compilou: “Se você compilar seu app com uma versão anterior do iOS mas ele estiver rodando no iOS 9.0 ou posterior, você pode chamar este método até 50 vezes. Depois desse limite, chamadas subsequentes sempre retornam false. Se o usuário reinstalar ou atualizar o app, o iOS zera o limite.”2 Leia a condição. Esse orçamento pertence a apps compilados com um SDK anterior ao iOS 9, coisa que você não publica hoje. Todo app atual cai no outro ramo, o teto de declaração, que é justamente o que a Apple cortou de 50 para 25 entradas. Os dois se esgotam no mesmo false silencioso, e os dois estão documentados apenas dentro do método que a Apple acabou de descontinuar.

A leitura de privacidade também é inferência. O canOpenURL era a forma padrão de detectar quais apps a pessoa tinha instalados — um sinal do dispositivo, não uma verificação de link — e a própria definição de fingerprinting da Apple abrange APIs “usadas indevidamente para acessar sinais do dispositivo na tentativa de identificar o dispositivo ou o usuário.”10 A Apple nunca conecta as duas coisas: o método não aparece em nenhuma lista de APIs de motivo obrigatório, e a nota de versão, a mensagem de descontinuação e a página do símbolo dão todas uma justificativa puramente mecânica.1210 Um teto de consulta cortado pela metade combina com a história de privacidade — e a Apple não a escreveu.

O que o canOpenURL realmente prometia

Um true era uma garantia sobre a chamada seguinte, não uma descrição da URL: “Quando este método retorna true, o iOS garante que chamadas subsequentes ao método open(_:options:completionHandler:) com a mesma URL vão abrir com sucesso um app capaz de lidar com ela.”2 Um false era ambíguo por design, com duas causas e nenhuma forma de saber qual disparou: “false se o dispositivo não tiver um app instalado registrado para tratar o esquema da URL, ou se você não tiver declarado o esquema da URL no seu arquivo Info.plist.”2

É fácil supor que ele respondia a três coisas que nunca respondeu: “O valor de retorno não indica a validade da URL, se o recurso especificado existe nem, no caso de um universal link, se o dispositivo tem um app instalado registrado para responder ao universal link.”2 A terceira cláusula importa para a migração que a Apple recomenda, e ela volta mais abaixo.

A propriedade que tornava o método estruturalmente útil é justamente a que nada substitui. O canOpenURL carrega a palavra-chave nonisolated na declaração, e a Apple afirma sem rodeios que “você pode chamar este método com segurança em uma thread que não seja a principal.”2 Um booleano síncrono disponível fora do main actor consegue condicionar uma decisão de layout antes de qualquer coisa ser renderizada.

Tentar e tratar: o formato que muda

A instrução de substituição da Apple cabe em uma única oração, e o antes e depois parece trivial.

// Before: validate, then open. Requires an Info.plist declaration.
let url = URL(string: "someapp://profile/42")!
if UIApplication.shared.canOpenURL(url) {
    UIApplication.shared.open(url)
} else {
    presentWebFallback()
}
<key>LSApplicationQueriesSchemes</key>
<array>
    <string>someapp</string>
</array>
// After: attempt, then handle. No declaration needed.
let url = URL(string: "someapp://profile/42")!
let opened = await UIApplication.shared.open(url)
if !opened {
    presentWebFallback()
}

A entrada no Info.plist desaparece, e a Apple diz isso diretamente: “Diferentemente deste método, o método open(_:options:completionHandler:) não está sujeito à exigência de LSApplicationQueriesSchemes. Se houver um app disponível para tratar a URL, o sistema vai abri-lo, mesmo que você não tenha declarado o esquema.”2 Uma base de código que migra por completo apaga a chave e, com ela, o problema das 25 entradas.

Duas diferenças sobrevivem à reescrita, e as duas são estruturais.

A primeira é isolamento e momento de execução. A declaração de UIApplication é @MainActor class UIApplication, então open roda no main actor, e seu resumo o descreve exatamente assim: “Tenta abrir de forma assíncrona o recurso na URL especificada.”1112 O booleano síncrono, chamável fora da thread principal, acabou — então lógica de validar-e-abrir que mora numa camada de modelo, numa fila em segundo plano ou num helper sem isolamento precisa mudar de lugar ou virar async.

A segunda é que você não pode mais perguntar em silêncio. Quando a tentativa dá certo, outro app está em primeiro plano: “o iOS abre esse app e passa a URL para ele. (Abrir o app traz o outro app para primeiro plano.)”12 A resposta chega como efeito colateral de agir sobre ela. A Apple não documenta nada visível para o caso de falha, apenas que “o completion handler é chamado com o parâmetro success definido como false” — mas todo padrão que precisava da resposta antes da ação a perde: mostrar uma linha de “abrir em outro app” só quando o app existe, ordenar uma share sheet pelo que está instalado, ou escolher um padrão entre destinos concorrentes.12

A própria documentação da Apple também não acompanhou. A página de open(_:options:completionHandler:) ainda instrui: “Para determinar se há um app instalado capaz de tratar a URL, chame o método canOpenURL(_:) antes deste.”12 A página do substituto recomenda o método descontinuado.

A verificação de presença que sobrevive

Um caminho documentado responde à pergunta do canOpenURL tentando, sem nada visível ao usuário quando a resposta é não. UIApplication.OpenExternalURLOptionsKey.universalLinksOnly existe desde o iOS 10, a Apple não a descontinuou, e o comportamento é exatamente uma verificação de presença: “o método abre a URL somente se ela for um universal link válido e houver um app instalado capaz de abri-la.”3

// Presence check with no side effect when the app is absent.
let url = URL(string: "https://myphotoapp.example.com/albums?albumname=vacation")!
let installed = await UIApplication.shared.open(
    url,
    options: [.universalLinksOnly: true]
)
if !installed {
    presentWebFallback()   // nothing opened, nothing switched
}

O ramo do false é a parte valiosa. Nenhum navegador abriu, nenhum app veio ao primeiro plano, e quem chamou descobriu o que o canOpenURL costumava contar. A contrapartida se esconde no nome da opção: sem ela, uma tentativa em https dá certo sempre que um navegador puder assumi-la, já que “se nenhum app estiver disponível para tratar um universal link, o iOS o encaminha ao navegador padrão da pessoa, permitindo que o site associado responda.”2 A opção compra um booleano com significado suprimindo justamente o fallback que torna os universal links agradáveis. A Apple define o tipo do valor como “um objeto NSNumber contendo um valor booleano”, o que um true do Swift satisfaz via ponte com Objective-C.3

O preço é arquitetural, e é a terceira frase da Apple. Universal links exigem uma associação de mão dupla: “Quando alguém instala seu app, o sistema verifica um arquivo armazenado no seu servidor web para confirmar que seu site autoriza seu app a abrir URLs em seu nome. Só você pode guardar esse arquivo no seu servidor, o que torna segura a associação entre seu site e seu app.”13 Um arquivo no servidor sob seu controle é exatamente o que um esquema personalizado nunca exigiu — e é por isso que a migração funciona para a sua própria família de apps e não faz nada por um terceiro que publica só theirapp://. Duas surpresas documentadas vêm junto: seu app abrindo o próprio universal link não é roteado para dentro dele, e um toque no mesmo domínio enquanto se navega no seu site pelo Safari continua no Safari.13

E é aqui que aterrissa aquela terceira cláusula lá de cima: o canOpenURL também nunca respondeu à pergunta de presença para um universal link.2 Migrar remove a detecção de presença em vez de portá-la, porque uma tentativa https simples já dá certo tanto se o app responder quanto se apenas o site responder. Um padrão que só funcionava porque esquemas personalizados vazavam o estado de instalação vai embora junto com os esquemas — e a nota de versão da Apple apresenta essa perda como o motivo para migrar.

O SwiftUI nunca teve o método

O lado SwiftUI é curto, e a notícia é boa. EnvironmentValues.openURL, OpenURLAction e Link têm disponibilidade a partir do iOS 14.0 sem descontinuação, e nenhum dos três expõe uma verificação de validade.4514

O que o SwiftUI oferece no lugar é tentar-e-tratar com a semântica que a Apple agora quer em todo canto, e a documentação do parâmetro de completion diz que o booleano responde à pergunta antiga: “Um closure que o método chama depois de determinar se consegue abrir a URL, mas possivelmente antes de abri-la por completo. O closure recebe um valor booleano que indica se o método consegue abrir a URL.”15 Consegue abrir, não abriu. O próprio exemplo da Apple imprime esse valor:

openURL(url) { accepted in
    print(accepted ? "Success" : "Failure")
}

O Link não expõe booleano nenhum e delega ao ambiente, onde o padrão já implementa a história dos universal links: “a ação padrão abre um Universal Link no app associado, se possível, ou no navegador web padrão do usuário, caso contrário.”5 Um OpenURLAction personalizado que retorne .handled, .discarded ou .systemAction intercepta cada Link e cada link markdown num Text que lê a ação desse ambiente.5

Há uma lacuna que importa antes de planejar uma migração só em SwiftUI. OpenURLAction não tem dicionário de opções. Suas assinaturas de chamada são callAsFunction(_:), callAsFunction(_:completion:) e a adição do iOS 26 callAsFunction(_:prefersInApp:), e nenhuma das três aceita universalLinksOnly.1518 Uma verificação de presença sem efeito colateral ainda significa chamar UIApplication.shared.open.

Sete apps, 463 arquivos, zero chamadas

Auditei meu próprio portfólio antes de escrever sobre o dos outros, esperando uma lista de migração. Não há nada a migrar.6

App Arquivos Swift canOpenURL LSApplicationQueriesSchemes Pontos de chamada que abrem URL
Reps 77 0 0 5
Return 57 0 0 2
Banana List 55 0 0 2
Ace Citizenship 26 0 0 2
Water 34 0 0 0
ResumeGeni 71 0 0 12
Yawara 143 0 0 0
Total 463 0 0 23

O zero sobreviveu a três passagens de ampliação do escopo, até cada pacote Swift vendorizado e cada tipo de arquivo em cada repositório.6 Nenhum esquema consultado é declarado em 192 arquivos .plist, .pbxproj, .entitlements e .xcconfig — o que faz sentido: um app sem chamada a canOpenURL não tem motivo para declarar nada.

O motivo é banal e provavelmente comum. Dos 23 pontos de chamada, nove abrem um documento jurídico, quatro passam a bola para o produto web do próprio app, dois abrem census.gov a partir de um alerta para o usuário encontrar seu representante, dois abrem a URL de uma vaga que chega de uma API, dois são exportações file: exclusivas do macOS e um é UIApplication.openSettingsURLString atrás de um alerta de “Health Access Required”. Nenhum desses 20 consulta outro app, porque todo destino é https, que o Safari sempre atende, ou uma URL de sistema. Os três restantes carregam todo o comportamento interessante.

O único lugar que valida antes de abrir não está fazendo o que a descontinuação trata: o fluxo de cobrança do ResumeGeni faz POST para /api/me/portal e então checa url.scheme == "https" na URL devolvida pelo servidor, para o caso de um servidor comprometido ou com bug entregar ao app uma URL file: ou de esquema personalizado.6 A nota da Apple mira a validação de existência de um app-alvo e nada diz sobre confiar em entrada remota, então apagar essa guarda seria uma regressão de segurança, não uma migração.

Os outros dois são os handoffs de macOS do Banana List, e um deles guarda a única verificação de presença genuína do portfólio. O app pergunta NSWorkspace.shared.urlForApplication(withBundleIdentifier: "com.anthropic.claudefordesktop") != nil antes de entregar uma extensão .mcpb empacotada ao Launch Services, com um botão ao lado abrindo claude.com/download para quem precisa do app primeiro. O comentário no código nomeia o motivo: sem a verificação, o macOS exibe seu próprio diálogo de “nenhum aplicativo definido para abrir o documento”.6 Outra plataforma, e o dado mais útil da auditoria, porque flagra o modo de falha do tentar-e-tratar no mundo real. Uma tentativa fracassada nem sempre é silenciosa, e quando o sistema fala por você o usuário lê um erro do sistema em vez do seu fallback.

Nenhum repositório declara applinks:, então nenhum app aqui suporta universal links, e a correção arquitetural da Apple também segue sem ser paga do meu lado.6 Um esquema personalizado custa uma entrada no Info.plist; um universal link custa um arquivo num servidor web, um entitlement e um domínio sob seu controle.

Isso aponta para onde está o trabalho de verdade: não no código de apps de primeira parte, mas em share sheets que se reordenam pelo app instalado, seletores de “abrir em” e SDKs de atribuição — e nada disso é descartado por um repositório limpo.

A auditoria em si é mais fácil aqui do que em outros pontos do ciclo 27. Diferentemente das chaves de launch screen, que chegam pelas configurações de build e derrotam por completo uma busca textual, LSApplicationQueriesSchemes não tem equivalente INFOPLIST_KEY_ na referência de build settings da Apple — então um grep é o primeiro movimento certo, e ver de quem são os esquemas nesse array revela qual dependência quer a resposta.16

Perguntas frequentes

O canOpenURL para de funcionar no iOS 27?

Não, e a Apple não disse quando isso vai acontecer. O símbolo traz deprecatedAt 27.0, e todo o resto da página — o contrato do valor de retorno, a garantia sobre a chamada open seguinte, o teto de declaração e o orçamento de chamadas legado — continua documentado.2 Nenhuma versão de remoção aparece na nota de versão, na página do símbolo ou nas notas de versão do Xcode 27.129 Planeje um aviso e um declínio lento, não uma quebra.

O que escrevo no lugar, se eu precisar saber se o app está instalado?

Depende de você controlar ou não o destino. Se controla, publique um universal link e passe universalLinksOnly para open: a Apple documenta a opção como aquela que abre a URL apenas quando ela é um universal link válido e há um app instalado para assumi-la, então um false significa que nada abriu e nada trocou.3 O custo é um arquivo de associação de mão dupla no seu servidor web.13 Se um terceiro publica apenas um esquema personalizado, não existe substituto: o canOpenURL continua respondendo, continua exigindo sua declaração no Info.plist, e o teto dessa declaração cai pela metade, para 25 entradas, em apps compilados com o SDK do iOS 27 ou posterior.2 Código SwiftUI não precisa de mudança nessa frente, porque openURL e Link nunca ofereceram a verificação.414

Ainda preciso do LSApplicationQueriesSchemes?

Só para o canOpenURL. A documentação de Launch Services da Apple define a chave inteiramente em função do método descontinuado: ela “especifica os esquemas de URL que você quer que o app possa usar com o método canOpenURL: da classe UIApplication.”17 A chave não tem página alguma na referência moderna de Information Property List da Apple, e a discussão do método descontinuado aponta para o acervo legado.217 O substituto não precisa de nada, porque a Apple isenta o open da exigência de declaração de forma explícita.2 Termine a migração e o array some; mantenha uma chamada e você mantém o array, a exigência de declaração e o teto menor.

A App Store vai rejeitar um build com mais de 25 entradas?

Nenhuma página da Apple diz isso. O teto aparece num único lugar, a discussão de canOpenURL(_:), e a Apple o formula como um limite do que a chave comporta, não como regra de submissão.2 Nada nas notas de versão do iOS 27, na página de atualizações do UIKit ou nas notas de versão do Xcode 27 associa uma consequência de revisão a ele, e o sintoma documentado para um esquema que o sistema trata como não declarado é um false puro em tempo de execução.1289 As App Store Review Guidelines não contêm nenhuma ocorrência de LSApplicationQueriesSchemes, canOpenURL ou da cifra de 25 entradas — que é onde uma regra de submissão moraria, se existisse.19 Ou seja: a falha para a qual se preparar é uma resposta errada no seu próprio código, não um build rejeitado.

Pontos principais

Para desenvolvedores iOS: - Troque if canOpenURL(url) { open(url) } por let opened = await open(url) com um fallback em !opened, e apague a entrada correspondente em LSApplicationQueriesSchemes quando não sobrar nenhuma chamada.2 - Mantenha qualquer verificação de esquema que você faça numa URL vinda de um servidor. A nota da Apple mira a validação de presença de app, não entrada remota, e as duas parecem idênticas num grep.

Para times que publicam deep links entre apps da mesma família ou SDKs: - Recorra a universalLinksOnly em vez de canOpenURL quando precisar de uma resposta sobre presença, e reserve orçamento para o arquivo de domínios associados que ela exige.313 É a única verificação documentada que não deixa nada visível ao usuário quando o app está ausente. - Conte as entradas do seu array LSApplicationQueriesSchemes antes de compilar com o SDK do iOS 27. Qualquer coisa além de 25 entradas cai fora do teto documentado, e o sintoma da Apple para um esquema não declarado é um false puro — idêntico a um app que não está instalado.2

Para quem gerencia releases: - Não trate a descontinuação como bloqueio de release. Sem data de remoção e sem consequência em tempo de execução, ela fica atrás da chave de launch screen, que custa uma rejeição, da exigência de cenas, que impede o app de abrir, e da macro @State, que quebra o build. - Trate o teto de 25 entradas como o único item com gatilho real, porque ele depende do SDK com o qual você compila, não do sistema que seus usuários rodam.2


O ciclo 27 segue chegando em pesos diferentes, e ler o peso é o que faz você gastar bem o ciclo: a chave de launch screen bloqueia uma submissão, a exigência de cenas impede um app de abrir, a macro @State quebra um build, On Demand Resources dispara um relógio de migração, e o canOpenURL apenas avisa. Fabricar urgência em torno do aviso desperdiça um ciclo. A linha que merece atenção está num parágrafo de discussão, não numa nota de versão — e é um número. O hub completo da série é a Série Ecossistema Apple.

Referências


  1. Apple, iOS & iPadOS 27 Release Notes, seção UIKit, Deprecations (radar 179874781). A página se intitulava “iOS & iPadOS 27 Beta 4 Release Notes” na consulta, assim como todas as demais páginas de notas de versão citadas neste artigo, então trate toda a redação de notas de versão aqui como provisória; os metadados de disponibilidade das páginas de símbolo são o registro mais durável. Fonte da entrada completa citada aqui: “canOpenURL: is deprecated. Attempt to open the URL and handle any failure instead of validating it first. Using universal links instead of custom URL schemes removes the need for this validation entirely.” A entrada vem logo após a do ciclo de vida baseado em cenas (radar 141837548), “Apps built with the latest SDK must adopt the scene-based life cycle or they fail to launch.” Verificado contra o JSON da documentação da Apple em 26 de julho de 2026, porque a página HTML é renderizada via JavaScript. O mesmo JSON contém exatamente uma ocorrência de canOpenURL e uma do radar 179874781. As notas de versão de tvOS 27, visionOS 27, watchOS 27 e macOS 27 foram consultadas no mesmo dia, intituladas “tvOS 27 Beta 4”, “visionOS 27 Beta 4”, “watchOS 27 Beta 4” e “macOS 27 Golden Gate Beta 4” respectivamente, e não contêm nenhuma ocorrência de qualquer uma das duas strings. 

  2. Apple, canOpenURL(_:), referência de método de instância do UIKit. Declarado nonisolated func canOpenURL(_ url: URL) -> Bool, introduzido no iOS 3.0 (Mac Catalyst 13.1, tvOS 9.0, visionOS 1.0) e marcado com deprecatedAt 27.0 em iOS, iPadOS, Mac Catalyst, tvOS e visionOS, cada um com a mensagem de disponibilidade “Prefer attempting to open URLs and handling any failures.” O resumo de descontinuação da página repete duas das três frases da nota de versão literalmente: “Attempt to open the URL and handle any failure instead of validating it first. Using universal links instead of custom URL schemes removes the need for this validation entirely.” Fonte da documentação do valor de retorno (“false if the device doesn’t have an installed app registered to handle the URL’s scheme, or if you haven’t declared the URL’s scheme in your Info.plist file; otherwise, true”), da garantia (“When this method returns true, iOS guarantees subsequent calls to the open(_:options:completionHandler:) method with the same URL will successfully launch an app that can handle the URL. The return value doesn’t indicate the validity of the URL, whether the specified resource exists, or, in the case of a universal link, whether the device has an installed app registered to respond to the universal link”), da nota sobre threading (“You can call this method safely on a thread that isn’t the main thread”), do aparte sobre a lista de esquemas permitidos citado aqui, incluindo os dois tetos (“Apps linked on or after iOS 15 are limited to a maximum of 50 entries in the LSApplicationQueriesSchemes key. Apps linked on or after iOS 27 are limited to a maximum of 25 entries in the LSApplicationQueriesSchemes key”), do orçamento de chamadas em tempo de execução citado aqui (“If you link your app against an earlier version of iOS but it is running in iOS 9.0 or later, you can call this method up to 50 times. After reaching that limit, subsequent calls always return false. If the user reinstalls or upgrades the app, iOS resets the limit”), da isenção (“Unlike this method, the open(_:options:completionHandler:) method isn’t constrained by the LSApplicationQueriesSchemes requirement. If an app is available to handle the URL, the system will launch it, even if you haven’t declared the scheme”) e do fallback de universal link (“if no app is available to handle a universal link, iOS routes it to the person’s default browser, allowing the associated website to respond”). Uma frase do aparte soa estranha como publicada: “This method always returns false for undeclared schemes, even if the device doesn’t have a registered app installed.” A afirmação deste artigo sobre valores false ambíguos se apoia na seção de valor de retorno, não nessa frase. Verificado contra o JSON da documentação da Apple em 26 de julho de 2026. 

  3. Apple, UIApplication.OpenExternalURLOptionsKey.universalLinksOnly, referência de propriedade de tipo do UIKit. Disponível desde o iOS 10.0 (Mac Catalyst 13.1, tvOS 10.0, visionOS 1.0), sem metadados de descontinuação em 26 de julho de 2026. Fonte do resumo (“URLs must be universal links and have an app configured to open them”) e da discussão citada aqui: “When you include this key in the options dictionary of the open(_:options:completionHandler:) method, the method opens the URL only if the URL is a valid universal link and there is an installed app capable of opening that URL. The value of this key is an NSNumber object containing a Boolean value.” 

  4. Apple, EnvironmentValues.openURL, referência de propriedade de instância do SwiftUI. Declarada @MainActor @preconcurrency var openURL: OpenURLAction, disponível a partir de iOS 14.0, iPadOS 14.0, Mac Catalyst 14.0, macOS 11.0, tvOS 14.0, visionOS 1.0 e watchOS 7.0, sem metadados de descontinuação em 26 de julho de 2026. Fonte do exemplo openURL(url) { accepted in ... } reproduzido aqui e da descrição da ação padrão citada neste artigo. 

  5. Apple, OpenURLAction, referência de estrutura do SwiftUI. Declarada @MainActor @preconcurrency struct OpenURLAction, disponível a partir do iOS 14.0 sem metadados de descontinuação. Fonte de “The system provides a default open URL action with behavior that depends on the contents of the URL. For example, the default action opens a Universal Link in the associated app if possible, or in the user’s default web browser if not” e da afirmação de que uma ação personalizada se aplica à “built-in Link view and Text views with markdown links, or links in attributed strings”. Os membros de Result nomeados neste artigo vêm da página filha, Apple, OpenURLAction.Result, que enumera handled, discarded, systemAction, systemAction(_:) e o método de tipo do iOS 26 systemAction(_:prefersInApp:)

  6. Levantamento do autor em sete projetos publicados para plataformas Apple (Reps, Return, Banana List, Ace Citizenship, Water, ResumeGeni e Yawara) no macOS 26.5.2, em 26 de julho de 2026, usando ripgrep sobre código Swift de primeira parte com build/, DerivedData/, .build/, Pods/, Carthage/, .swiftpm/, SourcePackages/ e checkouts/ de pacotes excluídos. A cobertura de arquivos foi reconciliada com find contra rg por repositório (77, 57, 55, 26, 34, 71 e 143 arquivos) para confirmar que nenhum Swift de primeira parte ignorado pelo git ficou de fora. O zero de canOpenURL foi verificado de três formas: Swift de primeira parte; Swift com --no-ignore --hidden, que inclui produtos de build e todo checkout de pacote vendorizado (3.946 arquivos Swift na árvore do ResumeGeni, 15.760 na árvore compartilhada do 941Kit); e todos os tipos de arquivo com --no-ignore. Zero em todas as passagens, incluindo código de terceiros e vendorizado. LSApplicationQueriesSchemes e INFOPLIST_KEY_LSApplicationQueriesSchemes retornaram zero em 192 arquivos .plist, .pbxproj, .entitlements e .xcconfig. Os 23 pontos de chamada compreendem seis views Link do SwiftUI, três chamadas a UIApplication.shared.open, 12 chamadas a openURL(...) (todas no ResumeGeni, a partir de seis declarações @Environment(\.openURL)) e duas chamadas a NSWorkspace.shared.open no código macOS do Banana List; a contagem de Link( exige limite de palavra, já que um padrão simples também casa com NavigationLink( e com vários tipos ...Link( específicos dos projetos. Não existe nenhum uso de SFSafariViewController nem de WKWebView em qualquer projeto, e nenhum repositório declara applinks:. A guarda de cobrança do ResumeGeni está em Profile/ProfileView.swift:1746; a verificação de presença do Banana List e seu comentário explicativo estão em Banana List/SettingsView.swift:321 e :329, e a frase “no application set to open the document” citada neste artigo é a redação daquele comentário no código, não a transcrição do diálogo do macOS; o deep link de Ajustes do Return está em Return/ContentView.swift:222. O target principal do Reps declara SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx"; nenhuma afirmação sobre plataformas neste artigo deriva de uma chave *_DEPLOYMENT_TARGET, e o alcance completo de plataformas de nenhum app é inferido de SUPPORTED_PLATFORMS, que apenas 40 das 80 configurações de build desses projetos sequer definem. 

  7. Apple, openURL(_:), referência de método de instância do UIKit. Declarado func openURL(_ url: URL) -> Bool, introduzido no iOS 2.0 e descontinuado no iOS 10.0 (Mac Catalyst 13.1). Fonte da nota de descontinuação atual: “Calling this method has no effect. Use the open(_:options:completionHandler:) method instead.” Consultado em 26 de julho de 2026. 

  8. Apple, UIKit updates, Apple Developer Documentation. A seção de junho de 2026 traz quatro subseções (General, App life cycle, Drag and drop e Text views) e inclui a exigência do ciclo de vida baseado em cenas sob App life cycle: “Starting in iOS 27, apps built with the latest SDK must use the scene-based life cycle or they fail to launch.” Buscas por canOpenURL e LSApplicationQueriesSchemes em 26 de julho de 2026; nenhum dos dois aparece em qualquer ponto da página. 

  9. Apple, Xcode 27 Release Notes. Buscas por canOpenURL, LSApplicationQueriesSchemes e radar 179874781 em 26 de julho de 2026; nenhum aparece. 

  10. Apple, Describing use of required reason API, documentação de Bundle Resources. Fonte da definição de fingerprinting da Apple citada aqui: “Some APIs that your app uses to deliver its core functionality… have the potential of being misused to access device signals to try to identify the device or user, also known as fingerprinting. Regardless of whether a user gives your app permission to track, fingerprinting is not allowed.” Busca em 26 de julho de 2026: canOpenURL e LSApplicationQueriesSchemes não aparecem na página, o que é a base para a afirmação aqui de que a Apple nunca conecta o método ao fingerprinting. A página descreve a exigência de declaração e remete a lista de categorias à documentação de NSPrivacyAccessedAPIType. A leitura de privacidade da descontinuação neste artigo é inferência do autor, não justificativa declarada pela Apple. 

  11. Apple, UIApplication, referência de classe do UIKit. Declarada @MainActor class UIApplication, disponível a partir do iOS 2.0 sem metadados de descontinuação. Fonte do isolamento no main actor que open(_:options:completionHandler:) herda e do qual canOpenURL(_:) se exclui com nonisolated

  12. Apple, open(_:options:completionHandler:), referência de método de instância do UIKit. Disponível a partir do iOS 10.0 sem metadados de descontinuação, declarado tanto na forma com completion handler quanto na forma async: func open(_ url: URL, options: [UIApplication.OpenExternalURLOptionsKey : Any] = [:], completionHandler completion: (@MainActor @Sendable (Bool) -> Void)? = nil) e func open(_ url: URL, options: [UIApplication.OpenExternalURLOptionsKey : Any] = [:]) async -> Bool. Fonte do resumo (“Attempts to asynchronously open the resource at the specified URL”), do comportamento de abertura citado aqui (“If the specified URL scheme is handled by another app, iOS launches that app and passes the URL to it. (Launching the app brings the other app to the foreground.) If no app is capable of handling the specified scheme, the completion handler is called with the success parameter set to false”) e da instrução que ainda recomenda o método descontinuado: “To determine whether an app is installed that is capable of handling the URL, call the canOpenURL(_:) method before calling this one. Be sure to read the description of that method for an important note about registering the schemes you want to employ.” Consultado em 26 de julho de 2026. 

  13. Apple, Allowing apps and websites to link to your content, documentação do Xcode. Fonte da exigência de associação no lado do servidor (“When someone installs your app, the system checks a file stored on your web server to verify that your website allows your app to open URLs on its behalf. Only you can store this file on your server, securing the association of your website and your app”), do fallback para navegador (“If the person hasn’t installed your app, the system opens the URL in their default web browser, allowing your website to handle it”), da observação de que um app abrindo o próprio universal link não é roteado para si mesmo (“If your app uses one of the above methods to open a universal link to your website, the link won’t open in your app”) e do comportamento de mesmo domínio no Safari descrito aqui. A página lista EnvironmentValues.openURL do SwiftUI e open(_:options:completionHandler:) do UIKit entre as chamadas que roteiam universal links. 

  14. Apple, Link, referência de estrutura do SwiftUI. Declarada @MainActor @preconcurrency struct Link<Label> where Label : View, disponível a partir de iOS 14.0, macOS 11.0 e watchOS 7.0 sem metadados de descontinuação. Fonte do comportamento padrão citado aqui: “When a user taps or clicks a Link, the default behavior depends on the contents of the URL. For example, SwiftUI opens a Universal Link in the associated app if possible, or in the user’s default web browser if not.” 

  15. Apple, OpenURLAction.callAsFunction(_:completion:), referência de método de instância do SwiftUI. Declarado @MainActor @preconcurrency func callAsFunction(_ url: URL, completion: @escaping (Bool) -> Void). Fonte da semântica de completion citada aqui: “A closure the method calls after determining if it can open the URL, but possibly before fully opening the URL. The closure takes a Boolean value that indicates whether the method can open the URL.” A irmã callAsFunction(_:) recebe apenas uma URL. Consultado em 26 de julho de 2026. 

  16. Apple, Build settings reference, documentação do Xcode. Busca em 26 de julho de 2026 por INFOPLIST_KEY_LSApplicationQueriesSchemes: a configuração não aparece, enquanto INFOPLIST_KEY_LSApplicationCategoryType, INFOPLIST_KEY_LSBackgroundOnly, INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace e INFOPLIST_KEY_LSUIElement aparecem. Uma etapa de build que mescla sua própria property list ainda pode injetar a chave, então a ausência faz de uma busca no repositório o primeiro movimento certo, não uma auditoria completa. 

  17. Apple, Launch Services Keys, Information Property List Key Reference (arquivo histórico da Apple). Fonte da definição da chave: “LSApplicationQueriesSchemes (Array - iOS) Specifies the URL schemes you want the app to be able to use with the canOpenURL: method of the UIApplication class. For each URL scheme you want your app to use with the canOpenURL: method, add it as a string in this array.” A página afirma que a chave “is supported in iOS 9.0 and later” e não menciona teto de entradas. Esse acervo arquivado é o destino do link dentro da própria discussão de canOpenURL(_:). A chave não tem página na referência atual de Information Property List da Apple, verificado em 26 de julho de 2026: documentation/bundleresources/information-property-list/lsapplicationqueriesschemes.json retorna HTTP 404, enquanto páginas irmãs de chaves LS* como lsapplicationcategorytype e lsbackgroundonly retornam 200. O próprio JSON de índice da referência também não serve como teste em nenhuma direção, já que enumera apenas sete grupos de chaves de primeiro nível e não nomeia nenhuma chave LS* individual; lsapplicationcategorytype, que tem página ativa, também está ausente dele. 

  18. Apple, OpenURLAction.callAsFunction(_:prefersInApp:), referência de método de instância do SwiftUI. Declarado @MainActor @preconcurrency func callAsFunction(_ url: URL, prefersInApp: Bool), disponível a partir de iOS 26.0, iPadOS 26.0, Mac Catalyst 26.0, macOS 26.0, tvOS 26.0, visionOS 26.0 e watchOS 26.0. Listado na página de OpenURLAction sob Instance Methods, não sob Calling the action. Recebe um único booleano em vez de um dicionário de opções, então é a terceira e última assinatura de chamada, e nenhuma das três aceita universalLinksOnly. Consultado em 26 de julho de 2026. 

  19. Apple, App Store Review Guidelines. Busca em 26 de julho de 2026 por LSApplicationQueriesSchemes, canOpenURL e “25 entries”: zero ocorrências de cada. Citado para sustentar a ausência de uma regra de submissão, não para embasar qualquer afirmação positiva. 

Artigos relacionados

A regra da launch screen no iOS 27: quatro chaves ou rejeição

Apps compilados com o SDK do iOS 27 precisam declarar uma launch screen, ou a App Store rejeita o envio. As quatro chave…

14 min de leitura

On Demand Resources foi descontinuado: o preço do Background Assets

A Apple descontinuou o ODR em 13 palavras. O substituto se divide em três caminhos, impõe um piso no iOS 26 e devolve a …

23 min de leitura