programing

Swift에서의 SCNetwork Reachability 사용방법

prostudy 2022. 7. 31. 21:17
반응형

Swift에서의 SCNetwork Reachability 사용방법

이 코드 조각을 스위프트로 변환하려고 합니다.나는 몇 가지 어려움 때문에 출발하는 데 어려움을 겪고 있다.

- (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 argument라는 오류가 발생하는데, 이 오류는 구조체에 다수의 인수가 있기 때문에 이해할 수 있습니다.그래서 나는 다시 시도했다.

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

예상대로 변수가 초기값 내에서 사용된 다른 오류가 나타납니다.저도 그 오류의 원인을 이해합니다.C에서는 먼저 인스턴스를 선언한 후 파라미터를 채웁니다.내가 알기로는 스위프트에서는 가능하지 않아.그래서 나는 이 시점에서 무엇을 해야 할지 정말 모르겠다.

Swift에서 C API와 상호작용하는 것에 대한 애플의 공식 문서를 읽었지만 구조물과 관련된 작업 예는 없습니다.

누가 나 좀 도와줄래?그렇게 해주시면 정말 감사하겠습니다.

감사합니다.


업데이트: Martin 덕분에 초기 문제를 극복할 수 있었습니다.하지만 스위프트가 날 더 쉽게 만들진 않아새로운 에러가 여러 개 표시됩니다.

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'는 'CFAlocator'변환할 수 없다는 것입니다.패스하는 방법NULL스프????

또한 이 라인을 변경했더니 오류가 사라졌습니다.

let didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags)

편집 2: 합격했습니다.nil이 질문을 보고 줄을 서세요.하지만 그 대답은 여기의 대답과 모순된다.에 상당하는 것은 없다고 되어 있다.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)에서 Import된 C 구조체는 Swift에서 기본 이니셜라이저를 가지고 있으며, Swift는 구조체의 모든 필드를 0으로 초기화하기 때문에 소켓주소 구조를 초기화할 수 있습니다.

    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스테이트먼트(Swift 2.0의 새로운 기능)에 의해 언랩된 값이defaultRouteReachability변수(없을 경우)nil그렇지 않으면else블록이 실행되고 함수가 반환됩니다.

  • Swift 2를 기준으로SCNetworkReachabilityCreateWithAddress()관리 대상 개체를 반환합니다.명시적으로 공개할 필요는 없습니다.
  • Swift 2를 기준으로SCNetworkReachabilityFlags준거하다OptionSetType세트와 같은 인터페이스를 갖추고 있습니다.빈 플래그 변수를 작성하려면

    var flags : SCNetworkReachabilityFlags = []
    

    플래그를 체크합니다.

    let isReachable = flags.contains(.Reachable)
    let needsConnection = flags.contains(.ConnectionRequired)
    
  • 의 두 번째 파라미터SCNetworkReachabilityGetFlags타입이 있다UnsafeMutablePointer<SCNetworkReachabilityFlags>즉, 플래그 변수의 주소를 전달해야 합니다.

또한 Swift 2부터는 알림 콜백을 등록할 수 있습니다.Swift 및 Swift 2의 C API를 사용한 작업 - UnsecureMutablePointer <Void>를 오브젝트비교합니다.


Swift 3/4 업데이트:

안전하지 않은 포인터는 더 이상 다른 유형의 포인터로 변환할 수 없습니다(- SE-0107 안전하지 않은 RawPointer 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)
}

Swift 5, 사용NWPathMonitor

import Network

func configureNetworkMonitor(){
        let monitor = NWPathMonitor()
        
        monitor.pathUpdateHandler = { path in
            
            if path.status != .satisfied {
                print("not connected")
            }
            else if path.usesInterfaceType(.cellular) {
                print("Cellular")
            }
            else if path.usesInterfaceType(.wifi) {
                print("WIFI")
            }
            else if path.usesInterfaceType(.wiredEthernet) {
                print("Ethernet")
            }
            else if path.usesInterfaceType(.other){
                print("Other")
            }else if path.usesInterfaceType(.loopback){
                print("Loop Back")
            }
        }
        
        monitor.start(queue: DispatchQueue.global(qos: .background))
    }

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

SwiftUI는 위의 Mithra Sigam의 솔루션을 채택하고 있습니다.

import SwiftUI
import Network

class NetworkReachabilityManager: ObservableObject {
    @Published var networkPathStatus: NWPath.Status
    @Published var availableInterfaces: [NWInterface]
    
    let monitor = NWPathMonitor()
    
    init() {
        monitor.start(queue: DispatchQueue.global(qos: .background))
        
        let currentPath = monitor.currentPath
        
        networkPathStatus = currentPath.status
        availableInterfaces = currentPath.availableInterfaces
        
        monitor.pathUpdateHandler = { [self] networkPath in
            DispatchQueue.main.async {
                networkPathStatus = networkPath.status
                availableInterfaces = networkPath.availableInterfaces
            }
        }
    }
    
    deinit {
        monitor.cancel()
    }
}

이것은 Swift 4.0 입니다.

이 프레임워크를 사용하고 있습니다.https://github.com/ashleymills/Reachability.swift
팟을 설치합니다.
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 화면이 나타납니다.

이것은 Swift와는 관계가 없지만, 가장 좋은 해결책은 네트워크가 온라인 상태인지 아닌지를 판단하기 위해 Reachability를 사용하지 않는 것입니다.접속을 확립해, 에러가 발생했을 경우 대처해 주세요.접속을 확립하면 휴지 상태의 오프라인 무선이 기동하는 경우가 있습니다.

도달 가능성은 네트워크가 오프라인에서 온라인으로 이행했을 때 이를 사용하여 통지하는 것입니다.이 시점에서 실패한 연결을 다시 시도해야 합니다.

싱글톤 인스턴스를 생성하기 위해 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(){

}

가장 좋은 해결책은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()

마법처럼 일하는 것.

즐거운 시간 되세요.

언급URL : https://stackoverflow.com/questions/25623272/how-to-use-scnetworkreachability-in-swift

반응형