ตรวจสอบการเชื่อมต่ออินเทอร์เน็ตด้วย Swift


251

เมื่อฉันลองตรวจสอบการเชื่อมต่ออินเทอร์เน็ตบน iPhone ของฉันฉันจะได้รับข้อผิดพลาดมากมาย ใครสามารถช่วยฉันแก้ไขปัญหานี้ได้บ้าง

รหัส:

import Foundation
import SystemConfiguration

public class Reachability {

class func isConnectedToNetwork() -> Bool {

    var zeroAddress = sockaddr_in()
    zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
    zeroAddress.sin_family = sa_family_t(AF_INET)

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

    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) ? true : false
}

}

ข้อผิดพลาดกับรหัส:

ข้อผิดพลาด

หากไม่สามารถอ่านได้ข้อผิดพลาด 1 บอกว่า:

'Int' ไม่สามารถแปลงเป็น 'SCNetworkReachabilityFlags'

ข้อผิดพลาด 2 & 3:

ไม่พบโอเวอร์โหลดสำหรับ 'init' ที่ยอมรับอาร์กิวเมนต์ที่ให้มา


1
รหัสดูเหมือนกับสิ่งที่ฉันแนะนำที่นี่: stackoverflow.com/a/25623647/1187415ดังนั้นฉันจึงอัปเดตรหัสหนึ่งสำหรับ Swift 2
Martin R

2
ฉันมีการติดตั้งใช้งานการเข้าถึง Swift 2 ที่ทำงานได้อย่างสมบูรณ์ที่นี่ ... github.com/ashleymills/Reachability.swift - โปรดใช้เป็นข้อมูลอ้างอิง
Ashley Mills

1
stackoverflow.com/a/25623647/1187415อัปเดตตอนนี้สำหรับ Swift 3, Xcode 8 beta 6
Martin R

1
แอปเปิ้ลให้เป็นตัวอย่างอย่างเป็นทางการสวิฟท์และคุณสามารถได้รับมันใน CocoaPods มันเข้ากันได้กับโครงการ Swift ใด ๆ
Cœur

คำตอบ:


368

เพื่อแก้ปัญหา 4G ที่กล่าวถึงในความคิดเห็นที่ฉันได้ใช้ @AshleyMills การใช้งานการเข้าถึงได้เป็นข้อมูลอ้างอิงและเขียนใหม่การเข้าถึงได้สำหรับ Swift 3.1:

อัปเดต: Xcode 10.1 • Swift 4 หรือใหม่กว่า


ไฟล์ Reachability.swift

import Foundation
import SystemConfiguration

class Reachability {
    var hostname: String?
    var isRunning = false
    var isReachableOnWWAN: Bool
    var reachability: SCNetworkReachability?
    var reachabilityFlags = SCNetworkReachabilityFlags()
    let reachabilitySerialQueue = DispatchQueue(label: "ReachabilityQueue")
    init(hostname: String) throws {
        guard let reachability = SCNetworkReachabilityCreateWithName(nil, hostname) else {
            throw Network.Error.failedToCreateWith(hostname)
        }
        self.reachability = reachability
        self.hostname = hostname
        isReachableOnWWAN = true
        try start()
    }
    init() throws {
        var zeroAddress = sockaddr_in()
        zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
        zeroAddress.sin_family = sa_family_t(AF_INET)
        guard let reachability = withUnsafePointer(to: &zeroAddress, {
            $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
                SCNetworkReachabilityCreateWithAddress(nil, $0)
            }
        }) else {
            throw Network.Error.failedToInitializeWith(zeroAddress)
        }
        self.reachability = reachability
        isReachableOnWWAN = true
        try start()
    }
    var status: Network.Status {
        return  !isConnectedToNetwork ? .unreachable :
                isReachableViaWiFi    ? .wifi :
                isRunningOnDevice     ? .wwan : .unreachable
    }
    var isRunningOnDevice: Bool = {
        #if targetEnvironment(simulator)
            return false
        #else
            return true
        #endif
    }()
    deinit { stop() }
}

extension Reachability {

    func start() throws {
        guard let reachability = reachability, !isRunning else { return }
        var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
        context.info = Unmanaged<Reachability>.passUnretained(self).toOpaque()
        guard SCNetworkReachabilitySetCallback(reachability, callout, &context) else { stop()
            throw Network.Error.failedToSetCallout
        }
        guard SCNetworkReachabilitySetDispatchQueue(reachability, reachabilitySerialQueue) else { stop()
            throw Network.Error.failedToSetDispatchQueue
        }
        reachabilitySerialQueue.async { self.flagsChanged() }
        isRunning = true
    }

    func stop() {
        defer { isRunning = false }
        guard let reachability = reachability else { return }
        SCNetworkReachabilitySetCallback(reachability, nil, nil)
        SCNetworkReachabilitySetDispatchQueue(reachability, nil)
        self.reachability = nil
    }

    var isConnectedToNetwork: Bool {
        return isReachable &&
               !isConnectionRequiredAndTransientConnection &&
               !(isRunningOnDevice && isWWAN && !isReachableOnWWAN)
    }

    var isReachableViaWiFi: Bool {
        return isReachable && isRunningOnDevice && !isWWAN
    }

    /// Flags that indicate the reachability of a network node name or address, including whether a connection is required, and whether some user intervention might be required when establishing a connection.
    var flags: SCNetworkReachabilityFlags? {
        guard let reachability = reachability else { return nil }
        var flags = SCNetworkReachabilityFlags()
        return withUnsafeMutablePointer(to: &flags) {
            SCNetworkReachabilityGetFlags(reachability, UnsafeMutablePointer($0))
            } ? flags : nil
    }

    /// compares the current flags with the previous flags and if changed posts a flagsChanged notification
    func flagsChanged() {
        guard let flags = flags, flags != reachabilityFlags else { return }
        reachabilityFlags = flags
        NotificationCenter.default.post(name: .flagsChanged, object: self)
    }

    /// The specified node name or address can be reached via a transient connection, such as PPP.
    var transientConnection: Bool { return flags?.contains(.transientConnection) == true }

    /// The specified node name or address can be reached using the current network configuration.
    var isReachable: Bool { return flags?.contains(.reachable) == true }

    /// The specified node name or address can be reached using the current network configuration, but a connection must first be established. If this flag is set, the kSCNetworkReachabilityFlagsConnectionOnTraffic flag, kSCNetworkReachabilityFlagsConnectionOnDemand flag, or kSCNetworkReachabilityFlagsIsWWAN flag is also typically set to indicate the type of connection required. If the user must manually make the connection, the kSCNetworkReachabilityFlagsInterventionRequired flag is also set.
    var connectionRequired: Bool { return flags?.contains(.connectionRequired) == true }

    /// The specified node name or address can be reached using the current network configuration, but a connection must first be established. Any traffic directed to the specified name or address will initiate the connection.
    var connectionOnTraffic: Bool { return flags?.contains(.connectionOnTraffic) == true }

    /// The specified node name or address can be reached using the current network configuration, but a connection must first be established.
    var interventionRequired: Bool { return flags?.contains(.interventionRequired) == true }

    /// The specified node name or address can be reached using the current network configuration, but a connection must first be established. The connection will be established "On Demand" by the CFSocketStream programming interface (see CFStream Socket Additions for information on this). Other functions will not establish the connection.
    var connectionOnDemand: Bool { return flags?.contains(.connectionOnDemand) == true }

