Sprawdź dostępność połączenia internetowego w Swift

107

Czy istnieje sposób sprawdzenia, czy połączenie internetowe jest dostępne za pomocą Swift?

Wiem, że jest wiele bibliotek zewnętrznych, które to robią, ale wszystkie są napisane w Objective-C. Szukam szybkiej alternatywy.

Isuru
źródło
5
Jedną z ogromnych zalet Swift jest to, że dobrze integruje się z Objective-C (i takimi bibliotekami).
user2864740
W rzeczy samej. Jakie masz problemy z używaniem jednej z istniejących bibliotek? Korzystanie z biblioteki Objective C od Swift jest dość proste, a napisanie jakiejkolwiek aplikacji w języku Swift będzie niezwykle trudne, jeśli nie możesz tego zrobić.
Matt Gibson,
1
@MattGibson prawie wszystko, co możesz zrobić w ObjC, można zrobić w Swift ze względną łatwością. To prawda, w tym przypadku byłoby kilka dodatkowych linii, ale nadal dalekich od „ekstremalnie trudnych”
Byron Coetsee,
@ByronCoetsee Używałem bibliotek, takich jak AppKit itp. - chodzi mi o to, że musisz wiedzieć, jak współdziałać z bibliotekami Objective C, aby napisać cokolwiek pożytecznego w Swift.
Matt Gibson,
Zobacz odpowiedź alamofire - stackoverflow.com/a/46562290/7576100
Jack

Odpowiedzi:

228

Jak wspomniano w komentarzach, chociaż możliwe jest użycie bibliotek Objective-C w Swift, chciałem bardziej czystego rozwiązania Swift. Istniejąca klasa Apple Reachability i inne biblioteki innych firm wydawały mi się zbyt skomplikowane, abym przetłumaczył ją na Swift. Poszukałem więcej w Google i natknąłem się na ten artykuł, który pokazuje prostą metodę sprawdzania dostępności sieci. Postanowiłem przetłumaczyć to na Swift. Uderzyłem w wiele przeszkód, ale dzięki Martinowi R ze StackOverflow udało mi się je rozwiązać i wreszcie uzyskać działające rozwiązanie w Swift. Oto kod.

import Foundation
import SystemConfiguration

public class Reachability {

    class func isConnectedToNetwork() -> 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)

        let defaultRouteReachability = withUnsafePointer(&zeroAddress) {
            SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0)).takeRetainedValue()
        }

        var flags: SCNetworkReachabilityFlags = 0
        if SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) == 0 {
            return false
        }

        let isReachable = (flags & UInt32(kSCNetworkFlagsReachable)) != 0
        let needsConnection = (flags & UInt32(kSCNetworkFlagsConnectionRequired)) != 0

        return isReachable && !needsConnection
    }

}

Dla Swift> 3.0

public class Reachability {
    public func isConnectedToNetwork() -> 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
        }
        if flags.isEmpty {
            return false
        }

        let isReachable = flags.contains(.reachable)
        let needsConnection = flags.contains(.connectionRequired)

        return (isReachable && !needsConnection)
    }
}

Działa to zarówno w przypadku połączeń 3G, jak i Wi-Fi. Wrzuciłem go również na mój GitHub z działającym przykładem.

Isuru
źródło
7
Dzięki za szybką odpowiedź (gra słów nie zamierzona). MIT lub Apache byłyby idealne - dzięki!
Andrew Ebling
4
Gotowe. Zmieniono to na MIT.
Isuru
11
Znalazłem inny problem, jeśli 3G jest włączone, ale nie mam więcej danych, isConnectedToNetworkzwraca prawdę, ale nie mogę zadzwonić do mojej usługi internetowej
János
11
Jak powiedział @Isuru, jest to oparte na stackoverflow.com/a/25623647/1187415 , które zostało teraz zaktualizowane dla Swift 2.
Martin R
2
@ János: Ustawienie powiadomienia zwrotnego jest teraz możliwe dzięki Swift 2, zobacz zaktualizowaną odpowiedź stackoverflow.com/a/27142665/1187415 .
Martin R
16

Dam ci lepszy sposób ...

Musisz utworzyć klasę z tym kodem

 import Foundation
 public class Reachability {

class func isConnectedToNetwork()->Bool{

    var Status:Bool = false
    let url = NSURL(string: "http://google.com/")
    let request = NSMutableURLRequest(URL: url!)
    request.HTTPMethod = "HEAD"
    request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData
    request.timeoutInterval = 10.0

    var response: NSURLResponse?

    var data = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: nil) as NSData?

    if let httpResponse = response as? NSHTTPURLResponse {
        if httpResponse.statusCode == 200 {
            Status = true
        }
    }

    return Status
  }
}

Następnie możesz sprawdzić połączenie internetowe w dowolnym miejscu projektu za pomocą tego kodu:

if Reachability.isConnectedToNetwork() == true {
     println("Internet connection OK")
} else {
     println("Internet connection FAILED")
}

