J'essaie de convertir ceci extrait de code en Swift. J'ai du mal à décoller en raison de certaines 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;
}
Le premier et le principal problème que je me pose concerne la définition et l’utilisation 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 struct sockaddr_in (?), je suppose. J'ai essayé de déclarer un var
comme celui-ci.
var zeroAddress = sockaddr_in()
Mais j'obtiens l'erreur L'argument manquant pour le paramètre 'sin_len' dans l'appel, ce qui est compréhensible car cette structure prend un certain nombre d'arguments. Alors j'ai essayé à 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 dans Swift autant que je sache. Donc, je suis vraiment perdu à ce stade-ci sur ce qu'il faut faire.
J'ai lu le document officiel document d'Apple sur l'interaction avec les API C dans Swift mais il n'a pas d'exemple de travail avec les structures.
Quelqu'un peut-il m'aider s'il vous plaît ici? Je l'apprécierais vraiment.
Je vous remercie.
UPDATE: Grâce à Martin, j'ai pu surmonter le problème initial. Mais toujours Swift ne me facilite pas la tâche. Je reçois de nombreuses 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: D'accord, j'ai changé cette ligne en ceci,
var defaultRouteReachability: SCNetworkReachabilityRef = SCNetworkReachabilityCreateWithAddress(UnsafePointer<Void>(), &zeroAddress)
La nouvelle erreur que j'obtiens sur cette ligne est 'UnsafePointer' n'est pas convertible en 'CFAllocator'. Comment vous passez NULL
dans Swift?
Aussi j'ai changé cette ligne et l'erreur est parti maintenant.
let didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags)
EDIT 2: J'ai passé nil
dans cette ligne après avoir vu this question. Mais cette réponse est en contradiction avec la réponse ici . Il dit qu'il n'y a pas d'équivalent à NULL
dans Swift.
var defaultRouteReachability: SCNetworkReachabilityRef = SCNetworkReachabilityCreateWithAddress(nil, &zeroAddress)
Quoi qu'il en soit, je reçois une nouvelle erreur disant 'sockaddr_in' n'est pas identique à 'sockaddr' à la ligne ci-dessus.
(Cette réponse a été étendue à plusieurs reprises en raison de changements dans la langue Swift, ce qui rendait la discussion un peu déroutante. Je l’ai maintenant réécrite et j'ai supprimé tout ce qui se réfère à Swift 1.x. L'ancien code peut être trouvé dans l'historique d'édition si quelqu'un en a besoin.)
Voici comment vous procéderiez 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($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)
}
Explications:
À compter 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 de socket peut être initialisée avec
var zeroAddress = sockaddr_in()
sizeofValue()
donne la taille de cette structure, celle-ci doit être convertie en UInt8
pour sin_len
:
zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
AF_INET
Est un Int32
, Il doit être converti dans le type correct pour sin_family
:
zeroAddress.sin_family = sa_family_t(AF_INET)
withUnsafePointer(&zeroAddress) { ... }
passe l'adresse de la structure à la fermeture où il est utilisé comme argument pour SCNetworkReachabilityCreateWithAddress()
. La conversion de UnsafePointer($0)
est nécessaire car cette fonction attend un pointeur sur sockaddr
et non sur sockaddr_in
.
La valeur renvoyée par withUnsafePointer()
est la valeur renvoyée par SCNetworkReachabilityCreateWithAddress()
et qui a pour type SCNetworkReachability?
, C’est-à-dire une valeur facultative. L'instruction guard let
(Une nouvelle fonctionnalité de Swift 2.0) assigne la valeur non enveloppée à la variable defaultRouteReachability
si ce n'est pas nil
. Sinon le bloc else
est exécuté et la fonction retourne.
SCNetworkReachabilityCreateWithAddress()
] renvoie un objet géré. Vous n'avez pas à le publier explicitement.À partir de Swift 2, SCNetworkReachabilityFlags
se conforme à OptionSetType
qui possède une interface de type jeu. Vous créez une variable de drapeau vide avec
var flags : SCNetworkReachabilityFlags = []
et vérifier les drapeaux avec
let isReachable = flags.contains(.Reachable)
let needsConnection = flags.contains(.ConnectionRequired)
Le deuxième paramètre de SCNetworkReachabilityGetFlags
a le type UnsafeMutablePointer<SCNetworkReachabilityFlags>
, Ce qui signifie que vous devez passer le adresse de la variable flags.
Notez également que l’enregistrement d’un rappel par notifiant est possible à partir de Swift 2, comparez tilisation des API C de Swift et Swift 2 - UnsafeMutablePointer <Void> à objet .
Mise à jour pour Swift 3/4:
Les pointeurs non sécurisés ne peuvent plus être simplement convertis en un pointeur d'un type différent (voir - API SE-0107 UnsafeRawPointer ). 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, {
$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
Basé sur 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, {
$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)
}
})
}
Cela n’a rien à voir avec Swift, mais la meilleure solution consiste à NE PAS utiliser Reachability pour déterminer si le réseau est en ligne. Établissez simplement votre connexion et gérez les erreurs en cas d'échec. L'établissement d'une connexion peut parfois déclencher les radios hors ligne en veille.
La seule utilisation valable de Reachability consiste à l’utiliser pour vous avertir lorsqu’un réseau passe de la connexion hors connexion à la connexion en ligne. À ce stade, vous devez réessayer les connexions échouées.
La meilleure solution consiste à utiliser ReachabilitySwift
class , écrit en 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()
Travailler comme un charme.
Prendre plaisir
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, {
$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)
}
})
}
}
Usage
if Reachability.shared.isConnectedToNetwork(){
}
Ceci est dans Swift 4.0
J'utilise ce cadre https://github.com/ashleymills/Reachability.Swift
Et installez le pod ..
Dans 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")
}
}
}
L'écran reachabilityViewController apparaîtra si Internet n'est pas là