Как использовать 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;
}
Первый и главный вопрос, который у меня возникает, заключается в том, как определить и работать со структурами Си. В первой строке (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 они сначала объявляют экземпляр, а затем заполняют параметры. Насколько я знаю, в Свифте это невозможно. Так что я действительно потерял в этом вопросе, что делать.
Я прочитал официальный документ Apple по взаимодействию с C API в 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
в Свифте?
Также я изменил эту строку, и ошибка исчезла.
let didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags)
РЕДАКТИРОВАТЬ 2: я прошел nil
в этой строке, увидев этот вопрос. Но этот ответ противоречит ответу здесь. Он говорит, что нет эквивалента NULL
в Свифте.
var defaultRouteReachability: SCNetworkReachabilityRef = SCNetworkReachabilityCreateWithAddress(nil, &zeroAddress)
В любом случае я получаю новую ошибку, говоря, что 'sockaddr_in' не идентичен 'sockaddr' в приведенной выше строке.
8 ответов
(Этот ответ неоднократно расширялся из-за изменений в языке 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
неsockaddr_in
,Значение, возвращаемое из
withUnsafePointer()
это возвращаемое значение изSCNetworkReachabilityCreateWithAddress()
и это имеет типSCNetworkReachability?
т.е. это необязательно.guard let
оператор (новая функция в Swift 2.0) назначает развернутое значениеdefaultRouteReachability
переменная, если это не такnil
, В противном случаеelse
блок выполняется и функция возвращается.- Начиная с Swift 2,
SCNetworkReachabilityCreateWithAddress()
возвращает управляемый объект. Вы не должны выпустить это явно. Начиная с Swift 2,
SCNetworkReachabilityFlags
соответствуетOptionSetType
который имеет подобный множеству интерфейс. Вы создаете пустую переменную flagsvar flags : SCNetworkReachabilityFlags = []
и проверьте флаги с
let isReachable = flags.contains(.Reachable) let needsConnection = flags.contains(.ConnectionRequired)
Второй параметр
SCNetworkReachabilityGetFlags
имеет типUnsafeMutablePointer<SCNetworkReachabilityFlags>
, что означает, что вы должны передать адрес переменной flags.
Также обратите внимание, что регистрация обратного вызова уведомителя возможна с Swift 2, сравните Работу с C API от Swift и Swift 2 - UnsafeMutablePointer
Обновление для Swift 3/4:
Небезопасные указатели больше нельзя просто преобразовать в указатель другого типа (см. SE-0107 API UnsafeRawPointer). Вот обновленный код:
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
Основываясь на ответе Мартина Р:
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()
Работать как шарм.
наслаждаться
SwiftUI использует решение Mithra Sigam, описанное выше:
import Foundation
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
networkPathStatus = networkPath.status
availableInterfaces = networkPath.availableInterfaces
}
}
deinit {
monitor.cancel()
}
}
Обновленный ответ дзюньо на создание экземпляра синглтона
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 ..
В 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")
}
}
}
Экран появится, если нет интернета