[ios] Swift에서 SCNetworkReachability를 사용하는 방법

나는 이것을 변환하려고 해요 코드 스 니펫을 Swift . 나는 약간의 어려움 때문에 땅에서 내리는 데 어려움을 겪고있다.

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

내가 가진 첫 번째이자 주요 문제는 C 구조체를 정의하고 사용하는 방법입니다. struct sockaddr_in zeroAddress;위 코드 의 첫 번째 줄 ( )에서 나는 그들이 zeroAddresssockaddr_in (?) 구조체에서 호출 된 인스턴스를 정의하고 있다고 생각합니다. 나는 var이와 같은 선언을 시도 했습니다.

var zeroAddress = sockaddr_in()

그러나 해당 구조체가 여러 인수를 사용하기 때문에 이해할 수 있는 호출에서 매개 변수 ‘sin_len’에 대한 Missing 인수 오류가 발생 합니다. 그래서 다시 시도했습니다.

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

예상대로 자체 초기 값 내에서 사용되는 다른 오류 변수가 있습니다. . 그 오류의 원인도 이해합니다. C에서는 먼저 인스턴스를 선언 한 다음 매개 변수를 채 웁니다. 내가 아는 한 Swift에서는 불가능합니다. 그래서 저는이 시점에서 무엇을해야할지 정말로 길을 잃었습니다.

Swift에서 C API와의 상호 작용에 대한 Apple의 공식 문서 를 읽었 지만 구조체 작업에 대한 예제가 없습니다.

누구든지 여기서 나를 도울 수 있습니까? 정말 감사하겠습니다.

감사합니다.


업데이트 : Martin 덕분에 초기 문제를 극복 할 수있었습니다. 그러나 여전히 Swift는 나를 더 쉽게 만들지 않습니다. 여러 개의 새로운 오류가 발생합니다.

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
}

편집 1 : 좋아이 줄을 이것으로 변경했습니다.

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

이 줄에서 얻는 새로운 오류는 ‘UnsafePointer’가 ‘CFAllocator’로 변환되지 않습니다 . 통과하는 방법NULLSwift를 어떻게 합니까?

또한이 줄을 변경하고 오류가 사라졌습니다.

let didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags)

편집 2 : 질문 nil을 본 후이 줄을 통과 했습니다 . 그러나 그 대답은 여기 의 대답과 모순 됩니다 . 그것은 Swift에 상응하는 것이 없다고 말합니다 .NULL

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

어쨌든 위 줄에서 ‘sockaddr_in’이 ‘sockaddr’과 동일하지 않다는 새로운 오류가 발생 합니다.



답변

(이 답변은 Swift 언어의 변경으로 인해 반복적으로 확장되어 약간 혼란 스러웠습니다. 이제 다시 작성하고 Swift 1.x를 참조하는 모든 것을 제거했습니다. 누군가가 필요하면 편집 기록에서 이전 코드를 찾을 수 있습니다 그것.)

다음은 Swift 2.0 (Xcode 7) 에서 수행하는 방법입니다 .

import SystemConfiguration

func connectedToNetwork() -> Bool {

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

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

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

    let isReachable = flags.contains(.Reachable)
    let needsConnection = flags.contains(.ConnectionRequired)

    return (isReachable && !needsConnection)
}

설명 :

  • Swift 1.2 (Xcode 6.3)부터 가져온 C 구조체에는 Swift의 기본 초기화 프로그램이 있습니다.이 초기화는 구조체의 모든 필드를 0으로 초기화하므로 소켓 주소 구조를 다음과 같이 초기화 할 수 있습니다.

    var zeroAddress = sockaddr_in()
  • sizeofValue()이러한 구조의 크기는,이 변환되어야 제공 UInt8sin_len:

    zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
  • AF_INETInt32이면 올바른 유형으로 변환해야합니다 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의 새로운 기능)은 상기 언 래핑 된 값에 할당 defaultRouteReachability그렇지 않은 경우 변수를 nil. 그렇지 않으면 else블록이 실행되고 함수가 반환됩니다.

  • Swift 2부터는 SCNetworkReachabilityCreateWithAddress()관리되는 객체를 반환합니다. 명시 적으로 해제 할 필요는 없습니다.
  • Swift 2 SCNetworkReachabilityFlags부터는
    OptionSetTypeset-like 인터페이스를 가지고 있습니다. 다음을 사용하여 빈 플래그 변수를 만듭니다.

    var flags : SCNetworkReachabilityFlags = []

    플래그를 확인하십시오.

    let isReachable = flags.contains(.Reachable)
    let needsConnection = flags.contains(.ConnectionRequired)
  • 의 두 번째 매개 변수 SCNetworkReachabilityGetFlags에는 유형이 있습니다
    UnsafeMutablePointer<SCNetworkReachabilityFlags>. 이는 flags 변수 의 주소 를 전달해야 함을 의미 합니다.

