diff --git a/libnymea-app/connection/networkreachabilitymonitor.cpp b/libnymea-app/connection/networkreachabilitymonitor.cpp index b79440b2..0d57bc35 100644 --- a/libnymea-app/connection/networkreachabilitymonitor.cpp +++ b/libnymea-app/connection/networkreachabilitymonitor.cpp @@ -9,6 +9,10 @@ NetworkReachabilityMonitor::NetworkReachabilityMonitor(QObject *parent) : QObject{parent} { +#if defined(Q_OS_IOS) + setupIOS(); +#endif + m_networkConfigManager = new QNetworkConfigurationManager(this); QObject::connect(m_networkConfigManager, &QNetworkConfigurationManager::configurationAdded, this, [this](const QNetworkConfiguration &config){ @@ -40,18 +44,16 @@ NymeaConnection::BearerTypes NetworkReachabilityMonitor::availableBearerTypes() void NetworkReachabilityMonitor::updateActiveBearers() { + // NOTE: The Qt API is not working at all on iOS, we're using the iOS reachability API instead. +#if defined(Q_OS_IOS) + return; +#endif + NymeaConnection::BearerTypes availableBearerTypes; QList configs = m_networkConfigManager->allConfigurations(QNetworkConfiguration::Active); qCDebug(dcNymeaConnection()) << "Network configuations:" << configs.count(); foreach (const QNetworkConfiguration &config, configs) { qCDebug(dcNymeaConnection()) << "Active network config:" << config.name() << config.bearerTypeFamily() << config.bearerTypeName(); - - // NOTE: iOS doesn't correctly report bearer types. It'll be Unknown all the time. Let's hardcode it to WiFi for that... -#if defined(Q_OS_IOS) - availableBearerTypes.setFlag(NymeaConnection::BearerTypeWiFi); -#else - availableBearerTypes.setFlag(qBearerTypeToNymeaBearerType(config.bearerType())); -#endif } if (availableBearerTypes == NymeaConnection::BearerTypeNone) { // This is just debug info... On some platform bearer management seems a bit broken, so let's get some infos right away... @@ -76,7 +78,7 @@ void NetworkReachabilityMonitor::updateActiveBearers() emit availableBearerTypesUpdated(); } -NymeaConnection::BearerType NetworkReachabilityMonitor::qBearerTypeToNymeaBearerType(QNetworkConfiguration::BearerType type) const +NymeaConnection::BearerType NetworkReachabilityMonitor::qBearerTypeToNymeaBearerType(QNetworkConfiguration::BearerType type) { switch (type) { case QNetworkConfiguration::BearerUnknown: diff --git a/libnymea-app/connection/networkreachabilitymonitor.h b/libnymea-app/connection/networkreachabilitymonitor.h index 583864b9..95a526ca 100644 --- a/libnymea-app/connection/networkreachabilitymonitor.h +++ b/libnymea-app/connection/networkreachabilitymonitor.h @@ -6,6 +6,10 @@ #include "nymeaconnection.h" +#ifdef Q_OS_IOS +#import +#endif + class NetworkReachabilityMonitor : public QObject { Q_OBJECT @@ -22,13 +26,18 @@ signals: private slots: void updateActiveBearers(); -private: - NymeaConnection::BearerType qBearerTypeToNymeaBearerType(QNetworkConfiguration::BearerType type) const; - private: QNetworkConfigurationManager *m_networkConfigManager = nullptr; NymeaConnection::BearerTypes m_availableBearerTypes = NymeaConnection::BearerTypeNone; + static NymeaConnection::BearerType qBearerTypeToNymeaBearerType(QNetworkConfiguration::BearerType type); + +#ifdef Q_OS_IOS + void setupIOS(); + SCNetworkReachabilityRef _reachabilityRef; + static NymeaConnection::BearerType flagsToBearerType(SCNetworkReachabilityFlags flags); + static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info); +#endif }; #endif // NETWORKREACHABILITYMONITOR_H diff --git a/libnymea-app/connection/networkreachabilitymonitorios.mm b/libnymea-app/connection/networkreachabilitymonitorios.mm new file mode 100644 index 00000000..a0c4f6c7 --- /dev/null +++ b/libnymea-app/connection/networkreachabilitymonitorios.mm @@ -0,0 +1,103 @@ +#include "networkreachabilitymonitor.h" + +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#include + +void NetworkReachabilityMonitor::ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) +{ +#pragma unused (target, flags) + NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback"); + + NetworkReachabilityMonitor* thiz = (__bridge NetworkReachabilityMonitor *)info; + // Post a notification to notify the client that the network reachability changed. + qCritical() << "******* network reachability changed"; + NymeaConnection::BearerTypes old = thiz->m_availableBearerTypes; + thiz->m_availableBearerTypes = flagsToBearerType(flags); + if (thiz->m_availableBearerTypes != old) { + emit thiz->availableBearerTypesChanged(); + } + emit thiz->availableBearerTypesUpdated(); +} + +void NetworkReachabilityMonitor::setupIOS() +{ + struct sockaddr_in zeroAddress; + bzero(&zeroAddress, sizeof(zeroAddress)); + zeroAddress.sin_len = sizeof(zeroAddress); + zeroAddress.sin_family = AF_INET; + + SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)&zeroAddress); + if (reachability != NULL) { + _reachabilityRef = reachability; + } + + SCNetworkReachabilityContext context = {0, (__bridge void *)(this), NULL, NULL, NULL}; + qCritical() << "Registering callback"; + if (SCNetworkReachabilitySetCallback(_reachabilityRef, ReachabilityCallback, &context)) { + qCritical() << "Callback registered"; + if (SCNetworkReachabilityScheduleWithRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) { + qCritical() << "******* reachability callback set up"; + } else { + qCritical() << "******** Error setting up reachability callback"; + } + } + + + SCNetworkReachabilityFlags flags; + + if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags)) { + m_availableBearerTypes = flagsToBearerType(flags); + } + + // TODO: unregister +// SCNetworkReachabilityUnscheduleFromRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + +} + +NymeaConnection::BearerType NetworkReachabilityMonitor::flagsToBearerType(SCNetworkReachabilityFlags flags) +{ + if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) { + return NymeaConnection::BearerTypeNone; + } + + if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0) { + /* + If the target host is reachable and no connection is required then we'll assume (for now) that you're on Wi-Fi... + */ + return NymeaConnection::BearerTypeWiFi; + } + + if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || + (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)) { + /* + ... and the connection is on-demand (or on-traffic) if the calling application is using the CFSocketStream or higher APIs... + */ + + if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0) + { + /* + ... and no [user] intervention is needed... + */ + return NymeaConnection::BearerTypeWiFi; + } + } + + if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN) + { + /* + ... but WWAN connections are OK if the calling application is using the CFNetwork APIs. + */ + return NymeaConnection::BearerTypeMobileData; + } + + return NymeaConnection::BearerTypeNone; +} diff --git a/libnymea-app/connection/nymeaconnection.cpp b/libnymea-app/connection/nymeaconnection.cpp index 24f32789..fe7d327f 100644 --- a/libnymea-app/connection/nymeaconnection.cpp +++ b/libnymea-app/connection/nymeaconnection.cpp @@ -55,6 +55,14 @@ NymeaConnection::NymeaConnection(QObject *parent) : QObject(parent) connect(m_networkReachabilityMonitor, &NetworkReachabilityMonitor::availableBearerTypesChanged, this, &NymeaConnection::availableBearerTypesChanged); connect(m_networkReachabilityMonitor, &NetworkReachabilityMonitor::availableBearerTypesUpdated, this, &NymeaConnection::onAvailableBearerTypesUpdated); +#ifdef Q_OS_IOS + connect(m_networkReachabilityMonitor, &NetworkReachabilityMonitor::availableBearerTypesChanged, this, [this](){ + if (m_currentTransport) { + m_currentTransport->disconnect(); + } + }); +#endif + QGuiApplication *app = static_cast(QGuiApplication::instance()); QObject::connect(app, &QGuiApplication::applicationStateChanged, this, [app, this](Qt::ApplicationState state) { qCDebug(dcNymeaConnection()) << "Application state changed to:" << state; diff --git a/libnymea-app/libnymea-app.pri b/libnymea-app/libnymea-app.pri index 85b31501..02fd0c2b 100644 --- a/libnymea-app/libnymea-app.pri +++ b/libnymea-app/libnymea-app.pri @@ -358,3 +358,7 @@ ubports: { android: { DESTDIR = $${ANDROID_TARGET_ARCH} } + +ios: { + OBJECTIVE_SOURCES += $${PWD}/connection/networkreachabilitymonitorios.mm +}