Bardzo łatwe!

* Ten sposób jest oparty na odpowiedzi Vikram Pote!

Dmitrij
źródło
15
Pamiętaj, że nie powinieneś używać tej metody zamiast tej używanej powyżej. W aplikacjach, które wymagają stałej łączności, sprawdzenie odpowiedzi, takie jak ta metoda, spowoduje zawieszenie aplikacji w sytuacjach, w których łączność internetowa jest słaba (np. Na Edge / GPRS). Nie używaj tego rozwiązania !!
Imran Ahmed
7
Zły sposób 1) Potrzebujesz dodatkowego trafienia na serwer w każdym czasie 2) google.com również może nie działać
Vijay Singh Rana
2
1. Tak, w ten sposób za każdym razem potrzebne jest dodatkowe trafienie do serwera, ale daje to 100% gwarancję, że Internet jest dostępny ... zdarza się, że urządzenie jest podłączone do sieci Wi-Fi, ale nie zapewnia dostępu do Internetu! W tej sytuacji w ten sposób determinuje brak dostępu do internetu… inne sposoby - nie! 2. Możesz użyć własnego serwera zamiast google.com ... To było pierwotnie przeznaczone ...
Dmitry
1
słabe rozwiązanie, zabójca połączenia.
gokhanakkurt
2
gokhanakkurt, proszę, zasugeruj inne rozwiązanie, które zapewni, że internet będzie działał w 100%
Dmitry
15

Dla Swift 3.1 (iOS 10.1)

Jeśli chcesz dokonać rozróżnienia między typem sieci (np. Wi-Fi lub WWAN):

Możesz użyć:

func checkWiFi() -> Bool {

    let networkStatus = Reachability().connectionStatus()
    switch networkStatus {
    case .Unknown, .Offline:
        return false
    case .Online(.WWAN):
        print("Connected via WWAN")
        return true
    case .Online(.WiFi):
        print("Connected via WiFi")
        return true
    }
}

Oto cała klasa osiągalności, która rozróżnia typy sieci:

import Foundation
import SystemConfiguration

import UIKit
import SystemConfiguration.CaptiveNetwork

public let ReachabilityStatusChangedNotification = "ReachabilityStatusChangedNotification"

public enum ReachabilityType: CustomStringConvertible {
    case WWAN
    case WiFi

    public var description: String {
        switch self {
        case .WWAN: return "WWAN"
        case .WiFi: return "WiFi"
        }
    }
}

public enum ReachabilityStatus: CustomStringConvertible  {
    case Offline
    case Online(ReachabilityType)
    case Unknown

    public var description: String {
        switch self {
        case .Offline: return "Offline"
        case .Online(let type): return "Online (\(type))"
        case .Unknown: return "Unknown"
        }
    }
}

public class Reachability {

    func connectionStatus() -> ReachabilityStatus {
        var zeroAddress = sockaddr_in()
        zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
        zeroAddress.sin_family = sa_family_t(AF_INET)

        guard let defaultRouteReachability = (withUnsafePointer(to: &zeroAddress) {
            $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { zeroSockAddress in
                SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress)
            }
        }) else {
           return .Unknown
        }

        var flags : SCNetworkReachabilityFlags = []
        if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) {
            return .Unknown
        }

        return ReachabilityStatus(reachabilityFlags: flags)
    }

    func monitorReachabilityChanges() {
        let host = "google.com"
        var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
        let reachability = SCNetworkReachabilityCreateWithName(nil, host)!

        SCNetworkReachabilitySetCallback(reachability, { (_, flags, _) in
            let status = ReachabilityStatus(reachabilityFlags: flags)

            NotificationCenter.default.post(name: NSNotification.Name(rawValue: ReachabilityStatusChangedNotification), object: nil, userInfo: ["Status": status.description])}, &context)

        SCNetworkReachabilityScheduleWithRunLoop(reachability, CFRunLoopGetMain(), CFRunLoopMode.commonModes.rawValue)
    }
}

extension ReachabilityStatus {

    public init(reachabilityFlags flags: SCNetworkReachabilityFlags) {
        let connectionRequired = flags.contains(.connectionRequired)
        let isReachable = flags.contains(.reachable)
        let isWWAN = flags.contains(.isWWAN)

        if !connectionRequired && isReachable {
            if isWWAN {
                self = .Online(.WWAN)
            } else {
                self = .Online(.WiFi)
            }
        } else {
            self =  .Offline
        }
    }
}
iKK
źródło
działa dobrze od 3 grudnia 2016 r., iOS 10 i swift 3.1, dzięki!
joey
Witam, jeśli chcemy rozróżnić połączenie Wi-Fi / 3G / Mobile-Data / 4G, abyśmy mogli zidentyfikować.
6

Ponieważ sendSynchronousRequest jest przestarzałe, próbowałem tego, ale przed zakończeniem odpowiedzi został wywołany komunikat „Return Status”.

Ta odpowiedź działa jednak dobrze: Sprawdź połączenie internetowe za pomocą Swift

