Source/TNLCommunicationAgent.h (132 lines of code) (raw):

// // TNLCommunicationAgent.h // TwitterNetworkLayer // // Created on 5/2/16. // Copyright © 2020 Twitter. All rights reserved. // #import <Foundation/Foundation.h> #if !TARGET_OS_WATCH // no communication agent for watchOS /** `TNLNetworkReachabilityFlags` are the same as `SCNetworkReachability` on iOS 11 and below. On iOS 12+, they are a different set of flags matching `TNLNetworkReachabilityMask` options. */ typedef uint32_t TNLNetworkReachabilityFlags; NS_ASSUME_NONNULL_BEGIN @protocol TNLCarrierInfo; @protocol TNLCommunicationAgentObserver; /** Mask that is in place for `TNLNetworkReachabilityFlags` on iOS 12+ */ typedef NS_OPTIONS(uint32_t, TNLNetworkReachabilityMask) { TNLNetworkReachabilityMaskNone = 0, TNLNetworkReachabilityMaskPathStatusSatisfied = 1 << 0, TNLNetworkReachabilityMaskPathStatusUnsatisfied = 1 << 1, TNLNetworkReachabilityMaskPathStatusSatisfiable = 1 << 2, // 0 path status == invalid TNLNetworkReachabilityMaskPathIntefaceTypeOther = (1 << 8) << 0, TNLNetworkReachabilityMaskPathIntefaceTypeWifi = (1 << 8) << 1, TNLNetworkReachabilityMaskPathIntefaceTypeCellular = (1 << 8) << 2, TNLNetworkReachabilityMaskPathIntefaceTypeWired = (1 << 8) << 3, TNLNetworkReachabilityMaskPathIntefaceTypeLoopback = (1 << 8) << 4, // 0 path interface type == none TNLNetworkReachabilityMaskPathConditionExpensive API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0)) = (1 << 16) << 0, TNLNetworkReachabilityMaskPathConditionConstrained API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0)) = (1 << 16) << 1, // 0 path condition == no condition } API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0)); /** Enum of reachability statuses from the `TNLCommunicationAgent` */ typedef NS_ENUM(NSInteger, TNLNetworkReachabilityStatus) { /** Not yet determined reachability */ TNLNetworkReachabilityUndetermined = -1, /** Unreachable */ TNLNetworkReachabilityNotReachable = 0, /** reachable via 802.3 Ethernet or 802.11 WiFi */ TNLNetworkReachabilityReachableViaEthernet = 1, /** reachabile via WWAN (cellular) */ TNLNetworkReachabilityReachableViaWWAN = 2, /** deprecated, use `TNLNetworkReachabilityReachableViaEthernet` instead */ TNLNetworkReachabilityReachableViaWiFi __attribute__((deprecated)) = TNLNetworkReachabilityReachableViaEthernet, }; //! Convert a `TNLNetworkReachabilityStatus` to an `NSString` FOUNDATION_EXTERN NSString * __nonnull TNLNetworkReachabilityStatusToString(TNLNetworkReachabilityStatus status); /** Enum of captive portal status from the `TNLCommunicationAgent` */ typedef NS_ENUM(NSInteger, TNLCaptivePortalStatus) { /** not yet determined captive portal status */ TNLCaptivePortalStatusUndetermined = -1, /** captive portal is not apparent */ TNLCaptivePortalStatusNoCaptivePortal = 0, /** captive portal was detected */ TNLCaptivePortalStatusCaptivePortalDetected = 1, /** captive portal cannot be detected due to ATS rules. In order to support captive portal detection, an ATS exception must be made for connectivitycheck.gstatic.com Update your `Info.plist` file to have an exception for connectivitycheck.gstatic.com like this: <key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <false/> <key>NSExceptionDomains</key> <dict> <key>connectivitycheck.gstatic.com</key> <dict> <key>NSExceptionAllowsInsecureHTTPLoads</key> <true/> </dict> </dict> </dict> */ TNLCaptivePortalStatusDetectionBlockedByAppTransportSecurity = -100, }; //! Convert a `TNLCaptivePortalStatus` to an `NSString` FOUNDATION_EXTERN NSString * __nonnull TNLCaptivePortalStatusToString(TNLCaptivePortalStatus status); /** An enum of known radio access technologies. See `CTRadioAccessTechnology` constants in `CTTelephonyNetworkInfo.h` */ typedef NS_ENUM(NSInteger, TNLWWANRadioAccessTechnologyValue) { /** Unknown radio access tech */ TNLWWANRadioAccessTechnologyValueUnknown = 0, #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST /** 2G, `CTRadioAccessTechnologyGPRS` */ TNLWWANRadioAccessTechnologyValueGPRS = 1, /** 2G, `CTRadioAccessTechnologyEdge` */ TNLWWANRadioAccessTechnologyValueEDGE = 2, /** 3G, `CTRadioAccessTechnologyWCDMA` */ TNLWWANRadioAccessTechnologyValueUMTS = 3, /** 3G, `CTRadioAccessTechnologyHSDPA` */ TNLWWANRadioAccessTechnologyValueHSDPA = 4, /** 3G, `CTRadioAccessTechnologyHSUPA` */ TNLWWANRadioAccessTechnologyValueHSUPA = 5, /** 3G, Not defined in `CTTelephonyNetworkInfo.h` */ TNLWWANRadioAccessTechnologyValueHSPA = 6, /** 2G, Not defined in `CTTelephonyNetworkInfo.h` */ TNLWWANRadioAccessTechnologyValueCDMA = 7, /** 1G, `CTRadioAccessTechnologyCDMAEVDORev0` */ TNLWWANRadioAccessTechnologyValueEVDO_0 = 8, /** 3G, `CTRadioAccessTechnologyCDMAEVDORevA` */ TNLWWANRadioAccessTechnologyValueEVDO_A = 9, /** 3G, `CTRadioAccessTechnologyCDMAEVDORevB` */ TNLWWANRadioAccessTechnologyValueEVDO_B = 10, /** 1G, `CTRadioAccessTechnologyCDMA1x` */ TNLWWANRadioAccessTechnologyValue1xRTT = 11, /** 2G, Not defined in `CTTelephonyNetworkInfo.h` */ TNLWWANRadioAccessTechnologyValueIDEN = 12, /** 4G, `CTRadioAccessTechnologyLTE` */ TNLWWANRadioAccessTechnologyValueLTE = 13, /** 4G, `CTRadioAccessTechnologyeHRPD` */ TNLWWANRadioAccessTechnologyValueEHRPD = 14, /** 4G, Not defined in `CTTelephonyNetworkInfo.h` */ TNLWWANRadioAccessTechnologyValueHSPAP = 15 #endif // #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST }; //! Convert a WWAN radio access technololgy `NSString` into a `TNLWWANRadioAccessTechnologyValue` FOUNDATION_EXTERN TNLWWANRadioAccessTechnologyValue TNLWWANRadioAccessTechnologyValueFromString(NSString * __nullable WWANTechString); //! Convert a `TNLWWANRadioAccessTechnologyValue` into an `NSString` FOUNDATION_EXTERN NSString * __nonnull TNLWWANRadioAccessTechnologyValueToString(TNLWWANRadioAccessTechnologyValue value); /** Enum for the generation of a radio access technology */ typedef NS_ENUM(NSInteger, TNLWWANRadioAccessGeneration) { /** Unknown */ TNLWWANRadioAccessGenerationUnknown = 0, /** 1G */ TNLWWANRadioAccessGeneration1G = 1, /** 2G */ TNLWWANRadioAccessGeneration2G = 2, /** 3G */ TNLWWANRadioAccessGeneration3G = 3, /** 4G */ TNLWWANRadioAccessGeneration4G = 4 }; //! Determine the `TNLWWANRadioAccessGeneration` from a `TNLWWANRadioAccessTechnologyValue` FOUNDATION_EXTERN TNLWWANRadioAccessGeneration TNLWWANRadioAccessGenerationForTechnologyValue(TNLWWANRadioAccessTechnologyValue value) __attribute__((const)); //! String to break TNLNetworkReachabilityFlags into a string of flags - for debug purposes only. Has different outputs for iOS 11- and iOS 12+. FOUNDATION_EXTERN NSString *TNLDebugStringFromNetworkReachabilityFlags(TNLNetworkReachabilityFlags flags); typedef void(^TNLCommunicationAgentIdentifyReachabilityCallback)(TNLNetworkReachabilityFlags flags, TNLNetworkReachabilityStatus status); typedef void(^TNLCommunicationAgentIdentifyCarrierInfoCallback)(id<TNLCarrierInfo> __nullable info); typedef void(^TNLCommunicationAgentIdentifyWWANRadioAccessTechnologyCallback)(NSString * __nullable info); typedef void(^TNLCommunicationAgentIdentifyCaptivePortalStatusCallback)(TNLCaptivePortalStatus status); /** An agent for observing and determing traits regarding communication, including: - network reachability - carrier info - radio info - captive portal status @warning There are known regressions since iOS 10 in SystemConfiguration for observing reachability. These issues range from reachability events not working at all on simulator to getting into a state where reachability returning does not trigger events. @warning As always with reachability APIs, do _NOT_ rely on the reachability state as a gate prevent triggering a network request. Always fire the networking request and use reachability as a helper signal, not a canonical source of truth. Seriously... don't block your app on reachability, that leads to nothing but pain. @note In order for captive portal status detection to work, an ATS exception must be made for connectivitycheck.gstatic.com to permit insecure HTTP requests. See `TNLCaptivePortalStatusDetectionBlockedByAppTransportSecurity` for more. */ @interface TNLCommunicationAgent : NSObject /** the network host for the agent */ @property (nonatomic, copy, readonly) NSString *host; /** if there is a cellular interface or not */ @property (class, nonatomic, readonly) BOOL hasCellularInterface; /** designated initializer */ - (instancetype)initWithInternetReachabilityHost:(NSString *)host NS_DESIGNATED_INITIALIZER; /** unavailable */ - (instancetype)init NS_UNAVAILABLE; /** unavailable */ + (instancetype)new NS_UNAVAILABLE; /** identify the reachability asynchronously (callback on main thread) */ - (void)identifyReachability:(TNLCommunicationAgentIdentifyReachabilityCallback)callback; /** identify the carrier info asynchronously (callback on main thread) */ - (void)identifyCarrierInfo:(TNLCommunicationAgentIdentifyCarrierInfoCallback)callback; /** identify the WWAN radio access technology asynchronously (callback on main thread) */ - (void)identifyWWANRadioAccessTechnology:(TNLCommunicationAgentIdentifyWWANRadioAccessTechnologyCallback)callback; /** identify the captive portal status (callback on main thread) */ - (void)identifyCaptivePortalStatus:(TNLCommunicationAgentIdentifyCaptivePortalStatusCallback)callback; /** add an observer for when communication traits update. _observer_ is held weakly. Callbacks will be made on the main thread. */ - (void)addObserver:(id<TNLCommunicationAgentObserver>)observer; /** explicitely remove an observer. */ - (void)removeObserver:(id<TNLCommunicationAgentObserver>)observer; @end /** _No LAN_ reachability ;P */ @interface TNLCommunicationAgent (LANReachability) /** unavailable */ @property (nonatomic, readonly) TNLNetworkReachabilityStatus cachedLANReachabilityStatus __attribute__((unavailable("LAN reachability is unavailable"))); /** unavailable */ - (void)identifyLANReachability:(TNLCommunicationAgentIdentifyReachabilityCallback)callback __attribute__((unavailable("LAN reachability is unavailable"))); @end /** Category for accessing cached properties. It is recommended to use either a `TNLCommunicationAgentObserver` or one of the `identify` methods instead, though. */ @interface TNLCommunicationAgent (CachedProperties) /** cached reachability status */ @property (atomic, readonly) TNLNetworkReachabilityStatus currentReachabilityStatus; /** cached reachability flags */ @property (atomic, readonly) TNLNetworkReachabilityFlags currentReachabilityFlags; /** cached radio access technology. Note: `nil` for macOS and UIKit for Mac */ @property (atomic, copy, readonly, nullable) NSString *currentWWANRadioAccessTechnology; /** cached captive portal status */ @property (atomic, readonly) TNLCaptivePortalStatus currentCaptivePortalStatus; /** cached carrier info. Note: `nil` for macOS and UIKit for Mac as there is no cellular carrier information */ @property (atomic, readonly, nullable) id<TNLCarrierInfo> currentCarrierInfo; // or use `synchronousCarrierInfo`, which is more robust but can be slower @end /** Unsafe category */ @interface TNLCommunicationAgent (UnsafeSynchronousAccess) /** access the carrier info synchronously */ - (nullable id<TNLCarrierInfo>)synchronousCarrierInfo __attribute__((deprecated("should not access carrier info synchronously! Use identifyCarrierInfo: instead"))); @end /** protocol for observer of communication traits via `TNLCommunicationAgent` */ @protocol TNLCommunicationAgentObserver <NSObject> @optional /** called when the oberver is registered with the initial trait values at the time of registration @parameter communicationAgent related comminication agent for registration @parameter flags Reachability configuration flags that were registered @parameter status current network reachability status @parameter carrierInfo Any available carrier information. Note: This is `nil` in macOS as there is no cellular carrier involved. @parameter WWANRadioAccessTechnology GPRS, EDGE, LTE, etc. Note: This is `nil` in macOS as there is no cellular carrier involved. @parameter captivePortalStatus the `TNLCaptivePortalStatus` upon registration of an observer */ - (void)tnl_communicationAgent:(TNLCommunicationAgent *)agent didRegisterObserverWithInitialReachabilityFlags:(TNLNetworkReachabilityFlags)flags status:(TNLNetworkReachabilityStatus)status carrierInfo:(nullable id<TNLCarrierInfo>)info WWANRadioAccessTechnology:(nullable NSString *)radioTech captivePortalStatus:(TNLCaptivePortalStatus)captivePortalStatus; /** called when reachability changes */ - (void)tnl_communicationAgent:(TNLCommunicationAgent *)agent didUpdateReachabilityFromPreviousFlags:(TNLNetworkReachabilityFlags)oldFlags previousStatus:(TNLNetworkReachabilityStatus)oldStatus toCurrentFlags:(TNLNetworkReachabilityFlags)newFlags currentStatus:(TNLNetworkReachabilityStatus)newStatus; /** called when carrier info changes @parameter communicationAgent related comminication agent for registration @parameter didUpdateCarrierFromPreviousInfo Existing carrier information if available. Note: This is `nil` in macOS as there is no cellular carrier involved. @parameter toCurrentInfo New carrier information if available. Note: This is `nil` in macOS as there is no cellular carrier involved. */ - (void)tnl_communicationAgent:(TNLCommunicationAgent *)agent didUpdateCarrierFromPreviousInfo:(nullable id<TNLCarrierInfo>)oldInfo toCurrentInfo:(nullable id<TNLCarrierInfo>)newInfo; /** called when WWAN radio access technology changes */ - (void)tnl_communicationAgent:(TNLCommunicationAgent *)agent didUpdateWWANRadioAccessTechnologyFromPreviousTech:(nullable NSString *)oldTech toCurrentTech:(nullable NSString *)newTech; /** called when captive portal status changes */ - (void)tnl_communicationAgent:(TNLCommunicationAgent *)agent didUpdateCaptivePortalStatusFromPreviousStatus:(TNLCaptivePortalStatus)oldStatus toCurrentStatus:(TNLCaptivePortalStatus)newStatus; @end /** Carrier info, matching `CTCarrier` */ @protocol TNLCarrierInfo <NSObject> /** An `NSString` containing the name of the subscriber's cellular service provider. */ @property (nonatomic, readonly, copy, nullable) NSString *carrierName; /** An `NSString` containing the mobile country code for the subscriber's cellular service provider, in its numeric representation */ @property (nonatomic, readonly, copy, nullable) NSString *mobileCountryCode; /** An `NSString` containing the mobile network code for the subscriber's cellular service provider, in its numeric representation */ @property (nonatomic, readonly, copy, nullable) NSString *mobileNetworkCode; /** Returns an `NSString` object that contains country code for the subscriber's cellular service provider, represented as an ISO 3166-1 country code string */ @property (nonatomic, readonly, copy, nullable) NSString* isoCountryCode; /** A `BOOL` value that is `YES` if this carrier allows VOIP calls to be made on its network, `NO` otherwise. */ @property (nonatomic, readonly) BOOL allowsVOIP; @end //! Convert a `TNLCarrierInfo` into a serializable dictionary, will contain `[NSNull null]` for `nil` properties FOUNDATION_EXTERN NSDictionary<NSString *, id> *TNLCarrierInfoToDictionaryDescription(id<TNLCarrierInfo> carrierInfo); NS_ASSUME_NONNULL_END #endif // !TARGET_OS_WATCH