स्विफ्ट में SCNetworkReachability का उपयोग कैसे करें


99

मैं इस कोड स्निपेट को स्विफ्ट में बदलने की कोशिश कर रहा हूं । मैं कुछ कठिनाइयों के कारण मैदान से हटने पर संघर्ष कर रहा हूं।

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

पहला और मुख्य मुद्दा मेरे पास है कि कैसे सी संरचनाओं के साथ परिभाषित और काम करना है। struct sockaddr_in zeroAddress;उपरोक्त कोड की पहली पंक्ति ( ) में, मुझे लगता है कि वे zeroAddressसंरचना sockaddr_in (?) से बुलाए गए एक उदाहरण को परिभाषित कर रहे हैं , मुझे लगता है। मैंने varइस तरह की घोषणा करने की कोशिश की ।

var zeroAddress = sockaddr_in()

लेकिन मुझे कॉल में पैरामीटर 'sin_len' के लिए त्रुटि मिस्ड तर्क मिलता है जो समझ में आता है क्योंकि वह संरचना कई तर्क लेती है। इसलिए मैंने फिर से कोशिश की।

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

जैसा कि अपेक्षित था मुझे कुछ अन्य त्रुटि प्राप्त हुई जिसका उपयोग अपने स्वयं के प्रारंभिक मूल्य के भीतर किया गया था । मैं उस त्रुटि का कारण भी समझता हूं। सी में, वे पहले उदाहरण की घोषणा करते हैं और फिर मापदंडों को भरते हैं। जहां तक ​​मुझे पता है स्विफ्ट में यह संभव नहीं है। तो मैं वास्तव में इस बिंदु पर खो गया हूं कि क्या करना है।

मैंने स्विफ्ट में सी एपीआई के साथ बातचीत करने पर ऐप्पल के आधिकारिक दस्तावेज को पढ़ा, लेकिन इसकी संरचना के साथ काम करने में कोई उदाहरण नहीं है।

किसी को भी कृपया मेरी मदद कर सकते हैं यहाँ? मैं वाकई इसकी सराहना करूंगा।

धन्यवाद।


अद्यतन: मार्टिन के लिए धन्यवाद मैं प्रारंभिक समस्या को हल करने में सक्षम था। लेकिन फिर भी स्विफ्ट मेरे लिए इसे आसान नहीं बना रहा है। मुझे कई नई त्रुटियाँ मिल रही हैं।

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: ठीक है मैंने इस लाइन को इसमें बदल दिया,

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

इस लाइन पर मुझे जो नई त्रुटि मिल रही है, वह है 'अनसेफ पॉइंटर' 'CFAllocator' के लिए परिवर्तनीय नहीं है । आप NULLस्विफ्ट में कैसे पास होंगे ?

इसके अलावा मैंने इस लाइन को बदल दिया है और अब त्रुटि हो गई है।

let didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags)

EDIT 2: मैं इस प्रश्न को nilदेखने के बाद इस पंक्ति में गया । लेकिन वह जवाब यहां के जवाब के साथ विरोधाभासी है । इसमें कहा गया है कि स्विफ्ट में कोई समकक्ष नहीं है ।NULL

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

वैसे भी मुझे एक नई त्रुटि यह कहते हुए मिलती है कि उपरोक्त पंक्ति में 'sockaddr_in' 'sockaddr' के समान नहीं है


मैं लाइन में त्रुटि कर रहा हूँ अगर! SCNetworkReachabilityGetFlags (defaultRouteReachability, और झंडे) यानी गैरी ऑपरेटर! बूलियन के एक ऑपरेंड पर लागू नहीं किया जा सकता है। । । । कृपया मदद कीजिए।
ज़िबोक

जवाबों:


236

(स्विफ्ट भाषा में बदलाव के कारण इस उत्तर को बार-बार बढ़ाया गया, जिसने इसे थोड़ा भ्रमित कर दिया। मैंने अब इसे फिर से लिखा है और स्विफ्ट 1.x को संदर्भित करने वाली सभी चीज़ों को हटा दिया है। पुराने कोड को संपादित इतिहास में पाया जा सकता है अगर किसी को ज़रूरत हो तो यह।)

यह आप इसे स्विफ्ट 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)
}

