Precisa de um ecrã que se comporte de forma diferente quando o túnel está ativo, e não há nenhuma chamada que lhe diga isso. É a restrição de entrega: no macOS nenhuma framework pública indica se uma VPN arbitrária está ligada, e no iOS o estado pertence à app que instalou a configuração. As verificações abaixo vão desse limite até algo que pode entregar, e qualquer uma delas pode terminar a pesquisa mais cedo.

Verificação 1: a pergunta tem sequer resposta?

O sandbox resolve a pergunta antes de qualquer API. Um processo de terceiros não consegue enumerar a extensão de outro fornecedor nem consegue ler a configuração dessa extensão, por isso não há nenhum objeto suportado a quem perguntar.

Todos os desvios batem na mesma parede. As listas de interfaces, scutil --nc list e as leituras diretas da configuração do sistema ou exigem direitos que o sandbox retém ou dependem de um comportamento que a Apple nunca prometeu. Se o plano precisa de uma dessas coisas, o problema é o plano. Uma funcionalidade construída sobre um detalhe não documentado é um pedido de suporte com data de entrega.

Verificação 2: a sua app gere o túnel sozinha?

Se gere, tem uma resposta suportada para a sua própria configuração. Carregue as preferências, trate do erro e depois leia o estado e subscreva as alterações em vez de fazer sondagens:

vpnManager.loadFromPreferences { error in
    // handle a load failure before reading status
}

NotificationCenter.default.addObserver(
    forName: .NEVPNStatusDidChange,
    object: nil,
    queue: nil
) { note in
    let status = (note.object as? NEVPNConnection)?.status
}

Seja preciso quanto ao que isto devolve: o estado do túnel que instalou. Um utilizador com outro cliente aparecerá como desligado, e isso não é um erro no seu código. Decida qual destas duas perguntas a sua funcionalidade está realmente a fazer antes de construir sobre a resposta.

Verificação 3: o que lhe diria realmente uma heurística?

A abordagem amplamente copiada pede ao dicionário de proxy do sistema as suas entradas com âmbito e lê as chaves, que por acaso são nomes de interface:

let settings = CFNetworkCopySystemProxySettings()?.takeRetainedValue() as NSDictionary?
let scoped = settings?["__SCOPED__"] as? [String: Any]
let names = scoped?.keys ?? []

Uma chave que comece por tap, tun, ppp, ipsec ou utun implica que existe uma interface com âmbito de túnel. Comparar o nome completo falha de imediato, porque o iOS 12 chegou com utun1 e utun2 e alguns perfis aparecem como ipsec4, ppp0 ou utun0. Compare antes os prefixos.

Mesmo assim, um nome não identifica nenhum proprietário. As funcionalidades do sistema e as extensões de outros fornecedores também criam interfaces utun, e uma pode existir sem transportar nada do seu tráfego. Leia isto como uma pista de que há algo em forma de túnel por perto, nada mais forte. Um falso positivo aqui é barato se só condicionar uma funcionalidade, e caro se condicionar o ecrã de início de sessão.

Verificação 4: pode testar o destino em vez disso?

Se a app só precisa de saber se um serviço interno está acessível, teste esse serviço. Uma sonda curta com tempo limite responde à pergunta de que a funcionalidade realmente depende, e continua a funcionar quando o utilizador muda de cliente ou o túnel muda de forma.

A acessibilidade é uma observação sobre o seu caminho. Um nome de interface é uma inferência sobre algo adjacente ao seu caminho, e é por isso que a heurística gera discussões nos relatórios de erros e a sonda gera provas. Se a sonda falhar enquanto o utilizador insiste que a VPN está ligada, tem agora um caminho concreto para depurar: resolução de nomes, rota ou permissão do serviço.

Verificação 5: a plataforma pode impor o que precisa?

Quando o requisito é a garantia de que nada escapa ao túnel, o perfil é o lugar certo, não o detetor. Ativar includeAllNetworks numa configuração de túnel completo no iOS 14 ou posterior altera o comportamento da plataforma em vez de pedir à sua app que o infira. O indicador está documentado em a referência do NEVPNProtocol.

É outro tipo de resposta: imposição em vez de observação. Além disso, só se aplica a perfis que controla, por isso não ajuda uma app de consumo que vigia o túnel de outra pessoa.

Verificação 6: torne a falha segura e observável

Desligue apenas a capacidade que precisa do túnel e deixe o resto da app utilizável. Um detetor que esconde toda a interface sempre que não tem a certeza cria uma falha para qualquer pessoa cujo túnel use um nome que nunca antecipou. Volte antes ao comportamento conservador: as funcionalidades locais continuam a funcionar e só a chamada remota que precisa mesmo do túnel é desativada. Essa escolha é uma decisão de produto, e merece ser escrita ao lado da heurística.

Registe o que observou: nomes de interface, versão da plataforma e o resultado de uma sonda. Isso transforma o relatório seguinte de uma discussão numa verificação. Depois prefira uma declaração suportada quando tiver uma, porque uma configuração gerida pode transportar a intenção explicitamente e remover a maior parte da razão para inspecionar nomes.

Se as verificações 1 a 5 o deixaram sem uma resposta suportada, entregue a sonda e rotule o resultado com honestidade. Um sinal documentado de melhor esforço é uma melhor decisão de produto do que uma API privada que se quebra na próxima versão.

Artigos relacionados