    /// The specified node name or address is one that is associated with a network interface on the current system.
    var isLocalAddress: Bool { return flags?.contains(.isLocalAddress) == true }

    /// Network traffic to the specified node name or address will not go through a gateway, but is routed directly to one of the interfaces in the system.
    var isDirect: Bool { return flags?.contains(.isDirect) == true }

    /// The specified node name or address can be reached via a cellular connection, such as EDGE or GPRS.
    var isWWAN: Bool { return flags?.contains(.isWWAN) == true }

    /// The specified node name or address can be reached using the current network configuration, but a connection must first be established. If this flag is set
    /// The specified node name or address can be reached via a transient connection, such as PPP.
    var isConnectionRequiredAndTransientConnection: Bool {
        return (flags?.intersection([.connectionRequired, .transientConnection]) == [.connectionRequired, .transientConnection]) == true
    }
}

func callout(reachability: SCNetworkReachability, flags: SCNetworkReachabilityFlags, info: UnsafeMutableRawPointer?) {
    guard let info = info else { return }
    DispatchQueue.main.async {
        Unmanaged<Reachability>
            .fromOpaque(info)
            .takeUnretainedValue()
            .flagsChanged()
    }
}

extension Notification.Name {
    static let flagsChanged = Notification.Name("FlagsChanged")
}

struct Network {
    static var reachability: Reachability!
    enum Status: String {
        case unreachable, wifi, wwan
    }
    enum Error: Swift.Error {
        case failedToSetCallout
        case failedToSetDispatchQueue
        case failedToCreateWith(String)
        case failedToInitializeWith(sockaddr_in)
    }
}

การใช้

เริ่มต้นมันใน AppDelegate.swift ของคุณได้ทำวิธีการFinishLaunchingWithOptionsและจัดการข้อผิดพลาดที่อาจเกิดขึ้น:

import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        do {
            try Network.reachability = Reachability(hostname: "www.google.com")
        }
        catch {
            switch error as? Network.Error {
            case let .failedToCreateWith(hostname)?:
                print("Network error:\nFailed to create reachability object With host named:", hostname)
            case let .failedToInitializeWith(address)?:
                print("Network error:\nFailed to initialize reachability object With address:", address)
            case .failedToSetCallout?:
                print("Network error:\nFailed to set callout")
            case .failedToSetDispatchQueue?:
                print("Network error:\nFailed to set DispatchQueue")
            case .none:
                print(error)
            }
        }
        return true
    }
}

และตัวอย่างตัวควบคุมมุมมอง:

import UIKit
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default
            .addObserver(self,
                         selector: #selector(statusManager),
                         name: .flagsChanged,
                         object: nil)
        updateUserInterface()
    }
    func updateUserInterface() {
        switch Network.reachability.status {
        case .unreachable:
            view.backgroundColor = .red
        case .wwan:
            view.backgroundColor = .yellow
        case .wifi:
            view.backgroundColor = .green
        }
        print("Reachability Summary")
        print("Status:", Network.reachability.status)
        print("HostName:", Network.reachability.hostname ?? "nil")
        print("Reachable:", Network.reachability.isReachable)
        print("Wifi:", Network.reachability.isReachableViaWiFi)
    }
    @objc func statusManager(_ notification: Notification) {
        updateUserInterface()
    }
}

ตัวอย่างโครงการ


2
คุณต้องลบ `== 0` จากif SCNetworkReachabilityGetFlags(…)xCode ส่งคืน Bool ในเวอร์ชันล่าสุด
Melvin

64
นี่คือไม่ถูกต้องตาม @NickM กล่าวถึง จะไม่ทำงานสำหรับการเชื่อมต่อ CELLULAR อย่าใช้สิ่งนี้ ฉันประหลาดใจที่มีคะแนนโหวตมากเกินไปบางทีนี่อาจเป็นเรื่องใหม่ใน iOS 9 ... แต่ฉันไม่คิดอย่างนั้น
Jordan Smith

1
ใช้งานไม่ได้กับ IOS9 พบวิธีที่ยากมาก - แอปในสโตร์ ... ดังนั้นฉันเดาว่าแม้ว่ามันจะไม่ถูกต้อง 100% แต่ก็ใช้ได้กับบางคนมันไม่ได้ดีที่สุดดังนั้นใครก็ตามที่ต้องการโอกาสอาจต้องการ เดิมพันม้าหรือสุนัขถ้าพวกเขารู้สึกโชคดี
Nick M

2
@NickM - ที่อธิบายว่าทำไมถึงลงมติมากมาย @ Leo อาจเป็นความคิดที่ดีที่จะอัปเดตคำตอบสำหรับ iOS 9 เพื่อช่วยคนอื่นให้สะดุดกับกับดักตัวเดียวกัน ฉันสามารถยืนยันได้ว่ามันไม่ทำงาน (อย่างน้อยในบางสถานการณ์) บน iOS 9 ... การเปลี่ยนเป็น github.com/ashleymills/Reachability.swift ทำงานได้ดี
Jordan Smith

1
ใช่ฉันเห็นว่ามันกำลังออกมาในสาขา iOS10
Ace Green

226

สำหรับ Swift 3, Swift 4 (ทำงานกับเซลลูล่าร์และ Wi-Fi):

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(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 = SCNetworkReachabilityFlags(rawValue: 0)
        if SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) == false {
            return false
        }

        /* Only Working for WIFI
        let isReachable = flags == .reachable
        let needsConnection = flags == .connectionRequired

        return isReachable && !needsConnection
        */

        // Working for Cellular and WIFI
        let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
        let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
        let ret = (isReachable && !needsConnection)

        return ret

    }
}

การใช้งาน:

if Reachability.isConnectedToNetwork(){
    print("Internet Connection Available!")
}else{
    print("Internet Connection not Available!")
}

3
โซลูชันนี้ + CoreTelephony (CTTelephonyNetworkInfo (). currentRadioAccessTechnology) เป็นทางออกที่ดีที่สุดในการรับประเภทเครือข่ายปัจจุบันและไม่จำเป็นต้องพึ่ง libs ของบุคคลที่สาม
Alejandro Jiménez Agudo

3
ใช้งานได้กับ Cellular และ WiFi พร้อม swift 3.1! ทำงานได้กับการทดสอบบน iPhone 6 และ 7 หากปิดเซลลูลาร์ฉันจะได้รับ ถ้าฉันเปิดโหมดเครื่องบินรับ "... ไม่พร้อมใช้" ถ้าฉันปิดเซลลูลาร์แล้วเปิด wifi ฉันจะได้ ".. Connection Available" และในที่สุดถ้าฉันปิด wifi และเปิด Cellular ที่ฉันได้รับ ".. Connection Available" หมายเหตุการเชื่อมต่อมือถือของฉันคือ LTE
Brian

1
สวัสดีนี่ใช้งานไม่ได้กับ iOS 11 โหมดเครื่องบินบนอุปกรณ์จริง เมื่อตั้งค่าเป็นโหมดเครื่องบิน IsReachable ยังคงเป็นจริง ... ความคิดใด ๆ
Lawrence Tan

3
มันทำงานได้อย่างสมบูรณ์แบบสำหรับฉันในทุกสถานการณ์ ผ่านการทดสอบกับ iOS 12 ที่ทำงานบน iPhone 6
r3dm4n

3
ไม่สามารถใช้งานได้หากฉันเชื่อมต่อกับเครือข่าย wifi โดยไม่ต้องเชื่อมต่ออินเทอร์เน็ต
Nathan Barreto

58

Reachability.swiftสร้างแฟ้มสวิฟท์ใหม่ภายในโครงการของคุณชื่อมัน ตัดและวางรหัสต่อไปนี้เพื่อสร้างชั้นเรียนของคุณ

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(kCFAllocatorDefault, UnsafePointer($0))
        }

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

        let isReachable = flags == .Reachable
        let needsConnection = flags == .ConnectionRequired

        return isReachable && !needsConnection

    }
}

คุณสามารถตรวจสอบการเชื่อมต่ออินเทอร์เน็ตได้ทุกที่ในโครงการของคุณโดยใช้รหัสนี้:

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

หากผู้ใช้ไม่ได้เชื่อมต่อกับอินเทอร์เน็ตคุณอาจต้องการแสดงกล่องโต้ตอบการแจ้งเตือนให้พวกเขาทราบ

if Reachability.isConnectedToNetwork() {
    println("Internet connection OK")
} else {
    println("Internet connection FAILED")
    var alert = UIAlertView(title: "No Internet Connection", message: "Make sure your device is connected to the internet.", delegate: nil, cancelButtonTitle: "OK")
    alert.show()
}

คำอธิบาย:

เรากำลังทำคลาสสาธารณะที่ใช้ซ้ำได้และวิธีการที่สามารถใช้ที่ใดก็ได้ในโครงการเพื่อตรวจสอบการเชื่อมต่ออินเทอร์เน็ต เราต้องการเพิ่มกรอบพื้นฐานและการกำหนดค่าระบบ

ในความสามารถเข้าถึงได้ระดับสาธารณะวิธีisConnectedToNetwork() -> Bool { }นี้จะส่งคืนค่าบูลเกี่ยวกับการเชื่อมต่ออินเทอร์เน็ต เราใช้ if วนเพื่อดำเนินการที่จำเป็นในกรณี ฉันหวังว่านี่จะเพียงพอ ไชโย!


4
เมื่อคุณให้ทางออกโปรดลองอธิบายให้ฟังหน่อย การให้วิธีแก้ปัญหานั้นไม่สำคัญ แต่การทำให้ผู้อ่านของคุณเข้าใจวิธีแก้ปัญหา & แนวความคิดนั้นสำคัญกว่า
G.Abhisek

1
ฉันยังใหม่กับการเขียนโปรแกรม iOS ทำไมเราต้องเริ่มต้นโครงสร้าง C เพื่อวัตถุประสงค์ เราสามารถใช้โครงสร้าง C ในกรณีใด
G.Abhisek

1
ฉันวางสิ่งนี้ไว้บน "viewdidload" สำหรับการทดสอบเท่านั้น ฉันเปิดแอพและตกลงมีอินเทอร์เน็ต แต่เมื่อฉันถ่ายภาพอินเทอร์เน็ตมันจะยังคงแสดงให้ฉันเห็นว่าฉันมีอินเทอร์เน็ต ... นี่เป็นแบบถาวรหรือไม่ หรือฉันต้อง "เรียกการกระทำ" ทุกครั้งหรือไม่
Daniel Arantes Loverde

16
@AlvinGeorge สิ่งนี้ไม่ทำงานเมื่อเชื่อมต่อกับเครือข่ายมือถือ ใช้งานได้เมื่อใช้ wifi เท่านั้น สำหรับคนอื่น ๆ - คุณไม่ต้องการใช้สิ่งนี้ในแอพของคุณ! @ Alvin - ฉันขอแนะนำให้คุณอัปเดตคำตอบหรือลบออกได้ไหม? ด้วยคะแนน 19 คะแนนทำให้ผู้พัฒนารายอื่นเข้าใจผิดมากซึ่งอาจตกอยู่ในกับดักเดียวกัน
Jordan Smith

@Jordan: วิธีนี้ใช้กรอบแอปเปิ้ลเริ่มต้นสำหรับสถานะเครือข่าย มันอาจมีการเปลี่ยนแปลงด้วย iOS เวอร์ชันใหม่ ดังนั้นฉันขอแนะนำให้คุณตรวจสอบและใช้คลาสการเข้าถึงของ Ashely Mill ฉันได้ทำการติดตั้งใช้งานฟรีแล้วและแชร์ลิงค์ด้านบน กรุณาดู
AG

56

หากมีคนใช้ Alamofire อยู่แล้ว -

struct Connectivity {
  static let sharedInstance = NetworkReachabilityManager()!
  static var isConnectedToInternet:Bool {
      return self.sharedInstance.isReachable
    }
}

การใช้งาน:

if Connectivity.isConnectedToInternet {
     print("Connected")
 } else {
     print("No Internet")
}

1
จากเอกสารของ Alamofire:> คลาส NetworkReachabilityManager รับฟังการเปลี่ยนแปลงการเข้าถึงของโฮสต์และที่อยู่สำหรับทั้งอินเตอร์เฟสเครือข่าย WWAN และ WiFi > ความสามารถในการเข้าถึงสามารถใช้เพื่อกำหนดข้อมูลพื้นหลังเกี่ยวกับสาเหตุที่การดำเนินงานเครือข่ายล้มเหลวหรือลองส่งคำขอเครือข่ายอีกครั้งเมื่อมีการสร้างการเชื่อมต่อ ไม่ควรใช้เพื่อป้องกันไม่ให้ผู้ใช้เริ่มต้นคำขอเครือข่ายเนื่องจากอาจเป็นไปได้ว่าอาจต้องมีคำขอเริ่มต้นเพื่อสร้างความสามารถในการเข้าถึง
fespinozacast

1
@fespinozacast ดีคุณสามารถตรวจสอบรหัสการเชื่อมต่ออินเทอร์เน็ตในNetworkReachabilityManager.swiftซึ่งใช้ได้ดี :)
แจ็ค

15

ฉันได้ตรวจสอบการใช้งานระดับการเข้าถึงของแอชลี่ย์มิลล์โดยไม่มี Cocoa Pods / Dependancy Manager แนวคิดคือทำให้การพึ่งพาการเข้าถึงฟรีในโครงการ

Xcode 7.2 - Swift 2.1

1) https://github.com/ashleymills/Reachability.swift ดาวน์โหลดเพิ่มคลาสความสามารถในการเข้าถึงให้กับโครงการ

หมายเหตุ: ในขณะที่เพิ่มโปรดตรวจสอบให้แน่ใจว่าได้ทำเครื่องหมายที่ 'คัดลอกรายการถ้าจำเป็น'

2) สร้างคลาส AppManager.swift คลาสนี้จะรองรับเป็นคลาสโมเดลสาธารณะซึ่งจะเพิ่มวิธีการและข้อมูลสาธารณะและสามารถใช้ใน VC ใด ๆ

//  AppManager.swift

import UIKit
import Foundation

class AppManager: NSObject{
    var delegate:AppManagerDelegate? = nil
    private var _useClosures:Bool = false
    private var reachability: Reachability?
    private var _isReachability:Bool = false
    private var _reachabiltyNetworkType :String?

    var isReachability:Bool {
        get {return _isReachability}
    }  
   var reachabiltyNetworkType:String {
    get {return _reachabiltyNetworkType! }
   }   




    // Create a shared instance of AppManager
    final  class var sharedInstance : AppManager {
        struct Static {
            static var instance : AppManager?
        }
        if !(Static.instance != nil) {
            Static.instance = AppManager()

        }
        return Static.instance!
    }

    // Reachability Methods
    func initRechabilityMonitor() {
        print("initialize rechability...")
        do {
            let reachability = try Reachability.reachabilityForInternetConnection()
            self.reachability = reachability
        } catch ReachabilityError.FailedToCreateWithAddress(let address) {
            print("Unable to create\nReachability with address:\n\(address)")
            return
        } catch {}
        if (_useClosures) {
            reachability?.whenReachable = { reachability in
                self.notifyReachability(reachability)
            }
            reachability?.whenUnreachable = { reachability in
                self.notifyReachability(reachability)
            }
        } else {
            self.notifyReachability(reachability!)
        }

        do {
            try reachability?.startNotifier()
        } catch {
            print("unable to start notifier")
            return
        }


    }        
    private func notifyReachability(reachability:Reachability) {
        if reachability.isReachable() {
            self._isReachability = true

//Determine Network Type 
      if reachability.isReachableViaWiFi() {   
        self._reachabiltyNetworkType = CONNECTION_NETWORK_TYPE.WIFI_NETWORK.rawValue
      } else {
        self._reachabiltyNetworkType = CONNECTION_NETWORK_TYPE.WWAN_NETWORK.rawValue
      }

        } else {
            self._isReachability = false
self._reachabiltyNetworkType = CONNECTION_NETWORK_TYPE.OTHER.rawValue

        }

        NSNotificationCenter.defaultCenter().addObserver(self, selector: "reachabilityChanged:", name: ReachabilityChangedNotification, object: reachability)
    }
    func reachabilityChanged(note: NSNotification) {
        let reachability = note.object as! Reachability
        dispatch_async(dispatch_get_main_queue()) {
            if (self._useClosures) {
                self.reachability?.whenReachable = { reachability in
                    self.notifyReachability(reachability)
                }
                self.reachability?.whenUnreachable = { reachability in
                    self.notifyReachability(reachability)
                }
            } else {
                self.notifyReachability(reachability)
            }
            self.delegate?.reachabilityStatusChangeHandler(reachability)
        }
    }
    deinit {
        reachability?.stopNotifier()
        if (!_useClosures) {
            NSNotificationCenter.defaultCenter().removeObserver(self, name: ReachabilityChangedNotification, object: nil)
        }
    }
}

3) สร้างคลาสผู้แทน ฉันใช้วิธีผู้ได้รับมอบหมายเพื่อแจ้งสถานะการเชื่อมต่อ

//  Protocols.swift

import Foundation
@objc protocol AppManagerDelegate:NSObjectProtocol {

    func reachabilityStatusChangeHandler(reachability:Reachability)
}

4) สร้างคลาสผู้ปกครองของ UIViewController (วิธีการสืบทอด) คลาสพาเรนต์มีเมธอดซึ่งสามารถเข้าถึง VCs ลูกทั้งหมด

//  UIappViewController.swift

    import UIKit

    class UIappViewController: UIViewController,AppManagerDelegate {
        var manager:AppManager = AppManager.sharedInstance

        override func viewDidLoad() {
            super.viewDidLoad()
            manager.delegate = self
        }
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
        }
        func reachabilityStatusChangeHandler(reachability: Reachability) {
            if reachability.isReachable() {
                print("isReachable")
            } else {
                print("notReachable")
            }
        }
    }

5) เริ่มการตรวจสอบการเชื่อมต่ออินเทอร์เน็ตแบบเรียลไทม์ใน AppDelegate

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    AppManager.sharedInstance.initRechabilityMonitor()
return true
}

6) ฉันได้เพิ่ม AppReference ชื่อ Swift File เพื่อเก็บค่า enum คงที่

//  AppReference.swift

import Foundation

enum CONNECTION_NETWORK_TYPE : String {

  case WIFI_NETWORK = "Wifi"
  case WWAN_NETWORK = "Cellular"
  case OTHER = "Other"

}

7) บน ViewController (เช่นคุณต้องการเรียก API เฉพาะเมื่อเครือข่ายพร้อมใช้งาน)

//  ViewController.swift

        import UIKit

class ViewController: UIappViewController {
  var reachability:Reachability?

  override func viewDidLoad() {
    super.viewDidLoad()
    manager.delegate = self

    if(AppManager.sharedInstance.isReachability)
    {
      print("net available")
      //call API from here.

    } else {
      dispatch_async(dispatch_get_main_queue()) {
        print("net not available")
        //Show Alert
      }
    }


    //Determine Network Type
    if(AppManager.sharedInstance.reachabiltyNetworkType == "Wifi")
    {
      print(".Wifi")
    }
    else if (AppManager.sharedInstance.reachabiltyNetworkType == "Cellular")
    {
      print(".Cellular")
    }
    else {
      dispatch_async(dispatch_get_main_queue()) {
        print("Network not reachable")
      }
    }

  }
  override func viewWillAppear(animated: Bool) {
  }
  override func didReceiveMemoryWarning() {
  }
}

สามารถดาวน์โหลดตัวอย่าง @ https://github.com/alvinreuben/Reachability-Sample

อัปเกรดเป็น Swift 3.1- https://github.com/alvinvgeorge/Reachability-UpgradedToSwift3


ในขั้นตอนที่ 4 ... UIappViewController.swift ทำไมเราต้องใช้คลาสนี้!
Learn2Code

UIappViewController.swift ไม่ทำงานกับ UICollectionViewController ดังนั้นถ้าคุณมีมุมมองที่เป็นของ UICollectionViewController คุณไม่สามารถสืบทอด UIappViewController !?
Learn2Code

12

Apple ได้เปิดตัว Network Framework ใน iOS12

import Foundation
import Network

class NetworkReachability {

   var pathMonitor: NWPathMonitor!
   var path: NWPath?
   lazy var pathUpdateHandler: ((NWPath) -> Void) = { path in
    self.path = path
    if path.status == NWPath.Status.satisfied {
        print("Connected")
    } else if path.status == NWPath.Status.unsatisfied {
        print("unsatisfied")
    } else if path.status == NWPath.Status.requiresConnection {
        print("requiresConnection")
    } 
}

let backgroudQueue = DispatchQueue.global(qos: .background)

init() {
    pathMonitor = NWPathMonitor()
    pathMonitor.pathUpdateHandler = self.pathUpdateHandler
    pathMonitor.start(queue: backgroudQueue)
   } 

 func isNetworkAvailable() -> Bool {
        if let path = self.path {
           if path.status == NWPath.Status.satisfied {
            return true
          }
        }
       return false
   }
 }

เส้นทาง var: NWPath? เป็นศูนย์ เราจะเริ่มต้นคุณสมบัตินี้ได้อย่างไร
Vitya Shurapov

@VityaShurapov โดยการกำหนดพา ธ ที่คุณได้รับในบล็อก pathUpdateHandler
Yogendra Singh

นี่ดูแฟนซี แต่ฉันพบ (อย่างน้อยในเครื่องจำลอง) ว่าถ้าฉันเปลี่ยนแล็ปท็อป wifi ของฉันออกไปแล้วฉันจะได้รับการติดต่อกลับ แต่มันบอกว่า 'ไม่พอใจ' และหลังจากนั้นการโทรกลับไม่เคยเรียก
zaitsman

เมื่อเปิด / ปิดเซลลูลาร์ตัวจัดการจะทริกเกอร์เปิด / ปิดเพียงครั้งเดียว เมื่อ WiFi เปิด / ปิดฉันจะได้รับทริกเกอร์เหมือนกัน 2 ตัวสำหรับเปิด / ปิด (คุณสมบัติทั้งหมดเหมือนกัน) ทำไมถึงเป็นอย่างนั้น?
geohei

pathUpdateHandlerจะแนะนำการอ้างอิงที่แข็งแกร่งพร้อมselfเพิ่ม[weak self]เพื่อแก้ปัญหา :)
Andrea de Marco

11

ใช้สำหรับSwift-5 +

import Foundation
import UIKit
import SystemConfiguration

public class InternetConnectionManager {


    private init() {

    }

    public static func isConnectedToNetwork() -> Bool {

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

                SCNetworkReachabilityCreateWithAddress(nil, $0)

            }

        }) else {

            return false
        }
        var flags = SCNetworkReachabilityFlags()
        if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) {
            return false
        }
        let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
        let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
        return (isReachable && !needsConnection)
    }

}

การใช้งาน:

InternetConnectionManager.isConnectedToNetwork{
    print("Connected")
}else{
    print("Not Connected")
}

หรือเพียงแค่ใช้frameworkเพื่อเพิ่มเติมUtilities : ลิงก์


@ DocAsh59 :) ยินดีต้อนรับ! การเข้ารหัสที่มีความสุข!
Jamil Hasnine Tamim

ฉันไม่เข้าใจการใช้งาน คอมไพเลอร์แสดงข้อผิดพลาด "อาร์กิวเมนต์ที่ส่งผ่านไปยังการเรียกที่ไม่มีอาร์กิวเมนต์"
Roland Lariotte

1
@RolandLariotte คุณเพียงแค่เรียนในโครงการของคุณชื่อ - "InternetConnectionManager" แล้วโทรจากที่ที่คุณต้องการ!
Jamil Hasnine Tamim

ทำงานได้อย่างสมบูรณ์แบบ คุณคิดว่ารหัสนี้สามารถโต้ตอบได้มากขึ้นโดยใช้การรวมและส่งคืน AnyPublisher <Bool, Never> หรือไม่ คุณสามารถเขียนด้วยวิธีนี้ได้ไหม
Roland Lariotte

@RolandLariotte ใช่อาจเป็นได้ ฉันจะเขียนรหัสอื่นในภายหลัง ขอขอบคุณและอย่าลืมสนับสนุน! :)
Jamil Hasnine Tamim

7

เพิ่งคิดออกเอง

Xcode: 7.3.1, iOS 9.3.3

การใช้Ashleymills / Reachability.swiftเป็น Reachability.swift ในโครงการของฉันฉันได้สร้างฟังก์ชั่นต่อไปนี้:

func hasConnectivity() -> Bool {
    do {
        let reachability: Reachability = try Reachability.reachabilityForInternetConnection()
        let networkStatus: Int = reachability.currentReachabilityStatus.hashValue

        return (networkStatus != 0)
    }
    catch {
        // Handle error however you please
        return false
    }
}

เพียงโทรไปhasConnectivity()ที่ที่คุณต้องการตรวจสอบการเชื่อมต่อ ใช้งานได้กับ Wifi รวมถึง Cellular


การเพิ่มความสามารถในการเข้าถึงของแอชลีย์มิลส์เพื่อให้ผู้คนไม่ต้องย้ายไปมาระหว่างเว็บไซต์:

Copyright (c) 2014, Ashley Mills
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/

// Reachability.swift version 2.2beta2

import SystemConfiguration
import Foundation

public enum ReachabilityError: ErrorType {
    case FailedToCreateWithAddress(sockaddr_in)
    case FailedToCreateWithHostname(String)
    case UnableToSetCallback
    case UnableToSetDispatchQueue
}

public let ReachabilityChangedNotification = "ReachabilityChangedNotification"

func callback(reachability:SCNetworkReachability, flags: SCNetworkReachabilityFlags, info: UnsafeMutablePointer<Void>) {
    let reachability = Unmanaged<Reachability>.fromOpaque(COpaquePointer(info)).takeUnretainedValue()

    dispatch_async(dispatch_get_main_queue()) {
        reachability.reachabilityChanged(flags)
    }
}


public class Reachability: NSObject {

    public typealias NetworkReachable = (Reachability) -> ()
    public typealias NetworkUnreachable = (Reachability) -> ()

    public enum NetworkStatus: CustomStringConvertible {

        case NotReachable, ReachableViaWiFi, ReachableViaWWAN

        public var description: String {
            switch self {
            case .ReachableViaWWAN:
                return "Cellular"
            case .ReachableViaWiFi:
                return "WiFi"
            case .NotReachable:
                return "No Connection"
            }
        }
    }

    // MARK: - *** Public properties ***
    public var whenReachable: NetworkReachable?
    public var whenUnreachable: NetworkUnreachable?
    public var reachableOnWWAN: Bool
    public var notificationCenter = NSNotificationCenter.defaultCenter()

    public var currentReachabilityStatus: NetworkStatus {
        if isReachable() {
            if isReachableViaWiFi() {
                return .ReachableViaWiFi
            }
            if isRunningOnDevice {
                return .ReachableViaWWAN
            }
        }
        return .NotReachable
    }

    public var currentReachabilityString: String {
        return "\(currentReachabilityStatus)"
    }

    private var previousFlags: SCNetworkReachabilityFlags?

    // MARK: - *** Initialisation methods ***

    required public init(reachabilityRef: SCNetworkReachability) {
        reachableOnWWAN = true
        self.reachabilityRef = reachabilityRef
    }

    public convenience init(hostname: String) throws {

        let nodename = (hostname as NSString).UTF8String
        guard let ref = SCNetworkReachabilityCreateWithName(nil, nodename) else { throw ReachabilityError.FailedToCreateWithHostname(hostname) }

        self.init(reachabilityRef: ref)
    }

    public class func reachabilityForInternetConnection() throws -> Reachability {

        var zeroAddress = sockaddr_in()
        zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
        zeroAddress.sin_family = sa_family_t(AF_INET)

        guard let ref = withUnsafePointer(&zeroAddress, {
            SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
        }) else { throw ReachabilityError.FailedToCreateWithAddress(zeroAddress) }

        return Reachability(reachabilityRef: ref)
    }

    public class func reachabilityForLocalWiFi() throws -> Reachability {

        var localWifiAddress: sockaddr_in = sockaddr_in(sin_len: __uint8_t(0), sin_family: sa_family_t(0), sin_port: in_port_t(0), sin_addr: in_addr(s_addr: 0), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
        localWifiAddress.sin_len = UInt8(sizeofValue(localWifiAddress))
        localWifiAddress.sin_family = sa_family_t(AF_INET)

        // IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0
        let address: UInt32 = 0xA9FE0000
        localWifiAddress.sin_addr.s_addr = in_addr_t(address.bigEndian)

        guard let ref = withUnsafePointer(&localWifiAddress, {
            SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
        }) else { throw ReachabilityError.FailedToCreateWithAddress(localWifiAddress) }

        return Reachability(reachabilityRef: ref)
    }

    // MARK: - *** Notifier methods ***
    public func startNotifier() throws {

        guard !notifierRunning else { return }

        var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
        context.info = UnsafeMutablePointer(Unmanaged.passUnretained(self).toOpaque())

        if !SCNetworkReachabilitySetCallback(reachabilityRef!, callback, &context) {
            stopNotifier()
            throw ReachabilityError.UnableToSetCallback
        }

        if !SCNetworkReachabilitySetDispatchQueue(reachabilityRef!, reachabilitySerialQueue) {
            stopNotifier()
            throw ReachabilityError.UnableToSetDispatchQueue
        }

        // Perform an intial check
        dispatch_async(reachabilitySerialQueue) { () -> Void in
            let flags = self.reachabilityFlags
            self.reachabilityChanged(flags)
        }

        notifierRunning = true
    }

    public func stopNotifier() {
        defer { notifierRunning = false }
        guard let reachabilityRef = reachabilityRef else { return }

        SCNetworkReachabilitySetCallback(reachabilityRef, nil, nil)
        SCNetworkReachabilitySetDispatchQueue(reachabilityRef, nil)
    }

    // MARK: - *** Connection test methods ***
    public func isReachable() -> Bool {
        let flags = reachabilityFlags
        return isReachableWithFlags(flags)
    }

    public func isReachableViaWWAN() -> Bool {

        let flags = reachabilityFlags

        // Check we're not on the simulator, we're REACHABLE and check we're on WWAN
        return isRunningOnDevice && isReachable(flags) && isOnWWAN(flags)
    }

    public func isReachableViaWiFi() -> Bool {

        let flags = reachabilityFlags

        // Check we're reachable
        if !isReachable(flags) {
            return false
        }

        // Must be on WiFi if reachable but not on an iOS device (i.e. simulator)
        if !isRunningOnDevice {
            return true
        }

        // Check we're NOT on WWAN
        return !isOnWWAN(flags)
    }

    // MARK: - *** Private methods ***
    private var isRunningOnDevice: Bool = {
        #if (arch(i386) || arch(x86_64)) && os(iOS)
            return false
        #else
            return true
        #endif
    }()

    private var notifierRunning = false
    private var reachabilityRef: SCNetworkReachability?
    private let reachabilitySerialQueue = dispatch_queue_create("uk.co.ashleymills.reachability", DISPATCH_QUEUE_SERIAL)

    private func reachabilityChanged(flags: SCNetworkReachabilityFlags) {

        guard previousFlags != flags else { return }

        if isReachableWithFlags(flags) {
            if let block = whenReachable {
                block(self)
            }
        } else {
            if let block = whenUnreachable {
                block(self)
            }
        }

        notificationCenter.postNotificationName(ReachabilityChangedNotification, object:self)

        previousFlags = flags
    }

    private func isReachableWithFlags(flags: SCNetworkReachabilityFlags) -> Bool {

        if !isReachable(flags) {
            return false
        }

        if isConnectionRequiredOrTransient(flags) {
            return false
        }

        if isRunningOnDevice {
            if isOnWWAN(flags) && !reachableOnWWAN {
                // We don't want to connect when on 3G.
                return false
            }
        }

        return true
    }

    // WWAN may be available, but not active until a connection has been established.
    // WiFi may require a connection for VPN on Demand.
    private func isConnectionRequired() -> Bool {
        return connectionRequired()
    }

    private func connectionRequired() -> Bool {
        let flags = reachabilityFlags
        return isConnectionRequired(flags)
    }

    // Dynamic, on demand connection?
    private func isConnectionOnDemand() -> Bool {
        let flags = reachabilityFlags
        return isConnectionRequired(flags) && isConnectionOnTrafficOrDemand(flags)
    }

    // Is user intervention required?
    private func isInterventionRequired() -> Bool {
        let flags = reachabilityFlags
        return isConnectionRequired(flags) && isInterventionRequired(flags)
    }

    private func isOnWWAN(flags: SCNetworkReachabilityFlags) -> Bool {
        #if os(iOS)
            return flags.contains(.IsWWAN)
        #else
            return false
        #endif
    }
    private func isReachable(flags: SCNetworkReachabilityFlags) -> Bool {
        return flags.contains(.Reachable)
    }
    private func isConnectionRequired(flags: SCNetworkReachabilityFlags) -> Bool {
        return flags.contains(.ConnectionRequired)
    }
    private func isInterventionRequired(flags: SCNetworkReachabilityFlags) -> Bool {
        return flags.contains(.InterventionRequired)
    }
    private func isConnectionOnTraffic(flags: SCNetworkReachabilityFlags) -> Bool {
        return flags.contains(.ConnectionOnTraffic)
    }
    private func isConnectionOnDemand(flags: SCNetworkReachabilityFlags) -> Bool {
        return flags.contains(.ConnectionOnDemand)
    }
    func isConnectionOnTrafficOrDemand(flags: SCNetworkReachabilityFlags) -> Bool {
        return !flags.intersect([.ConnectionOnTraffic, .ConnectionOnDemand]).isEmpty
    }
    private func isTransientConnection(flags: SCNetworkReachabilityFlags) -> Bool {
        return flags.contains(.TransientConnection)
    }
    private func isLocalAddress(flags: SCNetworkReachabilityFlags) -> Bool {
        return flags.contains(.IsLocalAddress)
    }
    private func isDirect(flags: SCNetworkReachabilityFlags) -> Bool {
        return flags.contains(.IsDirect)
    }
    private func isConnectionRequiredOrTransient(flags: SCNetworkReachabilityFlags) -> Bool {
        let testcase:SCNetworkReachabilityFlags = [.ConnectionRequired, .TransientConnection]
        return flags.intersect(testcase) == testcase
    }

    private var reachabilityFlags: SCNetworkReachabilityFlags {

        guard let reachabilityRef = reachabilityRef else { return SCNetworkReachabilityFlags() }

        var flags = SCNetworkReachabilityFlags()
        let gotFlags = withUnsafeMutablePointer(&flags) {
            SCNetworkReachabilityGetFlags(reachabilityRef, UnsafeMutablePointer($0))
        }

        if gotFlags {
            return flags
        } else {
            return SCNetworkReachabilityFlags()
        }
    }

    override public var description: String {

        var W: String
        if isRunningOnDevice {
            W = isOnWWAN(reachabilityFlags) ? "W" : "-"
        } else {
            W = "X"
        }
        let R = isReachable(reachabilityFlags) ? "R" : "-"
        let c = isConnectionRequired(reachabilityFlags) ? "c" : "-"
        let t = isTransientConnection(reachabilityFlags) ? "t" : "-"
        let i = isInterventionRequired(reachabilityFlags) ? "i" : "-"
        let C = isConnectionOnTraffic(reachabilityFlags) ? "C" : "-"
        let D = isConnectionOnDemand(reachabilityFlags) ? "D" : "-"
        let l = isLocalAddress(reachabilityFlags) ? "l" : "-"
        let d = isDirect(reachabilityFlags) ? "d" : "-"

        return "\(W)\(R) \(c)\(t)\(i)\(C)\(D)\(l)\(d)"
    }

    deinit {
        stopNotifier()

        reachabilityRef = nil
        whenReachable = nil
        whenUnreachable = nil
    }
}

@ Xitcod13 สิ่งนี้จะใช้ได้กับเครือข่าย 4G ที่ถูกกล่าวว่าจะไม่แนะนำให้ทดสอบ preflight จัดการข้อยกเว้นที่คุณพยายามเชื่อมต่อแทนการใช้กรณีทดสอบการเข้าถึง
cmeadows


Reachabilty.swift มีปัญหากับ Swift 3
Famic Tech

ทำงานได้ดีกับ Swift 3 เพียงเปลี่ยนlet reachability: Reachability = try Reachability.reachabilityForInternetConnection()เป็นlet reachability: Reachability = try Reachability()!
davidethell

7

แม้ว่าจะไม่ได้ตอบคำถามของคุณโดยตรงฉันอยากจะพูดถึง Apple เมื่อเร็ว ๆ นี้มีการพูดคุยนี้:

https://developer.apple.com/videos/play/wwdc2018/714/

เมื่อเวลาประมาณ 09:55 เขาพูดถึงการทำสิ่งนี้ที่คุณถามเกี่ยวกับ:

  1. ตรวจสอบการเชื่อมต่อ
  2. หากการเชื่อมต่อ -> ทำอะไรสักอย่าง
  3. หากไม่มีการเชื่อมต่อ -> ทำอย่างอื่น (รอหรือไม่ลองอีกครั้ง?)

อย่างไรก็ตามสิ่งนี้มีข้อผิดพลาดเล็กน้อย:

  • ถ้าในขั้นตอนที่ 2 มีข้อความว่ามีการเชื่อมต่อ แต่เขาไม่ได้ 0.5 วินาทีในภายหลัง
  • จะทำอย่างไรถ้าผู้ใช้อยู่หลังพร็อกซี
  • สุดท้าย แต่ไม่ท้ายสุดจะเกิดอะไรขึ้นถ้าคำตอบบางอย่างที่นี่ไม่สามารถกำหนดการเชื่อมต่อได้ (ฉันแน่ใจว่าถ้าคุณปิดการเชื่อมต่ออย่างรวดเร็วไปที่ wi-fi และปิดการใช้งาน (เพียงแค่ทำให้มันซับซ้อน) มันแทบจะไม่สามารถระบุได้อย่างถูกต้องว่าฉันได้เชื่อมต่อหรือไม่
  • อ้างจากวิดีโอ: "ไม่มีทางที่จะรับประกันได้ว่าการดำเนินการในอนาคตจะประสบความสำเร็จหรือไม่"

ประเด็นต่อไปนี้คือแนวปฏิบัติที่เหมาะสมที่สุดตาม Apple:

ตามที่พูดคุยที่ไม่ควรมีเหตุผลที่จะตรวจสอบก่อน whetever คุณมีการเชื่อมต่ออินเทอร์เน็ตหรือไม่เพราะมันอาจไม่ถูกต้องในเวลาที่คุณส่งคำขอของคุณไปยังเซิร์ฟเวอร์


2

ฉันสร้างโซลูชันของตัวเองโดยใช้ NSTimer และ Alamofire:

import Alamofire

public class ConnectionHelper: NSObject {
    var request: Alamofire.Request?

    func isInternetConnected(completionHandler: Bool -> Void) {
        NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: "requestTimeout", userInfo: nil, repeats: false)

        request = Alamofire
            .request(
                Method.HEAD,
                "http://www.testurl.com"
            )
            .response { response in
                if response.3?.code == -999 {
                    completionHandler(
                        false
                    )
                } else {
                    completionHandler(
                        true
                    )
                }
        }
    }

    func requestTimeout() {
        request!.cancel()
    }
}

NSTimer ใช้เป็นการหมดเวลาและถูกใช้เนื่องจากผลลัพธ์ที่ไม่น่าเชื่อถือโดยใช้การหมดเวลาของ Alamofire ควรทำคำขอไปยัง URL ที่คุณเชื่อถือได้ว่ามีความน่าเชื่อถือเช่นเซิร์ฟเวอร์ของคุณเองหรือเซิร์ฟเวอร์ที่โฮสต์บริการที่คุณพึ่งพา

เมื่อหมดเวลาคำขอจะถูกยกเลิกและผลลัพธ์จะถูกส่งกลับโดยใช้ตัวจัดการที่สมบูรณ์

การใช้งาน:

ConnectionHelper().isInternetConnected() { internetConnected in
    if internetConnected {
        // Connected
    } else {
        // Not connected
    }
}

1
เกิดอะไรขึ้นถ้าเซิร์ฟเวอร์เว็บไซต์ไม่ทำงาน
TechBee

จากนั้นrequestTimeout()จะถูกเรียกหลังจากหมดเวลาที่กำหนด
Mark Tickner

ขอบคุณมาร์ค! แนวทางของคุณควรเป็นเรื่องทั่วไป !!
TechBee

1
อย่างไหนล่ะ, แบบไหนล่ะ? เว็บไซต์ที่ใช้? แนวคิดก็คือเว็บไซต์ที่ใช้นั้นเป็นแอปที่แอปของคุณขึ้นอยู่กับ
Mark Tickner

อาจไม่ใช่ความคิดที่ดีที่จะทำการเชื่อมต่อเครือข่ายและดูว่ามันกลับมาพร้อมกับการตอบสนองหรือไม่ มีอะไรเกิดขึ้น
GoodSp33d

2

หากคุณใช้ Alamofire คุณสามารถทำสิ่งนี้:

let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.timeoutIntervalForRequest = 15 //Set timeouts in sec
configuration.timeoutIntervalForResource = 15

let alamoFireManager = Alamofire.Manager(configuration:configuration)
alamoFireManager?.request(.GET, "https://yourURL.com", parameters: headers, encoding: .URL)
                     .validate()
                              .responseJSON { response in

                                if let error = response.result.error {
                                   switch error.code{
                                    case -1001:
                                        print("Slow connection")
                                        return
                                    case -1009:
                                        print("No Connection!")
                                        return
                                    default: break
                                    }
                                }

2

สวิฟท์ 5

import SystemConfiguration    

protocol Utilities {}
extension NSObject: Utilities {
    enum ReachabilityStatus {
        case notReachable
        case reachableViaWWAN
        case reachableViaWiFi
    }

    var currentReachabilityStatus: ReachabilityStatus {

        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 .notReachable
        }

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

        if flags.contains(.reachable) == false {
            // The target host is not reachable.
            return .notReachable
        }
        else if flags.contains(.isWWAN) == true {
            // WWAN connections are OK if the calling application is using the CFNetwork APIs.
            return .reachableViaWWAN
        }
        else if flags.contains(.connectionRequired) == false {
            // If the target host is reachable and no connection is required then we'll assume that you're on Wi-Fi...
            return .reachableViaWiFi
        }
        else if (flags.contains(.connectionOnDemand) == true || flags.contains(.connectionOnTraffic) == true) && flags.contains(.interventionRequired) == false {
            // The connection is on-demand (or on-traffic) if the calling application is using the CFSocketStream or higher APIs and no [user] intervention is needed
            return .reachableViaWiFi
        }
        else {
            return .notReachable
        }
    }
}

ไม่ว่าในวิธีใดให้ใช้เงื่อนไขดังต่อไปนี้

if currentReachabilityStatus == .notReachable {
    // Network Unavailable
 } else {
    // Network Available
 }

1

นี่คือทางออกของฉันสำหรับ swift 2.3 พร้อมกับ lib ( Reachability.swift )

เข้าสู่ของคุณPodfileและเพิ่ม:

pod 'ReachabilitySwift', '~> 2.4' // swift 2.3

จากนั้นเป็นของคุณterminal:

pod install

จากนั้นสร้างไฟล์ใหม่ReachabilityManagerและเพิ่มรหัสด้านล่าง:

import Foundation
import ReachabilitySwift

enum ReachabilityManagerType {
    case Wifi
    case Cellular
    case None
}

class ReachabilityManager {
    static let sharedInstance = ReachabilityManager()

    private var reachability: Reachability!
    private var reachabilityManagerType: ReachabilityManagerType = .None


    private init() {
        do {
            self.reachability = try Reachability.reachabilityForInternetConnection()
        } catch {
            print("Unable to create Reachability")
            return
        }

        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ReachabilityManager.reachabilityChanged(_:)),name: ReachabilityChangedNotification,object: self.reachability)
        do{
            try self.reachability.startNotifier()
        }catch{
            print("could not start reachability notifier")
        }
    }

    @objc private func reachabilityChanged(note: NSNotification) {

        let reachability = note.object as! Reachability

        if reachability.isReachable() {
            if reachability.isReachableViaWiFi() {
                self.reachabilityManagerType = .Wifi
            } else {
                self.reachabilityManagerType = .Cellular
            }
        } else {
            self.reachabilityManagerType = .None
        }
    }
}

extension ReachabilityManager {

    func isConnectedToNetwork() -> Bool {
        return reachabilityManagerType != .None
    }

}

ใช้อย่างไร:

เข้าไปของคุณAppDelegate.swiftและเพิ่มรหัสด้านล่าง:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
     ReachabilityManager.sharedInstance
}

จากนั้นเมื่อคุณต้องการตรวจสอบว่าอุปกรณ์เชื่อมต่อกับอินเทอร์เน็ตหรือไม่:

if ReachabilityManager.sharedInstance.isConnectedToNetwork() {
   // Connected
} else {
  // Not connected
}

1

ในขณะที่มันไม่สามารถระบุได้โดยตรงว่าโทรศัพท์เชื่อมต่อกับเครือข่ายหรือไม่วิธีแก้ปัญหาที่ง่ายที่สุด (สะอาดที่สุด) คือ 'ping' Google หรือเซิร์ฟเวอร์อื่น ๆ (ซึ่งไม่สามารถทำได้เว้นแต่โทรศัพท์จะเชื่อมต่อกับเครือข่าย ):

private var urlSession:URLSession = {
    var newConfiguration:URLSessionConfiguration = .default
    newConfiguration.waitsForConnectivity = false
    newConfiguration.allowsCellularAccess = true
    return URLSession(configuration: newConfiguration)
}()

public func canReachGoogle() -> Bool
{
    let url = URL(string: "https://8.8.8.8")
    let semaphore = DispatchSemaphore(value: 0)
    var success = false
    let task = urlSession.dataTask(with: url!)
    { data, response, error in
        if error != nil
        {
            success = false
        }
        else
        {
            success = true
        }
        semaphore.signal()
    }

    task.resume()
    semaphore.wait()

    return success
}

หากคุณกังวลว่าเซิร์ฟเวอร์อาจไม่ทำงานหรืออาจบล็อก IP ของคุณคุณสามารถ ping เซิร์ฟเวอร์หลายเครื่องในลักษณะเดียวกันและกลับมาว่าสามารถเข้าถึงได้หรือไม่ หรือมีคนตั้งค่าเซิร์ฟเวอร์เฉพาะเพื่อจุดประสงค์นี้


0

ด้วยความช่วยเหลือของรหัสด้านล่างคุณสามารถตรวจสอบการเชื่อมต่ออินเทอร์เน็ตสำหรับเครือข่ายเซลลูลาร์และ WiFi ภาษา - Swift 3.0

import UIKit
import Foundation
import SystemConfiguration

class NetworkConnection: UIViewController {

  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(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) {
        SCNetworkReachabilityCreateWithAddress(nil, $0)
      }
    }) else {
      return false
    }

    var flags: SCNetworkReachabilityFlags = SCNetworkReachabilityFlags(rawValue: 0)
    if SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) == false {
      return false
    }
    let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
    let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
    return (isReachable && !needsConnection)
  }

  class func checkConnection(sender:UIViewController){
    if NetworkConnection.isConnectedToNetwork() == true {
      print("Connected to the internet")
      //  Do something
    } else {
      print("No internet connection")
      let alertController = UIAlertController(title: "No Internet Available", message: "", preferredStyle: UIAlertControllerStyle.alert)
      let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default){(result:UIAlertAction) -> Void in
        return
      }
      alertController.addAction(okAction)
      sender.present(alertController, animated: true, completion: nil)
      //  Do something
    }
  }

}

0

สำหรับ Swift 3 ฉันไม่สามารถใช้การเข้าถึงได้ง่ายจากโซลูชันของ RAJAMOHAN-S เนื่องจากมันคืนค่า "จริง" ถ้ามี WiFi แต่ไม่มีอินเทอร์เน็ต ดังนั้นฉันจึงใช้การตรวจสอบที่สองผ่านชั้น URLSession และตัวจัดการความสมบูรณ์

นี่คือทั้งชั้นเรียน

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(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 = SCNetworkReachabilityFlags(rawValue: 0)
if SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) == false {
  return false
}

// Working for Cellular and WIFI
let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
let ret = (isReachable && !needsConnection)

return ret
}



class func isInternetAvailable(webSiteToPing: String?, completionHandler: @escaping (Bool) -> Void) {

// 1. Check the WiFi Connection
guard isConnectedToNetwork() else {
  completionHandler(false)
  return
}

// 2. Check the Internet Connection
var webAddress = "https://www.google.com" // Default Web Site
if let _ = webSiteToPing {
  webAddress = webSiteToPing!
}

guard let url = URL(string: webAddress) else {
  completionHandler(false)
  print("could not create url from: \(webAddress)")
  return
}

let urlRequest = URLRequest(url: url)
let session = URLSession.shared
let task = session.dataTask(with: urlRequest, completionHandler: { (data, response, error) in
  if error != nil || response == nil {
    completionHandler(false)
  } else {
    completionHandler(true)
  }
})

  task.resume()
}
}

และคุณเรียกสิ่งนี้เช่นนี้:

Reachability.isInternetAvailable(webSiteToPing: nil) { (isInternetAvailable) in
  guard isInternetAvailable else {
    // Inform user for example
    return
  }

  // Do some action if there is Internet
}

0

นี่คือรุ่นของฉัน เป็นหลักมันไม่ได้นำอะไรใหม่ ฉันผูกมันไว้กับ UIDevice

import UIKit
import SystemConfiguration

extension UIDevice {

    open class var isConnectedToNetwork: Bool {
        get {
            var zeroAddress = sockaddr_in()
            zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
            zeroAddress.sin_family = sa_family_t(AF_INET)

            guard
                let defaultRouteReachability: SCNetworkReachability = withUnsafePointer(to: &zeroAddress, {
                    $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
                        SCNetworkReachabilityCreateWithAddress(nil, $0)
                    }
                }),
                var flags: SCNetworkReachabilityFlags = SCNetworkReachabilityFlags() as SCNetworkReachabilityFlags?,
                SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags)
                else { return false }

            return flags.contains(.reachable) && !flags.contains(.connectionRequired)
        }
    }

}

print("Network status availability: " + ( UIDevice.isConnectedToNetwork ? "true" : "false" ))

0
struct Connectivity {
        static let sharedInstance = NetworkReachabilityManager()!
        static var isConnectedToInternet:Bool {
            return self.sharedInstance.isReachable
        }
    }

ตอนนี้เรียกมันว่า

if Connectivity.isConnectedToInternet{
            call_your_methods_here()
        }else{
            show_alert_for_noInternet()
        }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.