स्पष्टीकरण:

  • स्विफ्ट 1.2 (एक्सकोड 6.3) के रूप में, आयातित सी स्ट्रक्चर्स में स्विफ्ट में एक डिफ़ॉल्ट इनिशियलाइज़र है, जो संरचना के सभी क्षेत्रों को शून्य पर आरंभीकृत करता है, इसलिए सॉकेट एड्रेस संरचना के साथ इनिशियलाइज़ किया जा सकता है

    var zeroAddress = sockaddr_in()
  • sizeofValue()देता है इस संरचना का आकार, यह करने के लिए परिवर्तित किया गया है UInt8के लिए sin_len:

    zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
  • AF_INETएक है Int32, इसके लिए इसे सही प्रकार में बदलना होगा sin_family:

    zeroAddress.sin_family = sa_family_t(AF_INET)
  • withUnsafePointer(&zeroAddress) { ... }संरचना के पते को बंद करने के लिए पास करता है जहां इसका उपयोग तर्क के रूप में किया जाता है SCNetworkReachabilityCreateWithAddress()UnsafePointer($0) रूपांतरण की जरूरत है क्योंकि वह कार्य करने के लिए एक सूचक की उम्मीद है sockaddr, नहीं sockaddr_in

  • से लौटाया गया मान लौटा withUnsafePointer()मूल्य है SCNetworkReachabilityCreateWithAddress()और जिसका प्रकार है SCNetworkReachability?, यानी यह एक वैकल्पिक है। guard letबयान (स्विफ्ट 2.0 में एक नई सुविधा) के लिए unwrapped मूल्य प्रदान करती है defaultRouteReachabilityचर अगर यह नहीं है nil। अन्यथा elseब्लॉक निष्पादित होता है और फ़ंक्शन वापस लौटता है।

  • स्विफ्ट 2 के रूप में, SCNetworkReachabilityCreateWithAddress()एक प्रबंधित वस्तु लौटाता है। आपको इसे स्पष्ट रूप से जारी करने की आवश्यकता नहीं है।
  • स्विफ्ट 2 के रूप में, SCNetworkReachabilityFlagsके अनुरूप OptionSetTypeहै जो एक सेट की तरह इंटरफ़ेस है। आप एक खाली झंडे वाला चर बनाते हैं

    var flags : SCNetworkReachabilityFlags = []

    और झंडे के साथ की जाँच करें

    let isReachable = flags.contains(.Reachable)
    let needsConnection = flags.contains(.ConnectionRequired)
  • दूसरे पैरामीटर का SCNetworkReachabilityGetFlagsप्रकार होता है UnsafeMutablePointer<SCNetworkReachabilityFlags>, जिसका अर्थ है कि आपको झंडे के चर का पता पास करना होगा ।

यह भी ध्यान दें कि स्विफ्ट 2 के रूप में एक नोटिफ़ायर कॉलबैक दर्ज करना संभव है, स्विफ्ट और स्विफ्ट 2 से सी एपीआई के साथ काम करना तुलना करें - UnsafeMutablePointer <Void> to object


स्विफ्ट 3/4 के लिए अपडेट करें:

असुरक्षित पॉइंटर्स को अब अलग प्रकार के पॉइंटर में नहीं बदला जा सकता है (देखें - SE-0107 UnsafeRawPointer API )। यहाँ अद्यतन कोड:

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

4
@Isuru: UnsafePointer एक C पॉइंटर के बराबर स्विफ्ट है। तर्क के पते के साथ withUnsafePointer(&zeroAddress)निम्नलिखित बंद कहते हैं । क्लोजर के अंदर, उस तर्क के लिए खड़ा है। - क्षमा करें, कुछ वाक्यों में यह सब समझाना असंभव है। स्विफ्ट बुक में क्लोजर के बारे में डॉक्यूमेंटेशन देखें। $ 0 एक "शॉर्टहैंड तर्क नाम" है। { ...}zeroAddress$0
मार्टिन आर।

1
@ जाल: आप सही कह रहे हैं, एप्पल ने बदल दिया कि कैसे एक "बुलियन" को स्विफ्ट में मैप किया जाता है। आपकी प्रतिक्रिया के लिए धन्यवाद, मैं तदनुसार जवाब अपडेट कर दूंगा।
मार्टिन आर

1
यह रिटर्न trueअगर वाईफाई कनेक्ट नहीं है और 4 जी चालू है, लेकिन उपयोगकर्ता ने निर्दिष्ट किया है कि ऐप सेलुलर डेटा का उपयोग नहीं कर सकता है .. कोई समाधान?
मैक्स चुक्विमिया

5
@ जुगले: आप कुछ ऐसा कर सकते हैं: let cellular = flags.contains(.IsWWAN) आप बूलियन के बजाय एक लौटा सकते हैं, जैसे: func connectedToNetwork() -> (connected: Bool, cellular: Bool)
EdFunke

