network internet check ios c struct swift reachability

ios - internet - reachability swift 4



Cómo usar SCNetworkReachability en Swift (6)

Esto está en Swift 4.0

Estoy usando este marco class
E instalar Pod ..
En AppDelegate

var window: UIWindow? var reachability = InternetReachability()! var reachabilityViewController : UIViewController? = nil func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. reachabilityChecking() return true } extension AppDelegate { func reachabilityChecking() { reachability.whenReachable = { reachability in DispatchQueue.main.async { print("Internet is OK!") if reachability.connection != .none && self.reachabilityViewController != nil { } } } reachability.whenUnreachable = { _ in DispatchQueue.main.async { print("Internet connection FAILED!") let storyboard = UIStoryboard(name: "Reachability", bundle: Bundle.main) self.reachabilityViewController = storyboard.instantiateViewController(withIdentifier: "ReachabilityViewController") let rootVC = self.window?.rootViewController rootVC?.present(self.reachabilityViewController!, animated: true, completion: nil) } } do { try reachability.startNotifier() } catch { print("Could not start notifier") } } }

La pantalla reachabilityViewController aparecerá si Internet no está allí

Estoy tratando de convertir this fragmento de código a Swift. Estoy luchando por despegar debido a algunas dificultades.

- (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; }

El primer y principal problema que estoy teniendo es sobre cómo definir y trabajar con estructuras C. En la primera línea ( struct sockaddr_in zeroAddress; ) del código anterior, creo que están definiendo una instancia llamada zeroAddress de la estructura sockaddr_in (?), Supongo. Traté de declarar una var como esta.

var zeroAddress = sockaddr_in()

Pero recibo el error Falta un argumento para el parámetro ''sin_len'' en la llamada, lo cual es comprensible porque esa estructura toma varios argumentos. Así que lo intenté de nuevo.

var zeroAddress = sockaddr_in(sin_len: sizeof(zeroAddress), sin_family: AF_INET, sin_port: nil, sin_addr: nil, sin_zero: nil)

Como se esperaba, obtengo alguna otra variable de error utilizada dentro de su propio valor inicial . Entiendo la causa de ese error también. En C, declaran primero la instancia y luego completan los parámetros. No es posible en Swift hasta donde yo sé. Así que estoy realmente perdido en este punto sobre qué hacer.

Leí el document oficial de Apple sobre la interacción con las API de C en Swift, pero no tiene ejemplos de cómo trabajar con estructuras.

¿Alguien puede ayudarme aquí? Realmente lo agradecería.

Gracias.

ACTUALIZACIÓN: Gracias a Martin pude superar el problema inicial. Pero Swift no me lo está haciendo más fácil. Recibo múltiples errores nuevos.

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 }

EDITAR 1: Ok, cambié esta línea a esto,

var defaultRouteReachability: SCNetworkReachabilityRef = SCNetworkReachabilityCreateWithAddress(UnsafePointer<Void>(), &zeroAddress)

El nuevo error que obtengo en esta línea es ''UnsafePointer'' no es convertible a ''CFAllocator'' . ¿Cómo pasar NULL en Swift?

También cambié esta línea y el error desapareció ahora.

let didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags)

EDIT 2: Pasé nil en esta línea después de ver this pregunta. Pero esa respuesta contradice con la respuesta here . Dice que no hay equivalente a NULL en Swift.

var defaultRouteReachability: SCNetworkReachabilityRef = SCNetworkReachabilityCreateWithAddress(nil, &zeroAddress)

De todos modos , aparece un nuevo error que dice ''sockaddr_in'' no es idéntico a ''sockaddr'' en la línea anterior.


Esto no tiene nada que ver con Swift, pero la mejor solución es NO usar la Accesibilidad para determinar si la red está en línea. Simplemente haga su conexión y maneje los errores si falla. Hacer una conexión a veces puede encender las radios inactivas inactivas.

El único uso válido de Accesibilidad es usarlo para notificarle cuando una red pasa de fuera de línea a en línea. En ese punto, debe volver a intentar conexiones fallidas.


La mejor solución es usar la class ReachabilitySwift , escrita en Swift 2 , y usa SCNetworkReachabilityRef .

Simple y fácil:

let reachability = Reachability.reachabilityForInternetConnection() reachability?.whenReachable = { reachability in // keep in mind this is called on a background thread // and if you are updating the UI it needs to happen // on the main thread, like this: dispatch_async(dispatch_get_main_queue()) { if reachability.isReachableViaWiFi() { print("Reachable via WiFi") } else { print("Reachable via Cellular") } } } reachability?.whenUnreachable = { reachability in // keep in mind this is called on a background thread // and if you are updating the UI it needs to happen // on the main thread, like this: dispatch_async(dispatch_get_main_queue()) { print("Not reachable") } } reachability?.startNotifier()

Trabajando como un encanto.

Disfrutar


Se actualizó la respuesta de juanjo para crear una instancia única

import Foundation import SystemConfiguration final class Reachability { private init () {} class var shared: Reachability { struct Static { static let instance: Reachability = Reachability() } return Static.instance } func isConnectedToNetwork() -> Bool { guard let flags = getFlags() else { return false } let isReachable = flags.contains(.reachable) let needsConnection = flags.contains(.connectionRequired) return (isReachable && !needsConnection) } private func getFlags() -> SCNetworkReachabilityFlags? { guard let reachability = ipv4Reachability() ?? ipv6Reachability() else { return nil } var flags = SCNetworkReachabilityFlags() if !SCNetworkReachabilityGetFlags(reachability, &flags) { return nil } return flags } private func ipv6Reachability() -> SCNetworkReachability? { var zeroAddress = sockaddr_in6() zeroAddress.sin6_len = UInt8(MemoryLayout<sockaddr_in>.size) zeroAddress.sin6_family = sa_family_t(AF_INET6) return withUnsafePointer(to: &zeroAddress, { $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { SCNetworkReachabilityCreateWithAddress(nil, $0) } }) } private func ipv4Reachability() -> SCNetworkReachability? { var zeroAddress = sockaddr_in() zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size) zeroAddress.sin_family = sa_family_t(AF_INET) return withUnsafePointer(to: &zeroAddress, { $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { SCNetworkReachabilityCreateWithAddress(nil, $0) } }) } }

