Как использовать SCNetworkReachability в Swift
Я пытаюсь преобразовать этой фрагмент кода в 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 документ при взаимодействии с API C в 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'. Как вам пройти NULL в Swift?
также я изменил эту строку и ошибка пропала.
let didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags)
EDIT 2: сдал nil в этой строке после просмотра этого вопроса. Но это ответ противоречит с ответом здесь. Он говорит, что нет эквивалента NULL в Swift.
var defaultRouteReachability: SCNetworkReachabilityRef = SCNetworkReachabilityCreateWithAddress(nil, &zeroAddress)
в любом случае я получаю новую ошибку говорит 'sockaddr_in' не идентичен 'sockaddr' в строке выше.
5 ответов:
(этот ответ был неоднократно расширен из-за изменений в языке 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()) }) 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()преобразование необходимо, потому что эта функция ожидает указатель на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>, что означает, что ты должен ... передай адрес переменной flags.обратите внимание также, что регистрация обратного вызова уведомителя возможна Swift 2, сравните работа с API C от Swift и Swift 2-UnsafeMutablePointer
to object .
обновление для Swift 3/4:
небезопасные указатели не могут быть просто преобразованы в указатель a другой тип больше (см. - 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, { .withMemoryRebound(to: sockaddr.self, capacity: 1) { SCNetworkReachabilityCreateWithAddress(nil, ) } }) 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
на основе ответа Мартина 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, { .withMemoryRebound(to: sockaddr.self, capacity: 1) { SCNetworkReachabilityCreateWithAddress(nil, ) } }) } 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, { .withMemoryRebound(to: sockaddr.self, capacity: 1) { SCNetworkReachabilityCreateWithAddress(nil, ) } }) }
Это не имеет ничего общего с Swift, но лучшим решением является не использовать достижимость, чтобы определить, является ли сеть в сети. Просто сделайте ваше соединение и обрабатывать ошибки, если это не удается. Создание соединения может время от времени запускать бездействующие автономные радиостанции.
единственное допустимое использование достижимости - использовать его для уведомления вас, когда сеть переходит из автономного режима в онлайн. В этот момент Вы должны повторить неудачные подключения.
лучшее решение-использовать
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, { .withMemoryRebound(to: sockaddr.self, capacity: 1) { SCNetworkReachabilityCreateWithAddress(nil, ) } }) } 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, { .withMemoryRebound(to: sockaddr.self, capacity: 1) { SCNetworkReachabilityCreateWithAddress(nil, ) } }) } }использование
if Reachability.shared.isConnectedToNetwork(){ }
Comments