알림 콜백 등록은 Swift 2부터 가능합니다. Working with C APIs from SwiftSwift 2-UnsafeMutablePointer <Void>를 object와 비교하십시오 .


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


답변

스위프트 3, IPv4, IPv6

Martin R의 답변을 바탕으로 :

import SystemConfiguration

func isConnectedToNetwork() -> Bool {
    guard let flags = getFlags() else { return false }
    let isReachable = flags.contains(.reachable)
    let needsConnection = flags.contains(.connectionRequired)
    return (isReachable && !needsConnection)
}

func getFlags() -> SCNetworkReachabilityFlags? {
    guard let reachability = ipv4Reachability() ?? ipv6Reachability() else {
        return nil
    }
    var flags = SCNetworkReachabilityFlags()
    if !SCNetworkReachabilityGetFlags(reachability, &flags) {
        return nil
    }
    return flags
}

func ipv6Reachability() -> SCNetworkReachability? {
    var zeroAddress = sockaddr_in6()
    zeroAddress.sin6_len = UInt8(MemoryLayout<sockaddr_in>.size)
    zeroAddress.sin6_family = sa_family_t(AF_INET6)

    return withUnsafePointer(to: &zeroAddress, {
        $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
            SCNetworkReachabilityCreateWithAddress(nil, $0)
        }
    })
}

func ipv4Reachability() -> SCNetworkReachability? {
    var zeroAddress = sockaddr_in()
    zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
    zeroAddress.sin_family = sa_family_t(AF_INET)

    return withUnsafePointer(to: &zeroAddress, {
        $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
            SCNetworkReachabilityCreateWithAddress(nil, $0)
        }
    })
}


답변

이것은 Swift와 관련이 없지만 가장 좋은 해결책은 네트워크가 온라인 상태인지 확인하기 위해 Reachability를 사용하지 않는 것입니다. 연결이 실패하면 오류를 처리하십시오. 연결하면 때때로 휴면 상태 인 오프라인 라디오가 작동 할 수 있습니다.

Reachability의 한 가지 유효한 용도는 네트워크가 오프라인에서 온라인으로 전환 될 때이를 알리는 데 사용하는 것입니다. 이 시점에서 실패한 연결을 다시 시도해야합니다.


답변

가장 좋은 방법은 사용하는 것입니다 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()

매력처럼 작동합니다.

즐겨


답변

싱글 톤 인스턴스를 만들기 위해 juanjo의 답변 업데이트

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

}


답변

이것은 Swift 4.0에 있습니다.

이 프레임 워크 https://github.com/ashleymills/Reachability.swift
And Install Pod ..
In AppDelegate를 사용하고 있습니다.

var window: UIWindow?
var reachability = InternetReachability()!
var reachabilityViewController : UIViewController? = nil

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.

    reachabilityChecking()
    return true
}

extension AppDelegate {

func reachabilityChecking() {
    reachability.whenReachable = { reachability in
        DispatchQueue.main.async {
            print("Internet is OK!")
            if reachability.connection != .none && self.reachabilityViewController != nil {

            }
        }
    }
    reachability.whenUnreachable = { _ in
        DispatchQueue.main.async {
            print("Internet connection FAILED!")
            let storyboard = UIStoryboard(name: "Reachability", bundle: Bundle.main)
            self.reachabilityViewController = storyboard.instantiateViewController(withIdentifier: "ReachabilityViewController")
            let rootVC = self.window?.rootViewController
            rootVC?.present(self.reachabilityViewController!, animated: true, completion: nil)
        }
    }
    do {
        try reachability.startNotifier()
    } catch {
        print("Could not start notifier")
    }
}
}

인터넷이 없으면 reachabilityViewController 화면이 나타납니다.


답변