From fb05647d979f919b0aa36f405562c4608828e781 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 29 Jan 2021 01:46:57 +0500 Subject: [PATCH] Use NSPathMonitor instead of Reachability to monitor network connectivity --- submodules/Reachability/BUILD | 18 +- .../Reachability/LegacyReachability/BUILD | 22 +++ .../LegacyReachability/LegacyReachability.h} | 2 +- .../Sources/LegacyReachability.m} | 22 +-- .../Reachability/Sources/Reachability.swift | 180 ++++++++++++++++++ .../TelegramCore/Sources/NetworkType.swift | 134 +++---------- 6 files changed, 243 insertions(+), 135 deletions(-) create mode 100644 submodules/Reachability/LegacyReachability/BUILD rename submodules/Reachability/{PublicHeaders/Reachability/Reachability.h => LegacyReachability/PublicHeaders/LegacyReachability/LegacyReachability.h} (97%) rename submodules/Reachability/{Sources/Reachability.m => LegacyReachability/Sources/LegacyReachability.m} (92%) create mode 100644 submodules/Reachability/Sources/Reachability.swift diff --git a/submodules/Reachability/BUILD b/submodules/Reachability/BUILD index 35b3371126..5e08b22c98 100644 --- a/submodules/Reachability/BUILD +++ b/submodules/Reachability/BUILD @@ -1,20 +1,14 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") -objc_library( +swift_library( name = "Reachability", - enable_modules = True, module_name = "Reachability", srcs = glob([ - "Sources/*.m", + "Sources/**/*.swift", ]), - hdrs = glob([ - "PublicHeaders/**/*.h", - ]), - includes = [ - "PublicHeaders", - ], - sdk_frameworks = [ - "Foundation", - "SystemConfiguration", + deps = [ + "//submodules/Reachability/LegacyReachability:LegacyReachability", + "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", ], visibility = [ "//visibility:public", diff --git a/submodules/Reachability/LegacyReachability/BUILD b/submodules/Reachability/LegacyReachability/BUILD new file mode 100644 index 0000000000..f9f9023871 --- /dev/null +++ b/submodules/Reachability/LegacyReachability/BUILD @@ -0,0 +1,22 @@ + +objc_library( + name = "LegacyReachability", + enable_modules = True, + module_name = "LegacyReachability", + srcs = glob([ + "Sources/*.m", + ]), + hdrs = glob([ + "PublicHeaders/**/*.h", + ]), + includes = [ + "PublicHeaders", + ], + sdk_frameworks = [ + "Foundation", + "SystemConfiguration", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/Reachability/PublicHeaders/Reachability/Reachability.h b/submodules/Reachability/LegacyReachability/PublicHeaders/LegacyReachability/LegacyReachability.h similarity index 97% rename from submodules/Reachability/PublicHeaders/Reachability/Reachability.h rename to submodules/Reachability/LegacyReachability/PublicHeaders/LegacyReachability/LegacyReachability.h index 2ece48e5d4..f03b45f207 100644 --- a/submodules/Reachability/PublicHeaders/Reachability/Reachability.h +++ b/submodules/Reachability/LegacyReachability/PublicHeaders/LegacyReachability/LegacyReachability.h @@ -24,7 +24,7 @@ typedef enum : NSInteger { extern NSString *kReachabilityChangedNotification; -@interface Reachability : NSObject +@interface LegacyReachability : NSObject @property (nonatomic, copy) void (^reachabilityChanged)(NetworkStatus status); diff --git a/submodules/Reachability/Sources/Reachability.m b/submodules/Reachability/LegacyReachability/Sources/LegacyReachability.m similarity index 92% rename from submodules/Reachability/Sources/Reachability.m rename to submodules/Reachability/LegacyReachability/Sources/LegacyReachability.m index 4b3abb5088..ecedbeccbc 100644 --- a/submodules/Reachability/Sources/Reachability.m +++ b/submodules/Reachability/LegacyReachability/Sources/LegacyReachability.m @@ -14,7 +14,7 @@ #import -#import +#import #import #import @@ -126,14 +126,14 @@ static ReachabilityAtomic *contexts() { return instance; } -static void withContext(int32_t key, void (^f)(Reachability *)) { - Reachability *reachability = [contexts() with:^id(NSDictionary *dict) { +static void withContext(int32_t key, void (^f)(LegacyReachability *)) { + LegacyReachability *reachability = [contexts() with:^id(NSDictionary *dict) { return dict[@(key)]; }]; f(reachability); } -static int32_t addContext(Reachability *context) { +static int32_t addContext(LegacyReachability *context) { int32_t key = OSAtomicIncrement32(&nextKey); [contexts() modify:^id(NSMutableDictionary *dict) { NSMutableDictionary *updatedDict = [[NSMutableDictionary alloc] initWithDictionary:dict]; @@ -155,19 +155,19 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach { #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"); + //NSCAssert([(__bridge NSObject*) info isKindOfClass: [LegacyReachability 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) + withContext(key, ^(LegacyReachability *context) { + if ([context isKindOfClass:[LegacyReachability class]] && context.reachabilityChanged != nil) context.reachabilityChanged(context.currentReachabilityStatus); }); } -#pragma mark - Reachability implementation +#pragma mark - LegacyReachability implementation -@implementation Reachability +@implementation LegacyReachability { int32_t _key; SCNetworkReachabilityRef _reachabilityRef; @@ -175,7 +175,7 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach + (instancetype)reachabilityWithHostName:(NSString *)hostName { - Reachability* returnValue = NULL; + LegacyReachability* returnValue = NULL; SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]); if (reachability != NULL) { @@ -199,7 +199,7 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach { SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, hostAddress); - Reachability* returnValue = NULL; + LegacyReachability* returnValue = NULL; if (reachability != NULL) { diff --git a/submodules/Reachability/Sources/Reachability.swift b/submodules/Reachability/Sources/Reachability.swift new file mode 100644 index 0000000000..02c934d527 --- /dev/null +++ b/submodules/Reachability/Sources/Reachability.swift @@ -0,0 +1,180 @@ +import Foundation +import SwiftSignalKit + +import LegacyReachability +import Network + +private final class WrappedLegacyReachability: NSObject { + @objc private static func threadImpl() { + while true { + RunLoop.current.run(until: .distantFuture) + } + } + + private static let thread: Thread = { + let thread = Thread(target: Reachability.self, selector: #selector(WrappedLegacyReachability.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) { + WrappedLegacyReachability.perform(#selector(WrappedLegacyReachability.dispatchOnThreadImpl(_:)), on: WrappedLegacyReachability.thread, with: f, waitUntilDone: false) + } + + private let reachability: LegacyReachability + + let value: ValuePromise + + override init() { + assert(Thread.current === WrappedLegacyReachability.thread) + self.reachability = LegacyReachability.forInternetConnection() + let type: Reachability.NetworkType + switch self.reachability.currentReachabilityStatus() { + case NotReachable: + type = .none + case ReachableViaWiFi: + type = .wifi + case ReachableViaWWAN: + type = .cellular + default: + type = .none + } + self.value = ValuePromise(type) + + super.init() + + self.reachability.reachabilityChanged = { [weak self] status in + WrappedLegacyReachability.dispatchOnThread { + guard let strongSelf = self else { + return + } + let internalNetworkType: Reachability.NetworkType + switch status { + case NotReachable: + internalNetworkType = .none + case ReachableViaWiFi: + internalNetworkType = .wifi + case ReachableViaWWAN: + internalNetworkType = .cellular + default: + internalNetworkType = .none + } + strongSelf.value.set(internalNetworkType) + } + } + self.reachability.startNotifier() + } + + private static var valueRef: Unmanaged? + + static func withInstance(_ f: @escaping (WrappedLegacyReachability) -> Void) { + WrappedLegacyReachability.dispatchOnThread { + if self.valueRef == nil { + self.valueRef = Unmanaged.passRetained(WrappedLegacyReachability()) + } + if let valueRef = self.valueRef { + let value = valueRef.takeUnretainedValue() + f(value) + } + } + } +} + +@available(iOSApplicationExtension 12.0, iOS 12.0, *) +private final class PathMonitor { + private let queue: Queue + private let monitor: NWPathMonitor + + let networkType = Promise() + + init(queue: Queue) { + self.queue = queue + self.monitor = NWPathMonitor() + + self.monitor.pathUpdateHandler = { [weak self] path in + queue.async { + guard let strongSelf = self else { + return + } + let networkType: Reachability.NetworkType + if path.status == .satisfied { + if path.usesInterfaceType(.cellular) { + networkType = .cellular + } else { + networkType = .wifi + } + } else { + networkType = .none + } + + strongSelf.networkType.set(.single(networkType)) + } + } + + self.monitor.start(queue: self.queue.queue) + + let networkType: Reachability.NetworkType + let path = self.monitor.currentPath + if path.status == .satisfied { + if path.usesInterfaceType(.cellular) { + networkType = .cellular + } else { + networkType = .wifi + } + } else { + networkType = .none + } + + self.networkType.set(.single(networkType)) + } +} + +@available(iOSApplicationExtension 12.0, iOS 12.0, *) +private final class SharedPathMonitor { + static let queue = Queue() + static let impl = QueueLocalObject(queue: queue, generate: { + return PathMonitor(queue: queue) + }) +} + +public enum Reachability { + public enum NetworkType: Equatable { + case none + case wifi + case cellular + } + + public static var networkType: Signal { + if #available(iOSApplicationExtension 12.0, iOS 12.0, *) { + return Signal { subscriber in + let disposable = MetaDisposable() + + SharedPathMonitor.impl.with { impl in + disposable.set(impl.networkType.get().start(next: { value in + subscriber.putNext(value) + })) + } + + return disposable + } + |> distinctUntilChanged + } else { + return Signal { subscriber in + let disposable = MetaDisposable() + + WrappedLegacyReachability.withInstance({ impl in + disposable.set(impl.value.get().start(next: { next in + subscriber.putNext(next) + })) + }) + + return disposable + } + |> distinctUntilChanged + } + } +} diff --git a/submodules/TelegramCore/Sources/NetworkType.swift b/submodules/TelegramCore/Sources/NetworkType.swift index 52e111a845..ab672ea004 100644 --- a/submodules/TelegramCore/Sources/NetworkType.swift +++ b/submodules/TelegramCore/Sources/NetworkType.swift @@ -34,12 +34,6 @@ extension CellularNetworkType { #endif -enum InternalNetworkType: Equatable { - case none - case wifi - case cellular -} - public enum NetworkType: Equatable { case none case wifi @@ -50,7 +44,7 @@ public enum NetworkType: Equatable { extension NetworkType { #if os(iOS) - init(internalType: InternalNetworkType, cellularType: CellularNetworkType) { + init(internalType: Reachability.NetworkType, cellularType: CellularNetworkType) { switch internalType { case .none: self = .none @@ -61,7 +55,7 @@ extension NetworkType { } } #else - init(internalType: InternalNetworkType) { + init(internalType: Reachability.NetworkType) { switch internalType { case .none: self = .none @@ -72,91 +66,11 @@ extension NetworkType { #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 - - 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(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? - - 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 currentNetworkType: Reachability.NetworkType? var networkType: NetworkType? #if os(iOS) var currentCellularType: CellularNetworkType @@ -196,29 +110,27 @@ private final class NetworkTypeManagerImpl { 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 + + networkTypeDisposable.set((Reachability.networkType + |> deliverOn(queue)).start(next: { [weak self] 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) } - 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() {