webentwicklung-frage-antwort-db.com.de

Verwendung von SCNetworkReachability in Swift

Ich versuche, dieses Codefragment in Swift umzuwandeln. Aufgrund einiger Schwierigkeiten habe ich Probleme, mich auf den Weg zu machen.

- (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 das Definieren und Arbeiten mit C-Strukturen. In der ersten Zeile (struct sockaddr_in zeroAddress;) Des obigen Codes definieren sie vermutlich eine Instanz mit dem Namen zeroAddress aus der Struktur sockaddr_in (?). Ich habe versucht, so ein var zu deklarieren.

var zeroAddress = sockaddr_in()

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

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

Wie erwartet erhalte ich einen anderen Fehler . Die Variable wird innerhalb ihres eigenen Anfangswerts verwendet. Ich verstehe auch die Ursache dieses Fehlers. In C deklarieren sie zuerst die Instanz und füllen dann die Parameter aus. Soweit ich weiß, ist das in Swift nicht möglich. An diesem Punkt habe ich wirklich keine Ahnung, was ich tun soll.

Ich habe Apples offizielles Dokument über die Interaktion mit C-APIs in Swift gelesen, aber es gibt keine Beispiele für die Arbeit mit Strukturen.

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

Vielen Dank.


UPDATE: Dank Martin konnte ich das ursprü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
}

EDIT 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 erhalte, ist 'UnsafePointer' kann nicht in 'CFAllocator' konvertiert werden. Wie übergebe ich NULL in Swift?

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

let didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags)

EDIT 2: Ich habe nil in dieser Zeile übergeben, nachdem ich this question gesehen habe. Aber diese Antwort widerspricht der Antwort hier . Es heißt, dass es in Swift kein Äquivalent zu NULL gibt.

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

Wie auch immer, ich erhalte eine neue Fehlermeldung 'sockaddr_in' ist nicht identisch mit 'sockaddr' in der obigen Zeile.

97
Isuru

(Diese Antwort wurde wiederholt aufgrund von Änderungen in der Swift Sprache, was es etwas verwirrend machte. Ich habe sie jetzt umgeschrieben und alles entfernt, was sich auf Swift 1.x. Der ältere Code kann im Bearbeitungsverlauf gefunden werden, wenn jemand ihn benötigt.)

So würden Sie es in Swift 2.0 (Xcode 7) machen:

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

Erklärungen:

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

    var zeroAddress = sockaddr_in()
    
  • sizeofValue() gibt die Größe dieser Struktur an, diese muss für UInt8 in sin_len konvertiert werden:

    zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
    
  • AF_INET Ist ein Int32, Dieser muss in den richtigen Typ für sin_family Konvertiert werden:

    zeroAddress.sin_family = sa_family_t(AF_INET)
    
  • withUnsafePointer(&zeroAddress) { ... } übergibt die Adresse der Struktur an den Abschluß, wo sie als Argument für SCNetworkReachabilityCreateWithAddress() verwendet wird. Die UnsafePointer($0) -Umwandlung ist erforderlich, da diese Funktion einen Zeiger auf sockaddr erwartet, nicht auf sockaddr_in.

  • Der von withUnsafePointer() zurückgegebene Wert ist der Rückgabewert von SCNetworkReachabilityCreateWithAddress() und hat den Typ SCNetworkReachability?, D. H. Er ist optional. Die Anweisung guard let (Eine neue Funktion in Swift 2.0)) weist der Variablen defaultRouteReachability den nicht umbrochenen Wert zu, wenn dies nicht nil ist. Andernfalls Der Block else 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 entspricht SCNetworkReachabilityFlagsOptionSetType, das eine satzartige Schnittstelle hat. Mit erzeugen Sie eine leere Merkervariable

    var flags : SCNetworkReachabilityFlags = []
    

    und prüfen Sie mit

    let isReachable = flags.contains(.Reachable)
    let needsConnection = flags.contains(.ConnectionRequired)
    
  • Der zweite Parameter von SCNetworkReachabilityGetFlags hat den Typ UnsafeMutablePointer<SCNetworkReachabilityFlags>, Dh Sie müssen die Adresse der Variablen flags übergeben.

Beachten Sie auch, dass das Registrieren eines Notifier-Rückrufs ab Swift 2, vgl. Arbeiten mit C-APIs von Swift und Swift 2 - UnsafeMutablePointer <Void> bis) möglich ist Objekt .


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)
}
232
Martin R

Swift 3, 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)
        }
    })
}
11
juanjo

Dies hat nichts mit Swift zu tun, aber die beste Lösung besteht darin, NICHT die Erreichbarkeit zu verwenden, um zu bestimmen, ob das Netzwerk online ist. Stellen Sie einfach Ihre Verbindung her und behandeln Sie Fehler, wenn dies fehlschlägt. Das Herstellen einer Verbindung kann manchmal die ruhenden Offlineradios auslösen.

Die einzig gültige Verwendung von Reachability besteht darin, Sie zu benachrichtigen, wenn ein Netzwerk von Offline zu Online wechselt. An diesem Punkt sollten Sie fehlgeschlagene Verbindungen wiederholen.

6
EricS

Die beste Lösung ist die Verwendung von ReachabilitySwiftclass , geschrieben in Swift 2 und 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

3
Bonnke

die Antwort von Juanjo wurde 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)
            }
        })
    }
}

Verwendung

if Reachability.shared.isConnectedToNetwork(){

}
1
anoop4real

Dies ist in Swift 4.0

Ich benutze dieses Framework https://github.com/ashleymills/Reachability.Swift
Und Pod installieren ..
In 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")
    }
}
}

Der Bildschirm "reachabilityViewController" wird angezeigt, wenn das Internet nicht vorhanden ist

1
Sreekanth G