Estou tentando converter este trecho de código para Swift. Estou lutando para sair do chão devido a algumas dificuldades.
- (BOOL) connectedToNetwork
{
// Create zero addy
struct sockaddr_in zeroAddress;
bzero(&zeroAddress, sizeof(zeroAddress));
zeroAddress.sin_len = sizeof(zeroAddress);
zeroAddress.sin_family = AF_INET;
// Recover reachability flags
SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress);
SCNetworkReachabilityFlags flags;
BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags);
CFRelease(defaultRouteReachability);
if (!didRetrieveFlags)
{
return NO;
}
BOOL isReachable = flags & kSCNetworkFlagsReachable;
BOOL needsConnection = flags & kSCNetworkFlagsConnectionRequired;
return (isReachable && !needsConnection) ? YES : NO;
}
O primeiro e principal problema que estou tendo é como definir e trabalhar com estruturas C. Na primeira linha ( struct sockaddr_in zeroAddress;
) do código acima, acho que eles estão definindo uma instância chamada zeroAddress
de struct sockaddr_in (?), Presumo. Tentei declarar algo var
assim.
var zeroAddress = sockaddr_in()
Mas recebo o erro Argumento ausente para o parâmetro 'sin_len' na chamada, o que é compreensível porque essa estrutura leva vários argumentos. Então, tentei novamente.
var zeroAddress = sockaddr_in(sin_len: sizeof(zeroAddress), sin_family: AF_INET, sin_port: nil, sin_addr: nil, sin_zero: nil)
Como esperado, recebo alguma outra variável de erro usada em seu próprio valor inicial . Eu também entendo a causa desse erro. Em C, eles declaram a instância primeiro e depois preenchem os parâmetros. Não é possível em Swift, tanto quanto eu sei. Portanto, estou realmente perdido neste ponto sobre o que fazer.
Eu li o documento oficial da Apple sobre a interação com APIs C no Swift, mas não há exemplos de como trabalhar com structs.
Alguém pode me ajudar aqui? Eu realmente aprecio isso.
Obrigado.
ATUALIZAÇÃO: Graças a Martin, consegui superar o problema inicial. Mas ainda assim o Swift não está facilitando as coisas para mim. Estou recebendo vários novos erros.
func connectedToNetwork() -> Bool {
var zeroAddress = sockaddr_in(sin_len: 0, sin_family: 0, sin_port: 0, sin_addr: in_addr(s_addr: 0), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)
var defaultRouteReachability: SCNetworkReachabilityRef = SCNetworkReachabilityCreateWithAddress(UnsafePointer<Void>, UnsafePointer<zeroAddress>) // 'zeroAddress' is not a type
var flags = SCNetworkReachabilityFlags()
let didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, UnsafeMutablePointer<flags>) // 'flags' is not a type
defaultRouteReachability.dealloc(1) // 'SCNetworkReachabilityRef' does not have a member named 'dealloc'
if didRetrieveFlags == false {
return false
}
let isReachable: Bool = flags & kSCNetworkFlagsReachable // Cannot invoke '&' with an argument list of type '(@lvalue UInt32, Int)'
let needsConnection: Bool = flags & kSCNetworkFlagsConnectionRequired // Cannot invoke '&' with an argument list of type '(@lvalue UInt32, Int)'
return (isReachable && !needsConnection) ? true : false
}
EDIT 1: Ok, mudei esta linha para esta,
var defaultRouteReachability: SCNetworkReachabilityRef = SCNetworkReachabilityCreateWithAddress(UnsafePointer<Void>(), &zeroAddress)
O novo erro que estou obtendo nesta linha é 'UnsafePointer' não é conversível para 'CFAllocator' . Como você passaNULL
em Swift?
Também mudei essa linha e o erro desapareceu agora.
let didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags)
EDIT 2: Passei nil
nesta linha depois de ver esta questão. Mas essa resposta contradiz a resposta aqui . Diz que não há equivalente NULL
em Swift.
var defaultRouteReachability: SCNetworkReachabilityRef = SCNetworkReachabilityCreateWithAddress(nil, &zeroAddress)
De qualquer forma, recebo um novo erro dizendo que 'sockaddr_in' não é idêntico a 'sockaddr' na linha acima.
Respostas:
(Esta resposta foi estendida repetidamente devido a mudanças na linguagem Swift, o que a tornou um pouco confusa. Agora eu a reescrevi e removi tudo o que se refere a Swift 1.x. O código mais antigo pode ser encontrado no histórico de edição se alguém precisar isto.)
É assim que você faria no Swift 2.0 (Xcode 7) :
Explicações:
A partir do Swift 1.2 (Xcode 6.3), as estruturas C importadas têm um inicializador padrão em Swift, que inicializa todos os campos da estrutura para zero, de modo que a estrutura de endereço do socket pode ser inicializada com
sizeofValue()
dá o tamanho desta estrutura, isso deve ser convertidoUInt8
parasin_len
:AF_INET
é umInt32
, isso deve ser convertido para o tipo correto parasin_family
:withUnsafePointer(&zeroAddress) { ... }
passa o endereço da estrutura para o fechamento onde é usado como argumento paraSCNetworkReachabilityCreateWithAddress()
. AUnsafePointer($0)
conversão é necessária porque essa função espera um ponteiro parasockaddr
, nãosockaddr_in
.O valor retornado de
withUnsafePointer()
é o valor de retorno deSCNetworkReachabilityCreateWithAddress()
e que possui o tipoSCNetworkReachability?
, ou seja, é opcional. Aguard let
instrução (um novo recurso no Swift 2.0) atribui o valor não empacotado àdefaultRouteReachability
variável se não fornil
. Caso contrário, oelse
bloco é executado e a função retorna.SCNetworkReachabilityCreateWithAddress()
retorna um objeto gerenciado. Você não precisa liberá-lo explicitamente.A partir do Swift 2,
SCNetworkReachabilityFlags
está em conformidade com oOptionSetType
qual possui uma interface semelhante a um conjunto. Você cria uma variável de sinalizadores vazia come verifique se há sinalizadores com
O segundo parâmetro de
SCNetworkReachabilityGetFlags
tem o tipoUnsafeMutablePointer<SCNetworkReachabilityFlags>
, o que significa que você deve passar o endereço da variável sinalizadores.Observe também que registrar um retorno de chamada do notificador é possível a partir do Swift 2, compare Working with C APIs de Swift e Swift 2 - UnsafeMutablePointer <Void> para o objeto .
Atualização para Swift 3/4:
Os ponteiros inseguros não podem ser simplesmente convertidos em um ponteiro de um tipo diferente (consulte - SE-0107 API UnsafeRawPointer ). Aqui está o código atualizado:
fonte
withUnsafePointer(&zeroAddress)
chama o encerramento seguinte{ ...}
com o endereço dezeroAddress
como argumento. Dentro do fechamento,$0
representa esse argumento. - Desculpe, é impossível explicar tudo isso em poucas frases. Dê uma olhada na documentação sobre fechamentos no livro Swift. $ 0 é um "nome de argumento abreviado".true
se o wi-fi não estiver conectado e o 4G estiver ativado, mas o usuário tiver especificado que o aplicativo não pode usar dados do celular. Alguma solução?let cellular = flags.contains(.IsWWAN)
Você pode retornar um toque em vez de um booleano, como:func connectedToNetwork() -> (connected: Bool, cellular: Bool)
Swift 3, IPv4, IPv6
Com base na resposta de Martin R:
fonte
import SystemConfiguration
Isso não tem nada a ver com o Swift, mas a melhor solução é NÃO usar o Reachability para determinar se a rede está online. Basta fazer sua conexão e lidar com os erros se ela falhar. Fazer uma conexão às vezes pode ativar os rádios off-line inativos.
O único uso válido de Reachability é usá-lo para notificá-lo quando uma rede muda de offline para online. Nesse ponto, você deve tentar novamente as conexões com falha.
fonte
NSURLSession
API comNSURLSessionConfiguration.allowsCellularAccess = false
.A melhor solução é usar
ReachabilitySwift
classe , escritaSwift 2
e usaSCNetworkReachabilityRef
.Simples e fácil:
Funcionando como um encanto.
Aproveitar
fonte
SCNetworkReachability
classe em Swift, é uma sugestão de dependência a ser usada para verificar se há uma conexão de rede válida.atualizou a resposta de juanjo para criar uma instância singleton
Uso
fonte
Este é o Swift 4.0
Estou usando esta estrutura https://github.com/ashleymills/Reachability.swift
e instalar o Pod ..
Em AppDelegate
A tela reachabilityViewController aparecerá se não houver internet
fonte