this コードスニペットを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;
)で、それらはstruct sockaddr_in(?)からzeroAddress
というインスタンスを定義していると思います。このようにvar
を宣言してみました。
var zeroAddress = sockaddr_in()
しかし、エラーが発生します。callのパラメーター '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の公式 document SwiftのC APIとのやり取りについて読んでいますが、構造体を扱う例はありません。
誰も私をここで助けてくれますか?本当にありがたいです。
ありがとうございました。
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」に変換できません。 SwiftでNULL
を渡す方法は?
また、この行を変更すると、エラーはなくなりました。
let didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags)
EDIT 2:this の質問を見た後、この行にnil
を渡しました。しかし、その答えは答えと矛盾します here 。 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のデフォルトの初期化子があります。これにより、構造体のすべてのフィールドがゼロに初期化され、ソケットのアドレス構造は
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_in
ではなくsockaddr
へのポインターを予期しているためです。
withUnsafePointer()
から返される値は、SCNetworkReachabilityCreateWithAddress()
からの戻り値であり、タイプはSCNetworkReachability?
です。つまり、オプションです。 guard let
ステートメント(Swift 2.0の新機能)は、defaultRouteReachability
でない場合、nil
変数にラップ解除された値を割り当てます。それ以外の場合、else
ブロックが実行され、関数が戻ります。
SCNetworkReachabilityCreateWithAddress()
は管理対象オブジェクトを返します。明示的にリリースする必要はありません。Swift 2の時点で、SCNetworkReachabilityFlags
は、セットのようなインターフェースを持つOptionSetType
に準拠しています。空のフラグ変数を作成するには
var flags : SCNetworkReachabilityFlags = []
フラグをチェックします
let isReachable = flags.contains(.Reachable)
let needsConnection = flags.contains(.ConnectionRequired)
SCNetworkReachabilityGetFlags
の2番目のパラメーターのタイプはUnsafeMutablePointer<SCNetworkReachabilityFlags>
です。つまり、flags変数のaddressを渡す必要があります。
また、Swift 2の時点でノーティファイアコールバックの登録が可能であることに注意してください。 SwiftのC APIの操作 と Swift 2-UnsafeMutablePointer <Void> to 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)
}
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)
}
})
}
これはSwiftとは関係ありませんが、最善の解決策は、ネットワークがオンラインかどうかを判断するためにReachabilityを使用しないことです。接続を確立し、失敗した場合はエラーを処理するだけです。接続を確立すると、休止状態のオフライン無線が起動することがあります。
Reachabilityの有効な使用法の1つは、ネットワークがオフラインからオンラインに移行したときに通知するために使用することです。その時点で、失敗した接続を再試行する必要があります。
最良の解決策は、Swift 2
で記述されたReachabilitySwift
class を使用し、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
そしてPodをインストールします..
InAppDelegate
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画面が表示されます