Files
safe-ios/Multisig/Data/Services/Utils/NetworkStatusObserver.swift
Andrey Scherbovich 2a30811bdb refs/heads/wc-prototype-iteration2 Merge branch 'gh-1041-swift-pm' of github.com:gnosis/safe-ios into wc-prototype
# Conflicts:
#	Multisig.xcodeproj/project.pbxproj
#	Multisig.xcworkspace/xcshareddata/swiftpm/Package.resolved
#	Multisig/App/NSNotification+Events.swift
#	Multisig/App/SceneDelegate.swift
#	Multisig/Data/Services/Safe Client Gateway Service/SCGModels.swift
#	Multisig/Info.plist
#	Multisig/Logic/Models/DataString.swift
#	Multisig/Logic/Models/HashString.swift
#	Multisig/Logic/Models/Multisig.xcdatamodeld/Multisig.xcdatamodel/contents
#	Multisig/Logic/Models/Transaction.swift
#	Multisig/UI/App/ViewControllerFactory.swift
#	Multisig/UI/Settings/AppSettingsViewController/AppSettingsViewController.swift
#	Multisig/UI/Settings/AppSettingsViewController/ImportedKeyCell.xib
#	Multisig/UI/Settings/OwnerKeyManagement/EnterKeyOrSeedPhraseViewController.swift
#	Multisig/UI/Settings/SafeSettingsViewController/SafeSettingsViewController.swift
#	Multisig/UI/UI Library/Views/BasicCell.xib
#	MultisigIntegrationTests/Info.plist
#	MultisigTests/Info.plist
#	NotificationServiceExtension/Info.plist
#	Podfile
#	Podfile.lock
#	Pods/Manifest.lock
#	Pods/Pods.xcodeproj/project.pbxproj
#	Pods/Target Support Files/Pods-Multisig/Pods-Multisig-acknowledgements.markdown
#	Pods/Target Support Files/Pods-Multisig/Pods-Multisig-acknowledgements.plist
#	Pods/Target Support Files/Pods-Multisig/Pods-Multisig.debug.development.mainnet.xcconfig
#	Pods/Target Support Files/Pods-Multisig/Pods-Multisig.debug.development.rinkeby.xcconfig
#	Pods/Target Support Files/Pods-Multisig/Pods-Multisig.debug.production.mainnet.xcconfig
#	Pods/Target Support Files/Pods-Multisig/Pods-Multisig.debug.production.rinkeby.xcconfig
#	Pods/Target Support Files/Pods-Multisig/Pods-Multisig.debug.staging.mainnet.xcconfig
#	Pods/Target Support Files/Pods-Multisig/Pods-Multisig.debug.staging.rinkeby.xcconfig
#	Pods/Target Support Files/Pods-Multisig/Pods-Multisig.release.development.mainnet.xcconfig
#	Pods/Target Support Files/Pods-Multisig/Pods-Multisig.release.development.rinkeby.xcconfig
#	Pods/Target Support Files/Pods-Multisig/Pods-Multisig.release.production.mainnet.xcconfig
#	Pods/Target Support Files/Pods-Multisig/Pods-Multisig.release.production.rinkeby.xcconfig
#	Pods/Target Support Files/Pods-Multisig/Pods-Multisig.release.staging.mainnet.xcconfig
#	Pods/Target Support Files/Pods-Multisig/Pods-Multisig.release.staging.rinkeby.xcconfig
#	Pods/Target Support Files/Pods-MultisigIntegrationTests/Pods-MultisigIntegrationTests.debug.development.mainnet.xcconfig
#	Pods/Target Support Files/Pods-MultisigIntegrationTests/Pods-MultisigIntegrationTests.debug.development.rinkeby.xcconfig
#	Pods/Target Support Files/Pods-MultisigIntegrationTests/Pods-MultisigIntegrationTests.debug.production.mainnet.xcconfig
#	Pods/Target Support Files/Pods-MultisigIntegrationTests/Pods-MultisigIntegrationTests.debug.production.rinkeby.xcconfig
#	Pods/Target Support Files/Pods-MultisigIntegrationTests/Pods-MultisigIntegrationTests.debug.staging.mainnet.xcconfig
#	Pods/Target Support Files/Pods-MultisigIntegrationTests/Pods-MultisigIntegrationTests.debug.staging.rinkeby.xcconfig
#	Pods/Target Support Files/Pods-MultisigIntegrationTests/Pods-MultisigIntegrationTests.release.development.mainnet.xcconfig
#	Pods/Target Support Files/Pods-MultisigIntegrationTests/Pods-MultisigIntegrationTests.release.development.rinkeby.xcconfig
#	Pods/Target Support Files/Pods-MultisigIntegrationTests/Pods-MultisigIntegrationTests.release.production.mainnet.xcconfig
#	Pods/Target Support Files/Pods-MultisigIntegrationTests/Pods-MultisigIntegrationTests.release.production.rinkeby.xcconfig
#	Pods/Target Support Files/Pods-MultisigIntegrationTests/Pods-MultisigIntegrationTests.release.staging.mainnet.xcconfig
#	Pods/Target Support Files/Pods-MultisigIntegrationTests/Pods-MultisigIntegrationTests.release.staging.rinkeby.xcconfig
#	Pods/Target Support Files/Pods-MultisigTests/Pods-MultisigTests.debug.development.mainnet.xcconfig
#	Pods/Target Support Files/Pods-MultisigTests/Pods-MultisigTests.debug.development.rinkeby.xcconfig
#	Pods/Target Support Files/Pods-MultisigTests/Pods-MultisigTests.debug.production.mainnet.xcconfig
#	Pods/Target Support Files/Pods-MultisigTests/Pods-MultisigTests.debug.production.rinkeby.xcconfig
#	Pods/Target Support Files/Pods-MultisigTests/Pods-MultisigTests.debug.staging.mainnet.xcconfig
#	Pods/Target Support Files/Pods-MultisigTests/Pods-MultisigTests.debug.staging.rinkeby.xcconfig
#	Pods/Target Support Files/Pods-MultisigTests/Pods-MultisigTests.release.development.mainnet.xcconfig
#	Pods/Target Support Files/Pods-MultisigTests/Pods-MultisigTests.release.development.rinkeby.xcconfig
#	Pods/Target Support Files/Pods-MultisigTests/Pods-MultisigTests.release.production.mainnet.xcconfig
#	Pods/Target Support Files/Pods-MultisigTests/Pods-MultisigTests.release.production.rinkeby.xcconfig
#	Pods/Target Support Files/Pods-MultisigTests/Pods-MultisigTests.release.staging.mainnet.xcconfig
#	Pods/Target Support Files/Pods-MultisigTests/Pods-MultisigTests.release.staging.rinkeby.xcconfig
#	Pods/Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension.debug.development.mainnet.xcconfig
#	Pods/Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension.debug.development.rinkeby.xcconfig
#	Pods/Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension.debug.production.mainnet.xcconfig
#	Pods/Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension.debug.production.rinkeby.xcconfig
#	Pods/Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension.debug.staging.mainnet.xcconfig
#	Pods/Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension.debug.staging.rinkeby.xcconfig
#	Pods/Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension.release.development.mainnet.xcconfig
#	Pods/Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension.release.development.rinkeby.xcconfig
#	Pods/Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension.release.production.mainnet.xcconfig
#	Pods/Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension.release.production.rinkeby.xcconfig
#	Pods/Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension.release.staging.mainnet.xcconfig
#	Pods/Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension.release.staging.rinkeby.xcconfig
2021-04-01 12:36:14 +07:00