Oto, czego mimo wszystko próbowałem:

import Foundation

public class Reachability {

    class func isConnectedToNetwork()->Bool{

        var Status:Bool = false
        let url = NSURL(string: "http://google.com/")
        let request = NSMutableURLRequest(URL: url!)
        request.HTTPMethod = "HEAD"
        request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData
        request.timeoutInterval = 10.0
        let session = NSURLSession.sharedSession()

        session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
            print("data \(data)")
            print("response \(response)")
            print("error \(error)")

            if let httpResponse = response as? NSHTTPURLResponse {
                print("httpResponse.statusCode \(httpResponse.statusCode)")
                if httpResponse.statusCode == 200 {
                    Status = true
                }
            }

        }).resume()


        return Status
    }
}
Sarah
źródło
Podobało mi się i skorzystałem z twojego rozwiązania. Ale dodałem połączenie tego z tą odpowiedzią: stackoverflow.com/a/34591379 aka. dodałem semafor ... Więc czekam na zakończenie zadania.
Bjqn
6

SWIFT 3: Sprawdza połączenie Wi - Fi i Internet :

import Foundation
import SystemConfiguration

public class Reachability {
    public func isConnectedToNetwork() -> 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)
    }
}

STOSOWANIE:

if Reachability.isConnectedToNetwork() == true {
    print("Connected to the internet")
    //  Do something
} else {
    print("No internet connection")
    //  Do something
}
Gilad Brunfman
źródło
2
public func isConnectedToNetwork() {...}należy zmienić na class func isConnectedToNetwork{...}dla swojego przypadku użycia.
keverly
4

Możesz również użyć poniższej odpowiedzi.

    func checkInternet(flag:Bool, completionHandler:(internet:Bool) -> Void)
    {
      UIApplication.sharedApplication().networkActivityIndicatorVisible = true

      let url = NSURL(string: "http://www.google.com/")
      let request = NSMutableURLRequest(URL: url!)

      request.HTTPMethod = "HEAD"
      request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData
      request.timeoutInterval = 10.0

      NSURLConnection.sendAsynchronousRequest(request, queue:NSOperationQueue.mainQueue(), completionHandler:
      {(response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in

        UIApplication.sharedApplication().networkActivityIndicatorVisible = false

        let rsp = response as NSHTTPURLResponse?

        completionHandler(internet:rsp?.statusCode == 200)
    })
    }

     func yourMethod()
    {
    self.checkInternet(false, completionHandler:
    {(internet:Bool) -> Void in

        if (internet)
        {
            // "Internet" mean Google
        }
        else
        {
            // No "Internet" no Google
        }
    })
   }
Vikram Pote
źródło
Dzięki! Otrzymałem automatyczną korektę z odpowiedzi jako NSHTTPURLResponse? odpowiedzieć jako! NSHTTPURLResponse? w Swift 1.2.
Francis Jervis,
1

SWIFT 3: Sprawdź połączenie 3G i Wi-Fi

DispatchQueue.main.async {
        let url = URL(string: "https://www.google.com")!
        let request = URLRequest(url: url)

        let task = URLSession.shared.dataTask(with: request) {data, response, error in

            if error != nil {
                // do something here...
                print("Internet Connection not Available!")
            }
            else if let httpResponse = response as? HTTPURLResponse {
                if httpResponse.statusCode == 200 {
                    // do something here...
                    print("Internet Connection OK")
                }
                print("statusCode: \(httpResponse.statusCode)")
            }

        }
        task.resume()
}
Włodzimierz Woźniak
źródło
To nie jest preferowany sposób. Co się stanie, jeśli w przyszłości podany link przestanie odpowiadać lub zostanie wyłączony? Poleciłbym do tego użyć frameworka Apple SystemConfiguration. Zobacz powyższą odpowiedź.
Abdul Yasin
1

Dla Swift 5:

import Network
let monitor = NWPathMonitor()

func checkInterwebs() -> Bool {
    var status = false
    monitor.pathUpdateHandler = { path in
        if path.status == .satisfied {
            status = true  // online
        }
    }
    return status
}
RandallShanePhD
źródło
1

Szybki 4

if isInternetAvailable() {
    print("if called Internet Connectivity success \(isInternetAvailable())");
} else {
    print("else called Internet Connectivity success \(isInternetAvailable())");
}

func isInternetAvailable() -> Bool {
    var zeroAddress = sockaddr_in()
    zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
    zeroAddress.sin_family = sa_family_t(AF_INET)
    let defaultRouteReachability = withUnsafePointer(to: &zeroAddress) {
        $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {zeroSockAddress in
            SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress)
     }
    }

   var flags = SCNetworkReachabilityFlags()

   if !SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) {
      return false
   }
   let isReachable = flags.contains(.reachable)
   let needsConnection = flags.contains(.connectionRequired)
   //   print(isReachable && !needsConnection)
   return (isReachable && !needsConnection)
}
Keshav Gera
źródło