Uso

if Reachability.shared.isConnectedToNetwork(){ }


(Esta respuesta se extendió repetidamente debido a cambios en el lenguaje Swift, lo que lo hizo un poco confuso. Ahora lo reescribí y eliminé todo lo que se refiere a Swift 1.x. El código anterior se puede encontrar en el historial de edición si alguien necesita eso.)

Así es como lo haría en Swift 2.0 (Xcode 7) :

import SystemConfiguration func connectedToNetwork() -> Bool { var zeroAddress = sockaddr_in() zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress)) zeroAddress.sin_family = sa_family_t(AF_INET) guard let defaultRouteReachability = withUnsafePointer(&zeroAddress, { SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0)) }) else { return false } var flags : SCNetworkReachabilityFlags = [] if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) { return false } let isReachable = flags.contains(.Reachable) let needsConnection = flags.contains(.ConnectionRequired) return (isReachable && !needsConnection) }

Explicaciones:

  • A partir de Swift 1.2 (Xcode 6.3), las estructuras C importadas tienen un inicializador predeterminado en Swift, que inicializa todos los campos de la estructura a cero, por lo que la estructura de la dirección del socket se puede inicializar con

    var zeroAddress = sockaddr_in()

  • sizeofValue() da el tamaño de esta estructura, esto tiene que convertirse a UInt8 para sin_len :

    zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))

  • AF_INET es un Int32 , este tiene que convertirse al tipo correcto para sin_family :

    zeroAddress.sin_family = sa_family_t(AF_INET)

  • withUnsafePointer(&zeroAddress) { ... } pasa la dirección de la estructura al cierre donde se usa como argumento para SCNetworkReachabilityCreateWithAddress() . La UnsafePointer($0) es necesaria porque esa función espera que un puntero sockaddr , no sockaddr_in .

  • El valor devuelto por withUnsafePointer() es el valor SCNetworkReachabilityCreateWithAddress() por SCNetworkReachabilityCreateWithAddress() y que tiene el tipo SCNetworkReachability? , es decir, es opcional. La instrucción guard let (una nueva característica en Swift 2.0) asigna el valor sin envolver a la variable defaultRouteReachability si no es nil . De lo contrario, el bloque else se ejecuta y la función vuelve.

  • A partir de Swift 2, SCNetworkReachabilityCreateWithAddress() devuelve un objeto administrado. No tiene que liberarlo explícitamente.
  • A partir de Swift 2, SCNetworkReachabilityFlags ajusta a OptionSetType que tiene una interfaz similar a un conjunto. Crea una variable de banderas vacía con

    var flags : SCNetworkReachabilityFlags = []

    y busca banderas con

    let isReachable = flags.contains(.Reachable) let needsConnection = flags.contains(.ConnectionRequired)

  • El segundo parámetro de SCNetworkReachabilityGetFlags tiene el tipo UnsafeMutablePointer<SCNetworkReachabilityFlags> , lo que significa que debe pasar la dirección de la variable flags.

Tenga en cuenta también que es posible registrar una devolución de llamada de notificación a partir de Swift 2, compare Trabajar con API C de Swift y Swift 2 - UnsafeMutablePointer <Void> para objetar .

Actualización para Swift 3/4:

Los punteros inseguros ya no se pueden convertir simplemente en un puntero de un tipo diferente (consulte - SE-0107 API UnsafeRawPointer ). Aquí el código actualizado:

import SystemConfiguration func connectedToNetwork() -> Bool { var zeroAddress = sockaddr_in() zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size) zeroAddress.sin_family = sa_family_t(AF_INET) guard let defaultRouteReachability = withUnsafePointer(to: &zeroAddress, { $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { SCNetworkReachabilityCreateWithAddress(nil, $0) } }) else { return false } var flags: SCNetworkReachabilityFlags = [] if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) { return false } let isReachable = flags.contains(.reachable) let needsConnection = flags.contains(.connectionRequired) return (isReachable && !needsConnection) }


Swift 3, IPv4, IPv6

Basado en la respuesta de Martin R:

import SystemConfiguration func isConnectedToNetwork() -> Bool { guard let flags = getFlags() else { return false } let isReachable = flags.contains(.reachable) let needsConnection = flags.contains(.connectionRequired) return (isReachable && !needsConnection) } func getFlags() -> SCNetworkReachabilityFlags? { guard let reachability = ipv4Reachability() ?? ipv6Reachability() else { return nil } var flags = SCNetworkReachabilityFlags() if !SCNetworkReachabilityGetFlags(reachability, &flags) { return nil } return flags } func ipv6Reachability() -> SCNetworkReachability? { var zeroAddress = sockaddr_in6() zeroAddress.sin6_len = UInt8(MemoryLayout<sockaddr_in>.size) zeroAddress.sin6_family = sa_family_t(AF_INET6) return withUnsafePointer(to: &zeroAddress, { $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { SCNetworkReachabilityCreateWithAddress(nil, $0) } }) } func ipv4Reachability() -> SCNetworkReachability? { var zeroAddress = sockaddr_in() zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size) zeroAddress.sin_family = sa_family_t(AF_INET) return withUnsafePointer(to: &zeroAddress, { $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { SCNetworkReachabilityCreateWithAddress(nil, $0) } }) }