139 lines
4.9 KiB
Swift

//
// NetworkStatusObserver.swift
// Multisig
//
// Created by Dmitry Bespalov on 22.03.21.
// Copyright © 2021 Gnosis Ltd. All rights reserved.
//
import Foundation
import SystemConfiguration
import CoreFoundation
/// Class that can observe a network host reachability status and post
/// notifications when the status changes.
///
/// All methods MUST be called on main thread.
class NetworkHostStatusObserver {
/// Host to observe
let host: String
private var target: SCNetworkReachability?
enum Status {
case unknown, online, offline
fileprivate static func from(reachabilityFlags: SCNetworkReachabilityFlags) -> Self {
// if reachable and not need to connect
let reachableWithoutConnection = reachabilityFlags.contains(.reachable) && !reachabilityFlags.contains(.connectionRequired)
let reachableWithoutIntervention = reachabilityFlags.contains(.reachable) && !reachabilityFlags.contains(.interventionRequired) &&
(reachabilityFlags.contains(.connectionOnTraffic) || reachabilityFlags.contains(.connectionOnDemand))
if reachableWithoutConnection || reachableWithoutIntervention {
return .online
} else {
return .offline
}
}
}
private (set) var status: Status = .unknown
/// Constructor
/// - Parameter host: Pass the URL's host name to observe.
init(host: String) {
self.host = host
}
/// Starts asynchronous observing of the network host.
/// - Posts `networkHostUnreachable` when the host becomes unreachable or
/// after getting first update of the reachability (if it's unreachable).
/// - Posts `networkHostReachable` when the host becomes reachable after
/// being unreachable
func startObserving() {
dispatchPrecondition(condition: .onQueue(.main))
// create target
stopObserving()
target = SCNetworkReachabilityCreateWithName(nil, host)
guard let target = target else {
logError()
return
}
// schedule on the main thread's run loop
let scheduleSuccess = SCNetworkReachabilityScheduleWithRunLoop(target, CFRunLoopGetMain(), CFRunLoopMode.commonModes.rawValue)
guard scheduleSuccess else {
logError()
return
}
// wrap pointer to self into context info in order to get
// reference to `self` from the C callback closure
let info = UnsafeMutableRawPointer(Unmanaged.passRetained(self).toOpaque())
var context = SCNetworkReachabilityContext(version: 0, info: info, retain: nil, release: nil, copyDescription: nil)
let callbackSuccess = SCNetworkReachabilitySetCallback(target, { (_, flags, context) in
// unwrap self from context info
guard let ctx = context else { return }
let observer = Unmanaged<NetworkHostStatusObserver>.fromOpaque(ctx).takeUnretainedValue()
observer.updateStatus(flags: flags)
}, &context)
guard callbackSuccess else {
logError()
return
}
}
private func updateStatus(flags: SCNetworkReachabilityFlags) {
let oldStatus = status
let newStatus = Status.from(reachabilityFlags: flags)
status = newStatus
// Notify listeners
switch (oldStatus, newStatus) {
case (.unknown, .offline), (.online, .offline):
NotificationCenter.default.post(name: .networkHostUnreachable, object: self, userInfo: ["host": host])
case (.offline, .online):
NotificationCenter.default.post(name: .networkHostReachable, object: self, userInfo: ["host": host])
default:
break
}
}
private func logError(line: UInt = #line, file: StaticString = #file) {
let code = SCError()
let message = SCErrorString(code)
LogService.shared.error("\(file):\(line): Network reachability failure: '\(message)', Code \(code)")
}
/// Stops observing network host reachability
func stopObserving() {
dispatchPrecondition(condition: .onQueue(.main))
guard let target = target else { return }
// delete the callback
SCNetworkReachabilitySetCallback(target, nil, nil)
// unschedule from run loop
SCNetworkReachabilityUnscheduleFromRunLoop(target, CFRunLoopGetMain(), CFRunLoopMode.commonModes.rawValue)
self.target = nil
assert(self.target == nil)
}
/// Gets current reachability status without updating the object's status
func getStatus() -> Status? {
guard let target = SCNetworkReachabilityCreateWithName(nil, host) else {
logError()
return nil
}
var outFlags: SCNetworkReachabilityFlags = []
SCNetworkReachabilityGetFlags(target, &outFlags)
let status = Status.from(reachabilityFlags: outFlags)
return status
}
}