3
@Tejas: आप "शून्य पते" के बजाय किसी भी आईपी पते का उपयोग कर सकते हैं, या स्ट्रिंग के रूप में होस्ट नाम के साथ SCNetworkReachabilityCreateWithName () का उपयोग कर सकते हैं। लेकिन ध्यान दें कि SCNetworkReachability केवल यह जांचती है कि उस पते पर भेजा गया पैकेट स्थानीय डिवाइस को छोड़ सकता है या नहीं। यह गारंटी नहीं देता है कि डेटा पैकेट वास्तव में होस्ट द्वारा प्राप्त किया जाएगा।
मार्टिन आर

12

स्विफ्ट 3, आईपीवी 4, आईपीवी 6

मार्टिन आर के जवाब पर आधारित:

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

2
मेरे लिए NET64 / IPV6 के लिए भी सबसे अच्छा तरीका काम करना, मत भूलनाimport SystemConfiguration
Bhavin_m

@ जुआनजो, आप अपने कोड का उपयोग करके जिस होस्ट तक पहुंचना चाहते हैं, उसे कैसे सेट करते हैं
user2924482

6

इसका स्विफ्ट से कोई लेना-देना नहीं है, लेकिन सबसे अच्छा उपाय यह है कि नेटवर्क ऑनलाइन हो या नहीं यह निर्धारित करने के लिए रीचैबिलिटी का उपयोग न करें। असफल होने पर बस अपना कनेक्शन बनाएं और त्रुटियों को संभालें। एक कनेक्शन बनाने से कई बार निष्क्रिय ऑफ़लाइन रेडियो में आग लग सकती है।

रीचैबिलिटी का एक वैध उपयोग यह है कि इसका उपयोग आपको सूचित करने के लिए किया जाए जब कोई नेटवर्क ऑफ़लाइन से ऑनलाइन में परिवर्तन करता है। उस समय आपको असफल कनेक्शन को पुनः प्रयास करना चाहिए।



अभी भी छोटी गाड़ी है। बस कनेक्शन बनाएं और त्रुटियों को संभालें। Openradar.me/21581686 और mail-archive.com/macnetworkprog@lists.apple.com/msg00200.html देखें और यहां पहली टिप्पणी mikeash.com/pyblog/friday-qa-2014-06-14-reachability.html
EricS

मुझे यह नहीं मिलता है - क्या आप यह जानना नहीं चाहेंगे कि क्या आप किसी बड़े अपलोड के प्रयास से पहले वाईफाई या 3 जी पर थे?
dumbledad

3
ऐतिहासिक रूप से, यदि रेडियो को नीचे संचालित किया गया था, तो रीचैबिलिटी काम नहीं करती थी। मैंने आईओएस 9 में आधुनिक उपकरणों पर इसका परीक्षण नहीं किया है, लेकिन मैं गारंटी देता हूं कि यह आईओएस के पुराने संस्करणों में अपलोड विफलताओं का कारण बनता था जब केवल एक कनेक्शन बनाने से ठीक काम होता था। यदि आप केवल वाईफाई के माध्यम से जाना चाहते हैं, तो आपको NSURLSessionएपीआई का उपयोग करना चाहिए NSURLSessionConfiguration.allowsCellularAccess = false
एरिकस

3

सबसे अच्छा उपाय ReachabilitySwift कक्षा का उपयोग करना , लिखित Swift 2और उपयोग करना है SCNetworkReachabilityRef

सरल और आसान:

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

आकर्षण की तरह काम करना।

का आनंद लें


7
मैं स्वीकृत उत्तर को पसंद करता हूं क्योंकि इसमें किसी तीसरे पक्ष की निर्भरता को एकीकृत करने की आवश्यकता नहीं है। इसके अतिरिक्त, यह SCNetworkReachabilityस्विफ्ट में कक्षा का उपयोग करने के तरीके के सवाल का जवाब नहीं देता है, यह वैध नेटवर्क कनेक्शन की जांच के लिए उपयोग करने के लिए निर्भरता का सुझाव है।
JAL

1

सिंगलटन उदाहरण बनाने के लिए जुआनजो का जवाब अपडेट किया गया

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

प्रयोग

if Reachability.shared.isConnectedToNetwork(){

}

1

यह स्विफ्ट 4.0 में है

मैं इस ढांचे का उपयोग कर रहा https://github.com/ashleymills/Reachability.swift
और Pod स्थापित करें ..
में 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")
    }
}
}

अगर इंटरनेट नहीं है तो रीचबिलिटी व्यू कॉन्ट्रोलर स्क्रीन दिखाई देगी

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.