So verwenden Sie SCNetworkReachability in Swift

Lesezeit: 11 Minuten

Isurus Benutzeravatar
Isuru

Ich versuche zu konvertieren Dies Codeschnipsel an Swift. Ich habe Probleme, wegen einiger Schwierigkeiten vom Boden abzuheben.

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

Das erste und wichtigste Problem, das ich habe, ist die Definition und Arbeit mit C-Strukturen. In der ersten Zeile (struct sockaddr_in zeroAddress;) des obigen Codes, ich denke, sie definieren eine Instanz namens zeroAddress aus der struct sockaddr_in(?), nehme ich an. Ich habe versucht, a zu deklarieren var so was.

var zeroAddress = sockaddr_in()

Aber ich bekomme den Fehler Fehlendes Argument für Parameter ‘sin_len’ im Aufruf was verständlich ist, weil diese Struktur eine Reihe von Argumenten benötigt. Also versuchte ich es erneut.

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

Wie erwartet bekomme ich einen anderen Fehler Variable, die innerhalb ihres eigenen Anfangswerts verwendet wird. Ich verstehe die Ursache dieses Fehlers auch. In C deklarieren sie zuerst die Instanz und füllen dann die Parameter auf. In Swift ist das meines Wissens nach nicht möglich. Also bin ich an diesem Punkt wirklich verloren, was ich tun soll.

Ich las Apples offizielle dokumentieren zur Interaktion mit C-APIs in Swift, aber es gibt keine Beispiele für die Arbeit mit Strukturen.

Kann mir hier bitte jemand weiterhelfen? Ich würde es wirklich zu schätzen wissen.

Vielen Dank.


AKTUALISIEREN: Dank Martin konnte ich das anfängliche Problem überwinden. Aber Swift macht es mir trotzdem nicht leichter. Ich erhalte mehrere neue Fehler.

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
}

BEARBEITEN 1: Okay, ich habe diese Zeile in diese geändert,

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

Der neue Fehler, den ich in dieser Zeile bekomme, ist „UnsafePointer“ ist nicht in „CFAllocator“ konvertierbar. Wie Sie bestehen NULL auf Swift?

Auch diese Zeile habe ich geändert und der Fehler ist jetzt weg.

let didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags)

BEARBEITEN 2: Ich bestand nil in dieser Zeile, nachdem Sie diese Frage gesehen haben. Aber diese Antwort widerspricht der Antwort hier. Es heißt, es gibt kein Äquivalent zu NULL in Swift.

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

Jedenfalls bekomme ich eine neue Fehlermeldung ‘sockaddr_in’ ist nicht identisch mit ‘sockaddr’ in der oberen Zeile.

  • Ich habe einen Fehler in Zeile if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) dh unärer Operator! kann nicht auf einen Operanden vom Typ Boolean angewendet werden. . . . bitte helfen.

    – Seebok

    18. April 2016 um 9:16 Uhr

Benutzeravatar von Martin R
Martin R

(Diese Antwort wurde aufgrund von Änderungen in der Swift-Sprache wiederholt erweitert, was sie etwas verwirrend machte. Ich habe sie jetzt umgeschrieben und alles entfernt, was sich auf Swift 1.x bezieht. Den älteren Code finden Sie im Bearbeitungsverlauf, falls jemand ihn braucht es.)

So würdest du es machen 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)
}

