1. ホーム
  2. ios

[解決済み] SwiftでSCNetworkReachabilityを使用する方法

2022-11-24 09:37:35

質問

私は この のコードスニペットを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; というインスタンスを定義していると思います。 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)

予想通り、他のエラーが発生しました。 変数がそれ自身の初期値内で使用されています . このエラーの原因もわかった。C言語では、最初にインスタンスを宣言して、それからパラメータを埋めるのです。私の知る限りSwiftではありえないことです。なので、どうすればいいのか、この時点で本当に迷っています。

私はAppleの公式 ドキュメント を読みましたが、構造体を扱う例はありませんでした。

どなたか、ここで私を助けていただけませんか?私はそれを本当に感謝します。

ありがとうございます。


UPDATEです。 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
}


EDIT 1: なるほど、この行をこう変えてみました。

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

この行で新たに発生したエラーは 'UnsafePointer' は 'CFAllocator' に変換されません。 . どのようにあなたは NULL をどのように渡すのでしょうか?

また、この行を変更したところ、エラーがなくなりました。

let didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags)


EDIT 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) の時点で、インポートされた C 言語の構造体は Swift ではデフォルトの初期化子を持っており、構造体のすべてのフィールドを 0 に初期化するので、ソケットアドレス構造体は

    var zeroAddress = sockaddr_in()
    
    
  • sizeofValue() はこの構造体の大きさを表し、これを に変換する必要があります。 UInt8 に対して sin_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 ステートメント (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)
    
    
  • の第2パラメータは SCNetworkReachabilityGetFlags は、タイプ UnsafeMutablePointer<SCNetworkReachabilityFlags> であり、これは を渡すことになります。 アドレス を渡さなければならないことを意味します。

また、通知先コールバックの登録は Swift 2 では可能です。 SwiftからCのAPIを操作する Swift 2 - UnsafeMutablePointer<Void> をオブジェクトに変換する。 .


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