Comment utiliser SCNetworkReachability dans Swift
j'essaie de convertir cet extrait de code en Swift. J'ai du mal à décoller à cause de difficultés.
- (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;
}
la première et la principale question que je me pose est de savoir comment définir et travailler avec des structures C. Dans la première ligne ( struct sockaddr_in zeroAddress;
) du code ci-dessus, je pense qu'ils définissent une instance appelée zeroAddress
à partir de la structure sockaddr_in (?), Je suppose. J'ai essayé de déclarer un var
comme ça.
var zeroAddress = sockaddr_in()
mais j'obtiens l'erreur argument manquant pour le paramètre 'sin_len' dans l'appel qui est compréhensible parce que cette structure prend un certain nombre d'arguments. J'ai donc essayé de nouveau.
var zeroAddress = sockaddr_in(sin_len: sizeof(zeroAddress), sin_family: AF_INET, sin_port: nil, sin_addr: nil, sin_zero: nil)
comme prévu, j'obtiens une autre erreur Variable utilisée dans sa propre valeur initiale . Je comprends aussi la cause de cette erreur. En C, ils déclarent d'abord l'instance puis remplissent les paramètres. Ce n'est pas possible à Swift pour autant que je sache. Donc, je suis vraiment perdu à ce point sur ce qu'il faut faire.
j'ai lu le document officiel D'Apple document sur l'interaction avec les API de Swift, mais il n'a pas d'exemples dans le travail avec les structures.
est-ce que quelqu'un peut m'aider ici? J'ai vraiment l'apprécier.
Merci.
mise à jour: Merci pour Martin, j'ai pu surmonter le problème initial. Mais Swift ne me facilite pas la tâche. Je reçois plusieurs nouvelles erreurs.
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: Okay j'ai changé cette ligne en ceci,
var defaultRouteReachability: SCNetworkReachabilityRef = SCNetworkReachabilityCreateWithAddress(UnsafePointer<Void>(), &zeroAddress)
la nouvelle erreur que j'obtiens à cette ligne est 'UnsafePointer' n'est pas convertible en 'CFAllocator' . Comment passer NULL
à Swift?
aussi j'ai changé cette ligne et l'erreur est partie maintenant.
let didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags)
EDIT 2: j'ai réussi nil
dans cette ligne après avoir vu ce question. Mais cette réponse contredit avec la réponse ici . Il est dit qu'il n'y a pas d'équivalent de NULL
dans Swift.
var defaultRouteReachability: SCNetworkReachabilityRef = SCNetworkReachabilityCreateWithAddress(nil, &zeroAddress)
de toute façon je reçois une nouvelle erreur disant 'sockaddr_in 'n'est pas identique à' sockaddr ' à la ligne ci-dessus.
5 réponses
(cette réponse a été prolongée à plusieurs reprises en raison de changements dans le langage Swift, ce qui l'a rendue un peu confuse. Je l'ai réécrit et j'ai supprimé tout ce qui se réfère à Swift 1.x. Le plus ancien code peut être trouvé dans l'historique des modifications si quelqu'un en a besoin.)
Voici comment vous le feriez dans 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("151900920"))
}) 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)
}
explications:
-
à partir de Swift 1.2 (Xcode 6.3), les structures c importées ont un initialiseur par défaut dans Swift, qui initialise tous les champs de la structure à zéro, de sorte que la structure d'adresse socket peut être initialisée avec
var zeroAddress = sockaddr_in()
-
sizeofValue()
donne la taille de cette structure, ce qui a à convertir enUInt8
poursin_len
:zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
-
AF_INET
est unInt32
, qui doit être converti au type correct poursin_family
:zeroAddress.sin_family = sa_family_t(AF_INET)
-
withUnsafePointer(&zeroAddress) { ... }
passe l'adresse du structure à la fermeture où il est utilisé comme argument pourSCNetworkReachabilityCreateWithAddress()
. LeUnsafePointer(" 1519150920")
la conversion est nécessaire parce que cette fonction attend un pointeur poursockaddr
, passockaddr_in
. -
La valeur retournée par
withUnsafePointer()
est de retour valeur deSCNetworkReachabilityCreateWithAddress()
et qui a le tapezSCNetworkReachability?
, c'est à dire qu'il est facultatif. La déclarationguard let
(une nouvelle fonctionnalité dans Swift 2.0) attribue la valeur non emballée à la variabledefaultRouteReachability
si elle est pasnil
. Sinon le blocelse
est exécuté et la fonction retourner. - à partir de Swift 2,
SCNetworkReachabilityCreateWithAddress()
retourne un objet géré. Vous n'êtes pas obligé de le publier explicitement. -
à partir de Swift 2,
SCNetworkReachabilityFlags
est conforme àOptionSetType
qui a une interface fixe. Vous créez un variable flags vides avecvar flags : SCNetworkReachabilityFlags = []
et vérifier les drapeaux avec
let isReachable = flags.contains(.Reachable) let needsConnection = flags.contains(.ConnectionRequired)
-
le second paramètre de
SCNetworkReachabilityGetFlags
a le typeUnsafeMutablePointer<SCNetworkReachabilityFlags>
, ce qui signifie que vous devez passez le adresse de la variable flags.
noter aussi que l'enregistrement d'un avis de rappel est possible à partir de
Swift 2, comparez travaillant avec les API de Swift et Swift 2 - UnsafeMutablePointer
mise à jour pour Swift 3/4:
les aiguilles dangereuses ne peuvent pas être simplement converties en une aiguille d'un type différent plus (voir- SE-0107 UNSAFERAWPOINTER API ). Voici le code mis à jour:
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, {
"151960920".withMemoryRebound(to: sockaddr.self, capacity: 1) {
SCNetworkReachabilityCreateWithAddress(nil, "151960920")
}
}) 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
D'après la réponse 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, {
"151900920".withMemoryRebound(to: sockaddr.self, capacity: 1) {
SCNetworkReachabilityCreateWithAddress(nil, "151900920")
}
})
}
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, {
"151900920".withMemoryRebound(to: sockaddr.self, capacity: 1) {
SCNetworkReachabilityCreateWithAddress(nil, "151900920")
}
})
}
cela n'a rien à voir avec Swift, mais la meilleure solution est de ne pas utiliser L'accessibilité pour déterminer si le réseau est en ligne. Il suffit de faire votre connexion et de gérer les erreurs si elle échoue. Faire une connexion peut parfois allumer les radios hors ligne dormantes.
la seule utilisation valide de la possibilité d'accès est de l'utiliser pour vous prévenir lorsqu'un réseau passe de hors ligne à en ligne. À ce moment-là, vous devriez réessayer les connexions qui ont échoué.
la meilleure solution est d'utiliser ReachabilitySwift
classe , écrit dans Swift 2
, et utilise SCNetworkReachabilityRef
.
Simple et facile:
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()
fonctionne comme un charme.
Profiter
mise à jour de la réponse de juanjo pour créer une instance singleton
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, {
"151900920".withMemoryRebound(to: sockaddr.self, capacity: 1) {
SCNetworkReachabilityCreateWithAddress(nil, "151900920")
}
})
}
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, {
"151900920".withMemoryRebound(to: sockaddr.self, capacity: 1) {
SCNetworkReachabilityCreateWithAddress(nil, "151900920")
}
})
}
}
Utilisation
if Reachability.shared.isConnectedToNetwork(){
}