Erläuterungen:

  • Ab Swift 1.2 (Xcode 6.3) haben importierte C-Strukturen einen Standardinitialisierer in Swift, der alle Felder der Struktur auf Null initialisiert, sodass die Socket-Adressstruktur damit initialisiert werden kann

    var zeroAddress = sockaddr_in()
    
  • sizeofValue() gibt die Größe dieser Struktur an, in die umgerechnet werden muss UInt8 zum sin_len:

    zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
    
  • AF_INET ist ein Int32muss dies in den richtigen Typ für konvertiert werden sin_family:

    zeroAddress.sin_family = sa_family_t(AF_INET)
    
  • withUnsafePointer(&zeroAddress) { ... } übergibt die Adresse der Struktur an die Closure, wo sie als Argument für verwendet wird
    SCNetworkReachabilityCreateWithAddress(). Das UnsafePointer($0)
    Konvertierung ist erforderlich, da diese Funktion einen Zeiger auf erwartet
    sockaddrnicht sockaddr_in.

  • Der Wert, der von zurückgegeben wird withUnsafePointer() ist der Rückgabewert von SCNetworkReachabilityCreateWithAddress() und das hat den typ SCNetworkReachability?, dh es ist optional. Das guard let -Anweisung (eine neue Funktion in Swift 2.0) weist den ausgepackten Wert der zu defaultRouteReachability variabel, wenn nicht nil. Ansonsten der else Block wird ausgeführt und die Funktion kehrt zurück.

  • Ab Swift 2, SCNetworkReachabilityCreateWithAddress() gibt ein verwaltetes Objekt zurück. Sie müssen es nicht explizit freigeben.
  • Ab Swift 2, SCNetworkReachabilityFlags entspricht
    OptionSetType die eine mengenähnliche Schnittstelle hat. Sie erstellen eine leere flags-Variable mit

    var flags : SCNetworkReachabilityFlags = []
    

    und nach Flags suchen mit

    let isReachable = flags.contains(.Reachable)
    let needsConnection = flags.contains(.ConnectionRequired)
    
  • Der zweite Parameter von SCNetworkReachabilityGetFlags hat den Typ
    UnsafeMutablePointer<SCNetworkReachabilityFlags>was bedeutet, dass Sie die passieren müssen die Anschrift der flags-Variablen.

Beachten Sie auch, dass das Registrieren eines Notifier-Callbacks ab Swift 2 möglich ist, vergleiche Arbeiten mit C-APIs von Swift und Swift 2 – UnsafeMutablePointer to object.


Update für Swift 3/4:

Unsichere Zeiger können nicht mehr einfach in einen Zeiger eines anderen Typs konvertiert werden (siehe – SE-0107 UnsafeRawPointer-API). Hier der aktualisierte Code:

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)
}

  • @Isuru: UnsafePointer ist das Swift-Äquivalent eines C-Zeigers. withUnsafePointer(&zeroAddress) ruft die folgende Schließung auf { ...} mit der Adresse von zeroAddress als argument. Innerhalb des Verschlusses, $0 steht für dieses Argument. – Entschuldigung, es ist unmöglich, das alles in ein paar Sätzen zu erklären. Sehen Sie sich die Dokumentation über Closures im Swift-Buch an. $0 ist ein “Kurzform-Argumentname”.

    –Martin R

    2. September 2014 um 19:14 Uhr

  • @JAL: Sie haben Recht, Apple hat geändert, wie ein “Boolean” Swift zugeordnet wird. Vielen Dank für Ihr Feedback, ich werde die Antwort entsprechend aktualisieren.

    –Martin R

    7. August 2015 um 19:21 Uhr

  • Dies kehrt zurück true wenn WLAN nicht verbunden und 4G eingeschaltet ist, aber der Benutzer angegeben hat, dass die App keine Mobilfunkdaten verwenden darf. Irgendwelche Lösungen?

    – Max Chuquimia

    13. August 2015 um 6:36 Uhr


  • @Jugale: Sie könnten so etwas tun: let cellular = flags.contains(.IsWWAN) Sie können anstelle eines booleschen Werts auch ein Touple zurückgeben, wie zum Beispiel: func connectedToNetwork() -> (connected: Bool, cellular: Bool)

    – EdFunke

    25. November 2015 um 9:17 Uhr


  • @Tejas: Sie können anstelle der “Nulladresse” eine beliebige IP-Adresse verwenden oder SCNetworkReachabilityCreateWithName() mit einem Hostnamen als Zeichenfolge verwenden. Beachten Sie jedoch, dass SCNetworkReachability nur prüft, ob ein an diese Adresse gesendetes Paket das lokale Gerät verlassen kann. Es garantiert nicht, dass das Datenpaket tatsächlich vom Host empfangen wird.

    –Martin R

    27. Juni 2017 um 8:23 Uhr

Benutzeravatar von juanjo
Juanjo

Swift3, IPv4, IPv6

