mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2026-01-05 20:52:48 +00:00
Current network type detection
Small changes in Postbox API
This commit is contained in:
@@ -397,6 +397,12 @@
|
||||
D08774FE1E3E3A3500A97350 /* GlobalNotificationSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08774FD1E3E3A3500A97350 /* GlobalNotificationSettings.swift */; };
|
||||
D08984F22114B97400918162 /* ClearCloudDrafts.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08984F12114B97400918162 /* ClearCloudDrafts.swift */; };
|
||||
D08984F32114B97400918162 /* ClearCloudDrafts.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08984F12114B97400918162 /* ClearCloudDrafts.swift */; };
|
||||
D08984F521187ECA00918162 /* NetworkType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08984F421187ECA00918162 /* NetworkType.swift */; };
|
||||
D08984F621187ECA00918162 /* NetworkType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08984F421187ECA00918162 /* NetworkType.swift */; };
|
||||
D08984F92118816A00918162 /* Reachability.h in Headers */ = {isa = PBXBuildFile; fileRef = D08984F72118816900918162 /* Reachability.h */; };
|
||||
D08984FA2118816A00918162 /* Reachability.h in Headers */ = {isa = PBXBuildFile; fileRef = D08984F72118816900918162 /* Reachability.h */; };
|
||||
D08984FB2118816A00918162 /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = D08984F82118816A00918162 /* Reachability.m */; };
|
||||
D08984FC2118816A00918162 /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = D08984F82118816A00918162 /* Reachability.m */; };
|
||||
D08CAA7D1ED77EE90000FDA8 /* LocalizationSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08CAA7C1ED77EE90000FDA8 /* LocalizationSettings.swift */; };
|
||||
D08CAA7E1ED77EE90000FDA8 /* LocalizationSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08CAA7C1ED77EE90000FDA8 /* LocalizationSettings.swift */; };
|
||||
D08CAA801ED80ED20000FDA8 /* SuggestedLocalizationEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08CAA7F1ED80ED20000FDA8 /* SuggestedLocalizationEntry.swift */; };
|
||||
@@ -950,6 +956,9 @@
|
||||
D08774FB1E3E39F600A97350 /* ManagedGlobalNotificationSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedGlobalNotificationSettings.swift; sourceTree = "<group>"; };
|
||||
D08774FD1E3E3A3500A97350 /* GlobalNotificationSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlobalNotificationSettings.swift; sourceTree = "<group>"; };
|
||||
D08984F12114B97400918162 /* ClearCloudDrafts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClearCloudDrafts.swift; sourceTree = "<group>"; };
|
||||
D08984F421187ECA00918162 /* NetworkType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkType.swift; sourceTree = "<group>"; };
|
||||
D08984F72118816900918162 /* Reachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Reachability.h; sourceTree = "<group>"; };
|
||||
D08984F82118816A00918162 /* Reachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Reachability.m; sourceTree = "<group>"; };
|
||||
D08CAA7C1ED77EE90000FDA8 /* LocalizationSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalizationSettings.swift; sourceTree = "<group>"; };
|
||||
D08CAA7F1ED80ED20000FDA8 /* SuggestedLocalizationEntry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SuggestedLocalizationEntry.swift; sourceTree = "<group>"; };
|
||||
D08CAA831ED8164B0000FDA8 /* Localization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Localization.swift; sourceTree = "<group>"; };
|
||||
@@ -1489,6 +1498,7 @@
|
||||
D03B0D591D631A6900955575 /* Serialization.swift */,
|
||||
D06AFE8820BF66BF00EA5124 /* DeserializeFunctionResponse.swift */,
|
||||
D0F19F6520E6620D00EEC860 /* MultiplexedRequestManager.swift */,
|
||||
D08984F421187ECA00918162 /* NetworkType.swift */,
|
||||
);
|
||||
name = Network;
|
||||
sourceTree = "<group>";
|
||||
@@ -1564,6 +1574,8 @@
|
||||
D053B3FA1F1651FA00E2D58A /* MonotonicTime.m */,
|
||||
D02609BB20C6EB97006C34AC /* Crypto.h */,
|
||||
D02609BE20C6EC08006C34AC /* Crypto.m */,
|
||||
D08984F72118816900918162 /* Reachability.h */,
|
||||
D08984F82118816A00918162 /* Reachability.m */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
@@ -1878,6 +1890,7 @@
|
||||
D0B843B41DA7FF30005F29E1 /* NBMetadataCore.h in Headers */,
|
||||
D0B843C41DA7FF30005F29E1 /* NBPhoneNumber.h in Headers */,
|
||||
D0B843B31DA7FF30005F29E1 /* NBAsYouTypeFormatter.h in Headers */,
|
||||
D08984F92118816A00918162 /* Reachability.h in Headers */,
|
||||
D053B3FB1F1651FA00E2D58A /* MonotonicTime.h in Headers */,
|
||||
D0B843C81DA7FF30005F29E1 /* NBPhoneNumberDesc.h in Headers */,
|
||||
D0B843CA1DA7FF30005F29E1 /* NBPhoneNumberUtil.h in Headers */,
|
||||
@@ -1907,6 +1920,7 @@
|
||||
D050F2571E4A5AC500988324 /* NBMetadataCoreTestMapper.h in Headers */,
|
||||
D050F2591E4A5AC500988324 /* NBNumberFormat.h in Headers */,
|
||||
D050F25D1E4A5AC500988324 /* NBPhoneNumberDefines.h in Headers */,
|
||||
D08984FA2118816A00918162 /* Reachability.h in Headers */,
|
||||
D050F25B1E4A5AC500988324 /* NBPhoneMetaDataGenerator.h in Headers */,
|
||||
D050F2581E4A5AC500988324 /* NBMetadataHelper.h in Headers */,
|
||||
);
|
||||
@@ -2169,6 +2183,7 @@
|
||||
D053B3FE1F16534400E2D58A /* MonotonicTime.swift in Sources */,
|
||||
D0C50E341E93A86600F62E39 /* CallSessionManager.swift in Sources */,
|
||||
D00D34421E6EDD2E0057B307 /* ManagedSynchronizeConsumeMessageContentsOperations.swift in Sources */,
|
||||
D08984FB2118816A00918162 /* Reachability.m in Sources */,
|
||||
D0DA1D321F7043D50034E892 /* ManagedPendingPeerNotificationSettings.swift in Sources */,
|
||||
D099D7491EEF418D00A3128C /* HistoryViewChannelStateValidation.swift in Sources */,
|
||||
C23BC3871E9BE3CA00D79F92 /* ImportContact.swift in Sources */,
|
||||
@@ -2381,6 +2396,7 @@
|
||||
D0B843C51DA7FF30005F29E1 /* NBPhoneNumber.m in Sources */,
|
||||
D03B0D0D1D62255C00955575 /* SynchronizePeerReadState.swift in Sources */,
|
||||
D03B0D081D62255C00955575 /* ChannelState.swift in Sources */,
|
||||
D08984F521187ECA00918162 /* NetworkType.swift in Sources */,
|
||||
C251D7431E65E50500283EDE /* StickerSetInstallation.swift in Sources */,
|
||||
D07047BA1F3DF75500F6A8D4 /* ConsumePersonalMessageAction.swift in Sources */,
|
||||
D07E413F208A769D00FCA8F0 /* ProxyServersStatuses.swift in Sources */,
|
||||
@@ -2424,6 +2440,7 @@
|
||||
D033FEB11E61EB0200644997 /* PeerReportStatus.swift in Sources */,
|
||||
D0458C891E69B4AB00FB34C1 /* OutgoingContentInfoMessageAttribute.swift in Sources */,
|
||||
D050F26C1E4A5B6D00988324 /* UpdatePeers.swift in Sources */,
|
||||
D08984F621187ECA00918162 /* NetworkType.swift in Sources */,
|
||||
D050F26D1E4A5B6D00988324 /* CreateGroup.swift in Sources */,
|
||||
D0575AF51E9FFDDE006F2541 /* ManagedSynchronizeSavedGifsOperations.swift in Sources */,
|
||||
D00D34461E6EDD420057B307 /* SynchronizeConsumeMessageContentsOperation.swift in Sources */,
|
||||
@@ -2653,6 +2670,7 @@
|
||||
D0561DEB1E5754FA00E6B9E9 /* ChannelAdmins.swift in Sources */,
|
||||
D0AD02E41FFFA14800C1DCFF /* PeerLiveLocationsContext.swift in Sources */,
|
||||
D0613FCB1E60440600202CDB /* InvitationLinks.swift in Sources */,
|
||||
D08984FC2118816A00918162 /* Reachability.m in Sources */,
|
||||
D0B844471DAB91FD005F29E1 /* ManagedServiceViews.swift in Sources */,
|
||||
D0F3A8A91E82CD7D00B4C64C /* UpdatePeerChatInterfaceState.swift in Sources */,
|
||||
D00D343A1E6EC9520057B307 /* TeleramMediaUnsupported.swift in Sources */,
|
||||
|
||||
@@ -791,6 +791,11 @@ public class Account {
|
||||
return self.networkStateValue.get()
|
||||
}
|
||||
|
||||
private let networkTypeValue = Promise<NetworkType>()
|
||||
public var networkType: Signal<NetworkType, NoError> {
|
||||
return self.networkTypeValue.get()
|
||||
}
|
||||
|
||||
private let _loggedOut = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||
public var loggedOut: Signal<Bool, NoError> {
|
||||
return self._loggedOut.get()
|
||||
@@ -901,6 +906,8 @@ public class Account {
|
||||
}
|
||||
self.networkStateValue.set(networkStateSignal |> distinctUntilChanged)
|
||||
|
||||
self.networkTypeValue.set(currentNetworkType())
|
||||
|
||||
let appliedNotificationToken = self.notificationToken.get()
|
||||
|> distinctUntilChanged
|
||||
|> mapToSignal { token -> Signal<Void, NoError> in
|
||||
|
||||
@@ -205,7 +205,7 @@ func accountStateReset(postbox: Postbox, network: Network) -> Signal<Void, NoErr
|
||||
for (_, messageId) in topMesageIds {
|
||||
if messageId.id > 1 {
|
||||
var skipHole = false
|
||||
if let localTopId = transaction.getTopMesssageIndex(peerId: messageId.peerId, namespace: messageId.namespace)?.id {
|
||||
if let localTopId = transaction.getTopPeerMessageIndex(peerId: messageId.peerId, namespace: messageId.namespace)?.id {
|
||||
if localTopId >= messageId {
|
||||
skipHole = true
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ public func clearHistoryInteractively(postbox: Postbox, peerId: PeerId) -> Signa
|
||||
return postbox.transaction { transaction -> Void in
|
||||
if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.CloudGroup || peerId.namespace == Namespaces.Peer.CloudChannel {
|
||||
var topTimestamp: Int32?
|
||||
if let topIndex = transaction.getTopMesssageIndex(peerId: peerId, namespace: Namespaces.Message.Cloud) {
|
||||
if let topIndex = transaction.getTopPeerMessageIndex(peerId: peerId, namespace: Namespaces.Message.Cloud) {
|
||||
topTimestamp = topIndex.timestamp
|
||||
}
|
||||
cloudChatAddClearHistoryOperation(transaction: transaction, peerId: peerId, explicitTopMessageId: nil)
|
||||
|
||||
254
TelegramCore/NetworkType.swift
Normal file
254
TelegramCore/NetworkType.swift
Normal file
@@ -0,0 +1,254 @@
|
||||
import Foundation
|
||||
#if os(macOS)
|
||||
import SwiftSignalKitMac
|
||||
#else
|
||||
import SwiftSignalKit
|
||||
import CoreTelephony
|
||||
#endif
|
||||
|
||||
import TelegramCorePrivateModule
|
||||
|
||||
#if os(iOS)
|
||||
public enum CellularNetworkType {
|
||||
case unknown
|
||||
case gprs
|
||||
case edge
|
||||
case thirdG
|
||||
case lte
|
||||
}
|
||||
|
||||
extension CellularNetworkType {
|
||||
init(accessTechnology: String) {
|
||||
switch accessTechnology {
|
||||
case CTRadioAccessTechnologyGPRS:
|
||||
self = .gprs
|
||||
case CTRadioAccessTechnologyEdge, CTRadioAccessTechnologyCDMA1x:
|
||||
self = .edge
|
||||
case CTRadioAccessTechnologyLTE:
|
||||
self = .lte
|
||||
case CTRadioAccessTechnologyWCDMA, CTRadioAccessTechnologyHSDPA, CTRadioAccessTechnologyHSUPA, CTRadioAccessTechnologyCDMAEVDORev0, CTRadioAccessTechnologyCDMAEVDORevA, CTRadioAccessTechnologyCDMAEVDORevB, CTRadioAccessTechnologyeHRPD:
|
||||
self = .thirdG
|
||||
default:
|
||||
self = .unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
enum InternalNetworkType: Equatable {
|
||||
case none
|
||||
case wifi
|
||||
case cellular
|
||||
}
|
||||
|
||||
public enum NetworkType: Equatable {
|
||||
case none
|
||||
case wifi
|
||||
#if os(iOS)
|
||||
case cellular(CellularNetworkType)
|
||||
#endif
|
||||
}
|
||||
|
||||
extension NetworkType {
|
||||
#if os(iOS)
|
||||
init(internalType: InternalNetworkType, cellularType: CellularNetworkType) {
|
||||
switch internalType {
|
||||
case .none:
|
||||
self = .none
|
||||
case .wifi:
|
||||
self = .wifi
|
||||
case .cellular:
|
||||
self = .cellular(cellularType)
|
||||
}
|
||||
}
|
||||
#else
|
||||
init(internalType: InternalNetworkType) {
|
||||
switch internalType {
|
||||
case .none:
|
||||
self = .none
|
||||
case .wifi, .cellular:
|
||||
self = .wifi
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private final class WrappedReachability: NSObject {
|
||||
@objc private static func threadImpl() {
|
||||
while true {
|
||||
RunLoop.current.run(until: .distantFuture)
|
||||
}
|
||||
}
|
||||
|
||||
static let thread: Thread = {
|
||||
let thread = Thread(target: WrappedReachability.self, selector: #selector(WrappedReachability.threadImpl), object: nil)
|
||||
thread.start()
|
||||
return thread
|
||||
}()
|
||||
|
||||
@objc private static func dispatchOnThreadImpl(_ f: @escaping () -> Void) {
|
||||
f()
|
||||
}
|
||||
|
||||
private static func dispatchOnThread(_ f: @escaping @convention(block) () -> Void) {
|
||||
WrappedReachability.perform(#selector(WrappedReachability.dispatchOnThreadImpl(_:)), on: WrappedReachability.thread, with: f, waitUntilDone: false)
|
||||
}
|
||||
|
||||
private let reachability: Reachability
|
||||
|
||||
let value: ValuePromise<InternalNetworkType>
|
||||
|
||||
override init() {
|
||||
assert(Thread.current === WrappedReachability.thread)
|
||||
self.reachability = Reachability.forInternetConnection()
|
||||
let type: InternalNetworkType
|
||||
switch self.reachability.currentReachabilityStatus() {
|
||||
case NotReachable:
|
||||
type = .none
|
||||
case ReachableViaWiFi:
|
||||
type = .wifi
|
||||
case ReachableViaWWAN:
|
||||
type = .cellular
|
||||
default:
|
||||
type = .none
|
||||
}
|
||||
self.value = ValuePromise<InternalNetworkType>(type)
|
||||
|
||||
super.init()
|
||||
|
||||
self.reachability.reachabilityChanged = { [weak self] status in
|
||||
WrappedReachability.dispatchOnThread {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let internalNetworkType: InternalNetworkType
|
||||
switch status {
|
||||
case NotReachable:
|
||||
internalNetworkType = .none
|
||||
case ReachableViaWiFi:
|
||||
internalNetworkType = .wifi
|
||||
case ReachableViaWWAN:
|
||||
internalNetworkType = .cellular
|
||||
default:
|
||||
internalNetworkType = .none
|
||||
}
|
||||
strongSelf.value.set(internalNetworkType)
|
||||
}
|
||||
}
|
||||
self.reachability.startNotifier()
|
||||
}
|
||||
|
||||
static var valueRef: Unmanaged<WrappedReachability>?
|
||||
|
||||
static func withInstance(_ f: @escaping (WrappedReachability) -> Void) {
|
||||
WrappedReachability.dispatchOnThread {
|
||||
if self.valueRef == nil {
|
||||
self.valueRef = Unmanaged.passRetained(WrappedReachability())
|
||||
}
|
||||
if let valueRef = self.valueRef {
|
||||
let value = valueRef.takeUnretainedValue()
|
||||
f(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class NetworkTypeManagerImpl {
|
||||
let queue: Queue
|
||||
let updated: (NetworkType) -> Void
|
||||
var networkTypeDisposable: Disposable?
|
||||
var currentNetworkType: InternalNetworkType?
|
||||
var networkType: NetworkType?
|
||||
#if os(iOS)
|
||||
var currentCellularType: CellularNetworkType
|
||||
var cellularTypeObserver: NSObjectProtocol?
|
||||
#endif
|
||||
|
||||
init(queue: Queue, updated: @escaping (NetworkType) -> Void) {
|
||||
self.queue = queue
|
||||
self.updated = updated
|
||||
|
||||
#if os(iOS)
|
||||
let telephonyInfo = CTTelephonyNetworkInfo()
|
||||
let accessTechnology = telephonyInfo.currentRadioAccessTechnology ?? ""
|
||||
self.currentCellularType = CellularNetworkType(accessTechnology: accessTechnology)
|
||||
self.cellularTypeObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name.CTRadioAccessTechnologyDidChange, object: nil, queue: nil, using: { [weak self] notification in
|
||||
queue.async {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let accessTechnology = telephonyInfo.currentRadioAccessTechnology ?? ""
|
||||
let cellularType = CellularNetworkType(accessTechnology: accessTechnology)
|
||||
if strongSelf.currentCellularType != cellularType {
|
||||
strongSelf.currentCellularType = cellularType
|
||||
|
||||
if let currentNetworkType = strongSelf.currentNetworkType {
|
||||
let networkType = NetworkType(internalType: currentNetworkType, cellularType: cellularType)
|
||||
|
||||
if strongSelf.networkType != networkType {
|
||||
strongSelf.networkType = networkType
|
||||
strongSelf.updated(networkType)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
#endif
|
||||
|
||||
let networkTypeDisposable = MetaDisposable()
|
||||
self.networkTypeDisposable = networkTypeDisposable
|
||||
|
||||
WrappedReachability.withInstance({ [weak self] impl in
|
||||
networkTypeDisposable.set((impl.value.get()
|
||||
|> deliverOn(queue)).start(next: { networkStatus in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if strongSelf.currentNetworkType != networkStatus {
|
||||
strongSelf.currentNetworkType = networkStatus
|
||||
|
||||
let networkType: NetworkType
|
||||
#if os(iOS)
|
||||
networkType = NetworkType(internalType: networkStatus, cellularType: strongSelf.currentCellularType)
|
||||
#else
|
||||
networkType = NetworkType(internalType: networkStatus)
|
||||
#endif
|
||||
if strongSelf.networkType != networkType {
|
||||
strongSelf.networkType = networkType
|
||||
updated(networkType)
|
||||
}
|
||||
}
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
func stop() {
|
||||
self.networkTypeDisposable?.dispose()
|
||||
#if os(iOS)
|
||||
if let observer = self.cellularTypeObserver {
|
||||
NotificationCenter.default.removeObserver(observer, name: NSNotification.Name.CTRadioAccessTechnologyDidChange, object: nil)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
func currentNetworkType() -> Signal<NetworkType, NoError> {
|
||||
return Signal { subscriber in
|
||||
let queue = Queue()
|
||||
let disposable = MetaDisposable()
|
||||
queue.async {
|
||||
let impl = QueueLocalObject(queue: queue, generate: {
|
||||
return NetworkTypeManagerImpl(queue: queue, updated: { value in
|
||||
subscriber.putNext(value)
|
||||
})
|
||||
})
|
||||
disposable.set(ActionDisposable {
|
||||
impl.with({ impl in
|
||||
impl.stop()
|
||||
})
|
||||
})
|
||||
}
|
||||
return disposable
|
||||
}
|
||||
}
|
||||
66
TelegramCore/Reachability.h
Normal file
66
TelegramCore/Reachability.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
Copyright (C) 2016 Apple Inc. All Rights Reserved.
|
||||
See LICENSE.txt for this sample’s licensing information
|
||||
|
||||
Abstract:
|
||||
Basic demonstration of how to use the SystemConfiguration Reachablity APIs.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
#import <netinet/in.h>
|
||||
|
||||
|
||||
typedef enum : NSInteger {
|
||||
NotReachable = 0,
|
||||
ReachableViaWiFi,
|
||||
ReachableViaWWAN
|
||||
} NetworkStatus;
|
||||
|
||||
#pragma mark IPv6 Support
|
||||
//Reachability fully support IPv6. For full details, see ReadMe.md.
|
||||
|
||||
|
||||
extern NSString *kReachabilityChangedNotification;
|
||||
|
||||
|
||||
@interface Reachability : NSObject
|
||||
|
||||
@property (nonatomic, copy) void (^reachabilityChanged)(NetworkStatus status);
|
||||
|
||||
/*!
|
||||
* Use to check the reachability of a given host name.
|
||||
*/
|
||||
+ (instancetype)reachabilityWithHostName:(NSString *)hostName;
|
||||
|
||||
/*!
|
||||
* Use to check the reachability of a given IP address.
|
||||
*/
|
||||
+ (instancetype)reachabilityWithAddress:(const struct sockaddr *)hostAddress;
|
||||
|
||||
/*!
|
||||
* Checks whether the default route is available. Should be used by applications that do not connect to a particular host.
|
||||
*/
|
||||
+ (instancetype)reachabilityForInternetConnection;
|
||||
|
||||
|
||||
#pragma mark reachabilityForLocalWiFi
|
||||
//reachabilityForLocalWiFi has been removed from the sample. See ReadMe.md for more information.
|
||||
//+ (instancetype)reachabilityForLocalWiFi;
|
||||
|
||||
/*!
|
||||
* Start listening for reachability notifications on the current run loop.
|
||||
*/
|
||||
- (BOOL)startNotifier;
|
||||
- (void)stopNotifier;
|
||||
|
||||
- (NetworkStatus)currentReachabilityStatus;
|
||||
|
||||
/*!
|
||||
* WWAN may be available, but not active until a connection has been established. WiFi may require a connection for VPN on Demand.
|
||||
*/
|
||||
- (BOOL)connectionRequired;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
350
TelegramCore/Reachability.m
Normal file
350
TelegramCore/Reachability.m
Normal file
@@ -0,0 +1,350 @@
|
||||
/*
|
||||
Copyright (C) 2016 Apple Inc. All Rights Reserved.
|
||||
See LICENSE.txt for this sample’s licensing information
|
||||
|
||||
Abstract:
|
||||
Basic demonstration of how to use the SystemConfiguration Reachablity APIs.
|
||||
*/
|
||||
|
||||
#import <arpa/inet.h>
|
||||
#import <ifaddrs.h>
|
||||
#import <netdb.h>
|
||||
#import <sys/socket.h>
|
||||
#import <netinet/in.h>
|
||||
|
||||
#import <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
#import "Reachability.h"
|
||||
|
||||
#import <pthread.h>
|
||||
#import <libkern/OSAtomic.h>
|
||||
|
||||
#pragma mark IPv6 Support
|
||||
//Reachability fully support IPv6. For full details, see ReadMe.md.
|
||||
|
||||
|
||||
NSString *kReachabilityChangedNotification = @"kNetworkReachabilityChangedNotification";
|
||||
|
||||
|
||||
#pragma mark - Supporting functions
|
||||
|
||||
#define kShouldPrintReachabilityFlags 1
|
||||
|
||||
static void PrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char* comment)
|
||||
{
|
||||
#if kShouldPrintReachabilityFlags
|
||||
|
||||
NSLog(@"Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n",
|
||||
(flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-',
|
||||
|
||||
(flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-',
|
||||
comment
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
@interface ReachabilityAtomic : NSObject
|
||||
{
|
||||
pthread_mutex_t _lock;
|
||||
pthread_mutexattr_t _attr;
|
||||
id _value;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ReachabilityAtomic
|
||||
|
||||
- (instancetype)initWithValue:(id)value {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
pthread_mutex_init(&_lock, NULL);
|
||||
_value = value;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
pthread_mutex_destroy(&_lock);
|
||||
}
|
||||
|
||||
- (id)swap:(id)newValue {
|
||||
id previousValue = nil;
|
||||
pthread_mutex_lock(&_lock);
|
||||
previousValue = _value;
|
||||
_value = newValue;
|
||||
pthread_mutex_unlock(&_lock);
|
||||
return previousValue;
|
||||
}
|
||||
|
||||
- (id)value {
|
||||
id previousValue = nil;
|
||||
pthread_mutex_lock(&_lock);
|
||||
previousValue = _value;
|
||||
pthread_mutex_unlock(&_lock);
|
||||
|
||||
return previousValue;
|
||||
}
|
||||
|
||||
- (id)modify:(id (^)(id))f {
|
||||
id newValue = nil;
|
||||
pthread_mutex_lock(&_lock);
|
||||
newValue = f(_value);
|
||||
_value = newValue;
|
||||
pthread_mutex_unlock(&_lock);
|
||||
return newValue;
|
||||
}
|
||||
|
||||
- (id)with:(id (^)(id))f {
|
||||
id result = nil;
|
||||
pthread_mutex_lock(&_lock);
|
||||
result = f(_value);
|
||||
pthread_mutex_unlock(&_lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static int32_t nextKey = 1;
|
||||
static ReachabilityAtomic *contexts() {
|
||||
static ReachabilityAtomic *instance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
instance = [[ReachabilityAtomic alloc] initWithValue:@{}];
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
static void withContext(int32_t key, void (^f)(Reachability *)) {
|
||||
Reachability *reachability = [contexts() with:^id(NSDictionary *dict) {
|
||||
return dict[@(key)];
|
||||
}];
|
||||
f(reachability);
|
||||
}
|
||||
|
||||
static int32_t addContext(Reachability *context) {
|
||||
int32_t key = OSAtomicIncrement32(&nextKey);
|
||||
[contexts() modify:^id(NSMutableDictionary *dict) {
|
||||
NSMutableDictionary *updatedDict = [[NSMutableDictionary alloc] initWithDictionary:dict];
|
||||
updatedDict[@(key)] = context;
|
||||
return updatedDict;
|
||||
}];
|
||||
return key;
|
||||
}
|
||||
|
||||
static void removeContext(int32_t key) {
|
||||
[contexts() modify:^id(NSMutableDictionary *dict) {
|
||||
NSMutableDictionary *updatedDict = [[NSMutableDictionary alloc] initWithDictionary:dict];
|
||||
[updatedDict removeObjectForKey:@(key)];
|
||||
return updatedDict;
|
||||
}];
|
||||
}
|
||||
|
||||
static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
|
||||
{
|
||||
#pragma unused (target, flags)
|
||||
//NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback");
|
||||
//NSCAssert([(__bridge NSObject*) info isKindOfClass: [Reachability class]], @"info was wrong class in ReachabilityCallback");
|
||||
|
||||
int32_t key = (int32_t)((intptr_t)info);
|
||||
withContext(key, ^(Reachability *context) {
|
||||
if ([context isKindOfClass:[Reachability class]] && context.reachabilityChanged != nil)
|
||||
context.reachabilityChanged(context.currentReachabilityStatus);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Reachability implementation
|
||||
|
||||
@implementation Reachability
|
||||
{
|
||||
int32_t _key;
|
||||
SCNetworkReachabilityRef _reachabilityRef;
|
||||
}
|
||||
|
||||
+ (instancetype)reachabilityWithHostName:(NSString *)hostName
|
||||
{
|
||||
Reachability* returnValue = NULL;
|
||||
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]);
|
||||
if (reachability != NULL)
|
||||
{
|
||||
returnValue= [[self alloc] init];
|
||||
if (returnValue != NULL)
|
||||
{
|
||||
returnValue->_reachabilityRef = reachability;
|
||||
}
|
||||
else {
|
||||
CFRelease(reachability);
|
||||
}
|
||||
}
|
||||
if (returnValue) {
|
||||
returnValue->_key = addContext(returnValue);
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
|
||||
+ (instancetype)reachabilityWithAddress:(const struct sockaddr *)hostAddress
|
||||
{
|
||||
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, hostAddress);
|
||||
|
||||
Reachability* returnValue = NULL;
|
||||
|
||||
if (reachability != NULL)
|
||||
{
|
||||
returnValue = [[self alloc] init];
|
||||
if (returnValue != NULL)
|
||||
{
|
||||
returnValue->_reachabilityRef = reachability;
|
||||
}
|
||||
else {
|
||||
CFRelease(reachability);
|
||||
}
|
||||
}
|
||||
if (returnValue) {
|
||||
returnValue->_key = addContext(returnValue);
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
|
||||
+ (instancetype)reachabilityForInternetConnection
|
||||
{
|
||||
struct sockaddr_in zeroAddress;
|
||||
bzero(&zeroAddress, sizeof(zeroAddress));
|
||||
zeroAddress.sin_len = sizeof(zeroAddress);
|
||||
zeroAddress.sin_family = AF_INET;
|
||||
|
||||
return [self reachabilityWithAddress: (const struct sockaddr *) &zeroAddress];
|
||||
}
|
||||
|
||||
#pragma mark reachabilityForLocalWiFi
|
||||
//reachabilityForLocalWiFi has been removed from the sample. See ReadMe.md for more information.
|
||||
//+ (instancetype)reachabilityForLocalWiFi
|
||||
|
||||
|
||||
|
||||
#pragma mark - Start and stop notifier
|
||||
|
||||
- (BOOL)startNotifier
|
||||
{
|
||||
BOOL returnValue = NO;
|
||||
SCNetworkReachabilityContext context = {0, (void *)((intptr_t)_key), NULL, NULL, NULL};
|
||||
|
||||
if (SCNetworkReachabilitySetCallback(_reachabilityRef, ReachabilityCallback, &context))
|
||||
{
|
||||
if (SCNetworkReachabilityScheduleWithRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode))
|
||||
{
|
||||
returnValue = YES;
|
||||
}
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
|
||||
- (void)stopNotifier
|
||||
{
|
||||
if (_reachabilityRef != NULL)
|
||||
{
|
||||
SCNetworkReachabilityUnscheduleFromRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
removeContext(_key);
|
||||
[self stopNotifier];
|
||||
if (_reachabilityRef != NULL)
|
||||
{
|
||||
CFRelease(_reachabilityRef);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Network Flag Handling
|
||||
|
||||
- (NetworkStatus)networkStatusForFlags:(SCNetworkReachabilityFlags)flags
|
||||
{
|
||||
PrintReachabilityFlags(flags, "networkStatusForFlags");
|
||||
if ((flags & kSCNetworkReachabilityFlagsReachable) == 0)
|
||||
{
|
||||
// The target host is not reachable.
|
||||
return NotReachable;
|
||||
}
|
||||
|
||||
NetworkStatus returnValue = NotReachable;
|
||||
|
||||
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...
|
||||
*/
|
||||
returnValue = ReachableViaWiFi;
|
||||
}
|
||||
|
||||
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...
|
||||
*/
|
||||
returnValue = ReachableViaWiFi;
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN)
|
||||
{
|
||||
/*
|
||||
... but WWAN connections are OK if the calling application is using the CFNetwork APIs.
|
||||
*/
|
||||
returnValue = ReachableViaWWAN;
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)connectionRequired
|
||||
{
|
||||
NSAssert(_reachabilityRef != NULL, @"connectionRequired called with NULL reachabilityRef");
|
||||
SCNetworkReachabilityFlags flags;
|
||||
|
||||
if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags))
|
||||
{
|
||||
return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
- (NetworkStatus)currentReachabilityStatus
|
||||
{
|
||||
NSAssert(_reachabilityRef != NULL, @"currentNetworkStatus called with NULL SCNetworkReachabilityRef");
|
||||
NetworkStatus returnValue = NotReachable;
|
||||
SCNetworkReachabilityFlags flags;
|
||||
|
||||
if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags))
|
||||
{
|
||||
returnValue = [self networkStatusForFlags:flags];
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -2,5 +2,6 @@
|
||||
#define TelegramCoreIncludes_h
|
||||
|
||||
#import "Crypto.h"
|
||||
#import "Reachability.h"
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user