Basierend auf der Antwort von 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)
        }
    })
}

  • Funktioniert für mich auch am besten für NET64/IPV6, nicht vergessen import SystemConfiguration

    – Bhavin_m

    16. Dezember 2016 um 11:34 Uhr


  • @juanjo, wie legst du einen Host fest, den du mit deinem Code erreichen möchtest

    – Benutzer2924482

    23. Dezember 2019 um 19:36 Uhr

Benutzeravatar von Mithra Singam
Mithra Singam

Swift 5, mit NWPathMonitor

import Network

func configureNetworkMonitor(){
        let monitor = NWPathMonitor()
        
        monitor.pathUpdateHandler = { path in
            
            if path.status != .satisfied {
                print("not connected")
            }
            else if path.usesInterfaceType(.cellular) {
                print("Cellular")
            }
            else if path.usesInterfaceType(.wifi) {
                print("WIFI")
            }
            else if path.usesInterfaceType(.wiredEthernet) {
                print("Ethernet")
            }
            else if path.usesInterfaceType(.other){
                print("Other")
            }else if path.usesInterfaceType(.loopback){
                print("Loop Back")
            }
        }
        
        monitor.start(queue: DispatchQueue.global(qos: .background))
    }

  • Dafür braucht es mehr Upvotes. Dies ist viel sauberer und benutzerfreundlicher als das SystemConfiguration-Framework.

    – Xaxxus

    28. Januar 2021 um 14:24 Uhr

  • Einverstanden. Funktioniert in xCode 13.3 und Swift 5

    – Jason

    22. Mai um 18:32 Uhr

Dies hat nichts mit Swift zu tun, aber die beste Lösung besteht darin, Reachability NICHT zu verwenden, um festzustellen, ob das Netzwerk online ist. Stellen Sie einfach Ihre Verbindung her und behandeln Sie Fehler, wenn sie fehlschlägt. Das Herstellen einer Verbindung kann manchmal die inaktiven Offline-Funkgeräte starten.

Die einzig gültige Verwendung von Erreichbarkeit besteht darin, Sie zu benachrichtigen, wenn ein Netzwerk von offline zu online wechselt. An diesem Punkt sollten Sie fehlgeschlagene Verbindungen erneut versuchen.

Bonnkes Benutzeravatar
Bonke

Die beste Lösung ist die Verwendung ReachabilitySwift Klassegeschrieben in Swift 2und verwendet SCNetworkReachabilityRef.

Simpel und einfach:

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()

Arbeiten wie ein Zauber.

Genießen

  • Ich bevorzuge die akzeptierte Antwort, da keine Abhängigkeiten von Drittanbietern integriert werden müssen. Außerdem beantwortet dies nicht die Frage nach der Verwendung SCNetworkReachability Klasse in Swift ist es ein Vorschlag für eine Abhängigkeit, die verwendet werden kann, um nach einer gültigen Netzwerkverbindung zu suchen.

    – JAL

    15. Oktober 2015 um 15:34 Uhr


Benutzeravatar von anoop4real
anoop4real

die Antwort von Juanjo aktualisiert, um eine Singleton-Instanz zu erstellen

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)
            }
        })
    }
}

Verwendungszweck

if Reachability.shared.isConnectedToNetwork(){

}

  • Ich bevorzuge die akzeptierte Antwort, da keine Abhängigkeiten von Drittanbietern integriert werden müssen. Außerdem beantwortet dies nicht die Frage nach der Verwendung SCNetworkReachability Klasse in Swift ist es ein Vorschlag für eine Abhängigkeit, die verwendet werden kann, um nach einer gültigen Netzwerkverbindung zu suchen.

    – JAL

    15. Oktober 2015 um 15:34 Uhr


Dies ist in Swift 4.0

Ich verwende diesen Rahmen https://github.com/ashleymills/Reachability.swift

Und Pod installieren ..
Im App-Delegierter

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")
    }
}
}

Der Bildschirm „reachabilityViewController“ wird angezeigt, wenn kein Internet vorhanden ist

1421260cookie-checkSo verwenden Sie SCNetworkReachability in Swift

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy