mirror of
https://github.com/confirmedcode/Lockdown-iOS.git
synced 2025-12-21 12:14:02 +01:00
556 lines
22 KiB
Swift
556 lines
22 KiB
Swift
//
|
||
// LDFirewallViewController.swift
|
||
// Lockdown
|
||
//
|
||
// Created by Aliaksandr Dvoineu on 17.04.23.
|
||
// Copyright © 2023 Confirmed Inc. All rights reserved.
|
||
//
|
||
|
||
import UIKit
|
||
import CocoaLumberjackSwift
|
||
import PromiseKit
|
||
import NetworkExtension
|
||
import PopupDialog
|
||
|
||
final class LDFirewallViewController: BaseViewController {
|
||
|
||
// MARK: Properties
|
||
let kHasViewedTutorial = "hasViewedTutorial"
|
||
let kHasSeenInitialFirewallConnectedDialog = "hasSeenInitialFirewallConnectedDialog11"
|
||
let kHasSeenShare = "hasSeenShareDialog4"
|
||
|
||
let ratingCountKey = "ratingCount" + lastVersionToAskForRating
|
||
let ratingTriggeredKey = "ratingTriggered" + lastVersionToAskForRating
|
||
|
||
var lastFirewallStatus: NEVPNStatus?
|
||
var activePlans: [Subscription.PlanType] = []
|
||
let vc = FirewallPaywallViewController()
|
||
|
||
enum Mode {
|
||
case newSubscription
|
||
case upgrade(active: [Subscription.PlanType])
|
||
}
|
||
|
||
var mode = Mode.newSubscription
|
||
|
||
var metricsTimer : Timer?
|
||
|
||
private lazy var scrollView: UIScrollView = {
|
||
let view = UIScrollView()
|
||
view.isScrollEnabled = true
|
||
return view
|
||
}()
|
||
|
||
private lazy var contentView: UIView = {
|
||
let view = UIView()
|
||
view.anchors.height.equal(800)
|
||
return view
|
||
}()
|
||
|
||
lazy var yourCurrentPlanLabel: UILabel = {
|
||
let label = UILabel()
|
||
label.text = NSLocalizedString("Your current plan is", comment: "")
|
||
label.font = fontRegular14
|
||
label.numberOfLines = 0
|
||
label.textColor = .label
|
||
return label
|
||
}()
|
||
|
||
lazy var upgradeLabel: UILabel = {
|
||
let label = UILabel()
|
||
label.text = NSLocalizedString("Upgrade?", comment: "")
|
||
label.font = fontBold13
|
||
label.textColor = .tunnelsBlue
|
||
label.isUserInteractionEnabled = true
|
||
label.setOnClickListener {
|
||
let vc = VPNPaywallViewController()
|
||
self.present(vc, animated: true)
|
||
}
|
||
return label
|
||
}()
|
||
|
||
lazy var protectionPlanLabel: UILabel = {
|
||
let label = UILabel()
|
||
label.text = NSLocalizedString("Basic Protection", comment: "")
|
||
label.font = fontBold22
|
||
label.textColor = .label
|
||
return label
|
||
}()
|
||
|
||
lazy var firewallTitle: UILabel = {
|
||
let label = UILabel()
|
||
label.text = NSLocalizedString("Get complete protection", comment: "")
|
||
label.font = fontBold24
|
||
label.numberOfLines = 0
|
||
label.textColor = .label
|
||
return label
|
||
}()
|
||
|
||
private lazy var firewallDescriptionLabel1: DescriptionLabel = {
|
||
let label = DescriptionLabel()
|
||
label.configure(with: DescriptionLabelViewModel(text: NSLocalizedString("Block as many trackers as you want", comment: "")))
|
||
return label
|
||
}()
|
||
|
||
private lazy var firewallDescriptionLabel2: DescriptionLabel = {
|
||
let label = DescriptionLabel()
|
||
label.configure(with: DescriptionLabelViewModel(text: NSLocalizedString("Import and export your own block lists", comment: "")))
|
||
return label
|
||
}()
|
||
|
||
private lazy var firewallDescriptionLabel3: DescriptionLabel = {
|
||
let label = DescriptionLabel()
|
||
label.configure(with: DescriptionLabelViewModel(text: NSLocalizedString("Access to new curated lists of trackers", comment: "")))
|
||
return label
|
||
}()
|
||
|
||
private lazy var stackView: UIStackView = {
|
||
let stackView = UIStackView()
|
||
stackView.addArrangedSubview(firewallTitle)
|
||
stackView.addArrangedSubview(firewallDescriptionLabel1)
|
||
stackView.addArrangedSubview(firewallDescriptionLabel2)
|
||
stackView.addArrangedSubview(firewallDescriptionLabel3)
|
||
stackView.axis = .vertical
|
||
stackView.distribution = .fillProportionally
|
||
stackView.spacing = 10
|
||
return stackView
|
||
}()
|
||
|
||
private lazy var cpTitle: UILabel = {
|
||
let label = UILabel()
|
||
label.text = NSLocalizedString("Don't let those trackers know your every move – Upgrade to Advanced now!", comment: "")
|
||
label.textColor = .black
|
||
label.font = fontBold15
|
||
label.numberOfLines = 0
|
||
label.textAlignment = .center
|
||
return label
|
||
}()
|
||
|
||
private lazy var cpTrackersGroupView1: TrackersGroupView = {
|
||
let view = TrackersGroupView()
|
||
view.placeNumber.text = "#1"
|
||
view.placeNumber.textColor = .black
|
||
view.titleLabel.textColor = .black
|
||
view.number.textColor = .black
|
||
view.configure(with: TrackersGroupViewModel(image: UIImage(named: "icn_game_marketing")!, title: "Game Marketing", number: 4678))
|
||
view.number.isHidden = true
|
||
return view
|
||
}()
|
||
|
||
private lazy var cpTrackersGroupView2: TrackersGroupView = {
|
||
let view = TrackersGroupView()
|
||
view.placeNumber.text = "#2"
|
||
view.placeNumber.textColor = .black
|
||
view.titleLabel.textColor = .black
|
||
view.number.textColor = .black
|
||
view.configure(with: TrackersGroupViewModel(image: UIImage(named: "icn_marketing_trackers")!, title: "Marketing Trackers", number: 3432))
|
||
view.number.isHidden = true
|
||
return view
|
||
}()
|
||
|
||
private lazy var cpTrackersGroupView3: TrackersGroupView = {
|
||
let view = TrackersGroupView()
|
||
view.placeNumber.text = "#3"
|
||
view.placeNumber.textColor = .black
|
||
view.titleLabel.textColor = .black
|
||
view.number.textColor = .black
|
||
view.configure(with: TrackersGroupViewModel(image: UIImage(named: "icn_email_trackers")!, title: "Email Trackers", number: 2756))
|
||
view.number.isHidden = true
|
||
return view
|
||
}()
|
||
|
||
private lazy var cpStackView: UIStackView = {
|
||
let stackView = UIStackView()
|
||
stackView.addArrangedSubview(cpTitle)
|
||
stackView.addArrangedSubview(cpTrackersGroupView1)
|
||
stackView.addArrangedSubview(cpTrackersGroupView2)
|
||
stackView.addArrangedSubview(cpTrackersGroupView3)
|
||
stackView.layer.cornerRadius = 8
|
||
stackView.layer.borderWidth = 2
|
||
stackView.layer.borderColor = UIColor.gray.cgColor
|
||
stackView.axis = .vertical
|
||
stackView.distribution = .fillEqually
|
||
stackView.spacing = 0
|
||
stackView.backgroundColor = .extraLightGray
|
||
return stackView
|
||
}()
|
||
|
||
private lazy var upgradeButton: UIButton = {
|
||
let button = UIButton(type: .system)
|
||
button.tintColor = .white
|
||
button.setTitle(NSLocalizedString("Upgrade", comment: ""), for: .normal)
|
||
button.titleLabel?.font = fontBold18
|
||
button.backgroundColor = .tunnelsBlue
|
||
button.layer.cornerRadius = 28
|
||
button.anchors.height.equal(56)
|
||
button.addTarget(self, action: #selector(upgrade), for: .touchUpInside)
|
||
return button
|
||
}()
|
||
|
||
private lazy var maTitle: UILabel = {
|
||
let label = UILabel()
|
||
label.text = NSLocalizedString("Most active this week", comment: "")
|
||
label.textColor = .label
|
||
label.font = fontBold15
|
||
label.textAlignment = .center
|
||
return label
|
||
}()
|
||
|
||
private lazy var maTrackersGroupView1: TrackersGroupView = {
|
||
let view = TrackersGroupView()
|
||
view.placeNumber.text = "#1"
|
||
view.number.textColor = .label
|
||
view.configure(with: TrackersGroupViewModel(image: UIImage(named: "icn_facebook_trackers")!, title: "Facebook Trackers", number: 89))
|
||
view.lockImage.isHidden = true
|
||
return view
|
||
}()
|
||
|
||
private lazy var maTrackersGroupView2: TrackersGroupView = {
|
||
let view = TrackersGroupView()
|
||
view.placeNumber.text = "#2"
|
||
view.number.textColor = .label
|
||
view.configure(with: TrackersGroupViewModel(image: UIImage(named: "icn_data_trackers")!, title: "Data Trackers", number: 32))
|
||
view.lockImage.isHidden = true
|
||
return view
|
||
}()
|
||
|
||
private lazy var maTrackersGroupView3: TrackersGroupView = {
|
||
let view = TrackersGroupView()
|
||
view.placeNumber.text = "#3"
|
||
view.number.textColor = .label
|
||
view.configure(with: TrackersGroupViewModel(image: UIImage(named: "icn_clickbait_trackers")!, title: "Clickbait", number: 21))
|
||
view.lockImage.isHidden = true
|
||
return view
|
||
}()
|
||
|
||
private lazy var maStackView: UIStackView = {
|
||
let stackView = UIStackView()
|
||
stackView.addArrangedSubview(maTitle)
|
||
stackView.addArrangedSubview(maTrackersGroupView1)
|
||
stackView.addArrangedSubview(maTrackersGroupView2)
|
||
stackView.addArrangedSubview(maTrackersGroupView3)
|
||
stackView.layer.cornerRadius = 8
|
||
stackView.layer.borderWidth = 1
|
||
stackView.layer.borderColor = UIColor.lightGray.cgColor
|
||
stackView.axis = .vertical
|
||
stackView.distribution = .fillEqually
|
||
stackView.spacing = 0
|
||
return stackView
|
||
}()
|
||
|
||
lazy var statisitcsView: OverallStatiscticView = {
|
||
let view = OverallStatiscticView()
|
||
return view
|
||
}()
|
||
|
||
private lazy var firewallSwitchControl: CustomUISwitch = {
|
||
let uiSwitch = CustomUISwitch(onImage: UIImage(named: "firewall-on-image")!, offImage: UIImage(named: "firewall-off-image")!)
|
||
uiSwitch.setOnClickListener {
|
||
self.toggleFirewall()
|
||
}
|
||
return uiSwitch
|
||
}()
|
||
|
||
override func viewDidLoad() {
|
||
super.viewDidLoad()
|
||
VPNSubscription.cacheLocalizedPrices()
|
||
|
||
updateFirewallButtonWithStatus(status: FirewallController.shared.status())
|
||
updateMetrics()
|
||
if metricsTimer == nil {
|
||
metricsTimer = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: #selector(updateMetrics), userInfo: nil, repeats: true)
|
||
metricsTimer?.fire()
|
||
}
|
||
|
||
view.backgroundColor = .systemBackground
|
||
|
||
view.addSubview(firewallSwitchControl)
|
||
firewallSwitchControl.anchors.bottom.safeAreaPin()
|
||
firewallSwitchControl.anchors.leading.marginsPin()
|
||
firewallSwitchControl.anchors.trailing.marginsPin()
|
||
firewallSwitchControl.anchors.height.equal(56)
|
||
|
||
view.addSubview(yourCurrentPlanLabel)
|
||
yourCurrentPlanLabel.anchors.leading.marginsPin()
|
||
yourCurrentPlanLabel.anchors.top.safeAreaPin()
|
||
|
||
view.addSubview(upgradeLabel)
|
||
upgradeLabel.anchors.trailing.marginsPin()
|
||
upgradeLabel.anchors.centerY.equal(yourCurrentPlanLabel.anchors.centerY)
|
||
|
||
view.addSubview(protectionPlanLabel)
|
||
protectionPlanLabel.anchors.top.spacing(8, to: yourCurrentPlanLabel.anchors.bottom)
|
||
protectionPlanLabel.anchors.leading.marginsPin()
|
||
|
||
view.addSubview(scrollView)
|
||
scrollView.anchors.top.spacing(12, to: protectionPlanLabel.anchors.bottom)
|
||
scrollView.anchors.leading.pin()
|
||
scrollView.anchors.trailing.pin()
|
||
scrollView.anchors.bottom.spacing(8, to: firewallSwitchControl.anchors.top)
|
||
|
||
scrollView.addSubview(contentView)
|
||
contentView.anchors.top.pin()
|
||
contentView.anchors.centerX.align()
|
||
contentView.anchors.width.equal(scrollView.anchors.width)
|
||
contentView.anchors.bottom.pin()
|
||
|
||
contentView.addSubview(stackView)
|
||
stackView.anchors.top.marginsPin()
|
||
stackView.anchors.leading.marginsPin()
|
||
stackView.anchors.trailing.marginsPin()
|
||
|
||
contentView.addSubview(upgradeButton)
|
||
upgradeButton.anchors.top.spacing(18, to: stackView.anchors.bottom)
|
||
upgradeButton.anchors.leading.marginsPin()
|
||
upgradeButton.anchors.trailing.marginsPin()
|
||
|
||
contentView.addSubview(maStackView)
|
||
maStackView.anchors.top.spacing(18, to: upgradeButton.anchors.bottom)
|
||
maStackView.anchors.leading.marginsPin()
|
||
maStackView.anchors.trailing.marginsPin()
|
||
|
||
contentView.addSubview(statisitcsView)
|
||
statisitcsView.anchors.top.spacing(18, to: maStackView.anchors.bottom)
|
||
statisitcsView.anchors.leading.marginsPin()
|
||
statisitcsView.anchors.trailing.marginsPin()
|
||
|
||
updateProtectionPlanUI()
|
||
|
||
// accountStateDidChange()
|
||
|
||
NotificationCenter.default.addObserver(self, selector: #selector(tunnelStatusDidChange(_:)), name: .NEVPNStatusDidChange, object: nil)
|
||
}
|
||
|
||
override func viewWillAppear(_ animated: Bool) {
|
||
super.viewWillAppear(animated)
|
||
updateMetrics()
|
||
}
|
||
}
|
||
|
||
extension LDFirewallViewController: Loadable {
|
||
|
||
@objc func accountStateDidChange() {
|
||
updateActiveSubscription()
|
||
}
|
||
|
||
func updateProtectionPlanUI() {
|
||
if UserDefaults.hasSeenUniversalPaywall {
|
||
updateUI()
|
||
protectionPlanLabel.text = "Universal protection"
|
||
} else if UserDefaults.hasSeenAnonymousPaywall {
|
||
updateUI()
|
||
protectionPlanLabel.text = "Anonymous protection"
|
||
} else if UserDefaults.hasSeenAdvancedPaywall {
|
||
updateUI()
|
||
protectionPlanLabel.text = "Advanced protection"
|
||
} else {
|
||
protectionPlanLabel.text = "Basic protection"
|
||
}
|
||
}
|
||
|
||
func updateUI() {
|
||
firewallTitle.isHidden = true
|
||
firewallDescriptionLabel1.isHidden = true
|
||
firewallDescriptionLabel2.isHidden = true
|
||
firewallDescriptionLabel3.isHidden = true
|
||
upgradeButton.isHidden = true
|
||
upgradeButton.anchors.height.equal(0)
|
||
cpTrackersGroupView1.lockImage.isHidden = true
|
||
cpTrackersGroupView1.number.isHidden = false
|
||
cpTrackersGroupView2.lockImage.isHidden = true
|
||
cpTrackersGroupView2.number.isHidden = false
|
||
cpTrackersGroupView3.lockImage.isHidden = true
|
||
cpTrackersGroupView3.number.isHidden = false
|
||
}
|
||
|
||
func updateActiveSubscription() {
|
||
showLoadingView()
|
||
// not logged in via email, use receipt
|
||
firstly {
|
||
try Client.signIn()
|
||
}.then { _ in
|
||
try Client.activeSubscriptions()
|
||
}.ensure {
|
||
self.hideLoadingView()
|
||
}.done { [self] subscriptions in
|
||
self.activePlans = subscriptions.map({ $0.planType })
|
||
if let active = subscriptions.first {
|
||
if active.planType == .universalAnnual || active.planType == .universalMonthly {
|
||
protectionPlanLabel.text = "Universal protection"
|
||
updateUI()
|
||
} else if active.planType == .anonymousMonthly || active.planType == .anonymousAnnual {
|
||
updateUI()
|
||
protectionPlanLabel.text = "Anonymous protection"
|
||
} else if active.planType == .advancedMonthly || active.planType == .advancedAnnual {
|
||
updateUI()
|
||
protectionPlanLabel.text = "Advanced protection"
|
||
} else {
|
||
firewallTitle.textColor = .red
|
||
}
|
||
} else {
|
||
firewallTitle.textColor = .red
|
||
}
|
||
}.catch { [self] error in
|
||
DDLogError("Error reloading subscription: \(error.localizedDescription)")
|
||
hideLoadingView()
|
||
if let apiError = error as? ApiError {
|
||
switch apiError.code {
|
||
case kApiCodeNoSubscriptionInReceipt, kApiCodeNoActiveSubscription:
|
||
UserDefaults.hasSeenAdvancedPaywall = false
|
||
case kApiCodeSandboxReceiptNotAllowed:
|
||
UserDefaults.hasSeenAdvancedPaywall = false
|
||
default:
|
||
DDLogError("Error loading plan: API error code - \(apiError.code)")
|
||
UserDefaults.hasSeenAdvancedPaywall = false
|
||
}
|
||
} else {
|
||
DDLogError("Error loading plan: Non-API Error - \(error.localizedDescription)")
|
||
UserDefaults.hasSeenAdvancedPaywall = false
|
||
}
|
||
}
|
||
}
|
||
|
||
@objc func upgrade() {
|
||
let vc = FirewallPaywallViewController()
|
||
present(vc, animated: true)
|
||
}
|
||
|
||
@objc func tunnelStatusDidChange(_ notification: Notification) {
|
||
// Firewall
|
||
if let tunnelProviderSession = notification.object as? NETunnelProviderSession {
|
||
DDLogInfo("VPNStatusDidChange as NETunnelProviderSession with status: \(tunnelProviderSession.status.description)");
|
||
if (!getUserWantsFirewallEnabled()) {
|
||
updateFirewallButtonWithStatus(status: .disconnected)
|
||
}
|
||
else {
|
||
updateFirewallButtonWithStatus(status: tunnelProviderSession.status)
|
||
if (tunnelProviderSession.status == .connected && defaults.bool(forKey: kHasSeenInitialFirewallConnectedDialog) == false) {
|
||
defaults.set(true, forKey: kHasSeenInitialFirewallConnectedDialog)
|
||
// self.tapToActivateFirewallLabel.isHidden = true
|
||
// if (VPNController.shared.status() == .invalid) {
|
||
// self.showVPNSubscriptionDialog(title: NSLocalizedString("🔥🧱 Firewall Activated 🎊🎉", comment: ""), message: NSLocalizedString("Trackers, ads, and other malicious scripts are now blocked in all your apps, even outside of Safari.\n\nGet maximum privacy with a Secure Tunnel that protects connections, anonymizes your browsing, and hides your location.", comment: ""))
|
||
// }
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
@objc func updateMetrics() {
|
||
DispatchQueue.main.async { [unowned self] in
|
||
self.statisitcsView.enabledBoxView.numberLabel.text = String(getTotalEnabled().count)
|
||
self.statisitcsView.disabledBoxView.numberLabel.text = String(getTotalDisabled().count)
|
||
self.statisitcsView.blockedBoxView.numberLabel.text = String(getAllBlockedDomains().count)
|
||
}
|
||
}
|
||
|
||
func toggleFirewall() {
|
||
if (defaults.bool(forKey: kHasAgreedToFirewallPrivacyPolicy) == false) {
|
||
let storyboard = UIStoryboard(name: "Main", bundle: nil)
|
||
let viewController = storyboard.instantiateViewController(withIdentifier: "firewallPrivacyPolicyViewController") as! PrivacyPolicyViewController
|
||
viewController.privacyPolicyKey = kHasAgreedToFirewallPrivacyPolicy
|
||
viewController.parentVC1 = self
|
||
self.present(viewController, animated: true, completion: nil)
|
||
return
|
||
}
|
||
|
||
if getIsCombinedBlockListEmpty() {
|
||
FirewallController.shared.setEnabled(false, isUserExplicitToggle: true)
|
||
self.showPopupDialog(title: NSLocalizedString("No Block Lists Enabled", comment: ""), message: NSLocalizedString("Please tap Block List and enable at least one block list to activate Firewall.", comment: ""), acceptButton: NSLocalizedString("Okay", comment: ""))
|
||
return
|
||
}
|
||
|
||
switch FirewallController.shared.status() {
|
||
case .invalid:
|
||
FirewallController.shared.setEnabled(true, isUserExplicitToggle: true)
|
||
//ensureFirewallWorkingAfterEnabling(waitingSeconds: 5.0)
|
||
case .disconnected:
|
||
updateFirewallButtonWithStatus(status: .connecting)
|
||
FirewallController.shared.setEnabled(true, isUserExplicitToggle: true)
|
||
//ensureFirewallWorkingAfterEnabling(waitingSeconds: 5.0)
|
||
|
||
// checkForAskRating()
|
||
case .connected:
|
||
updateFirewallButtonWithStatus(status: .disconnecting)
|
||
FirewallController.shared.setEnabled(false, isUserExplicitToggle: true)
|
||
case .connecting, .disconnecting, .reasserting:
|
||
break;
|
||
}
|
||
}
|
||
|
||
func ensureFirewallWorkingAfterEnabling(waitingSeconds: TimeInterval) {
|
||
FirewallController.shared.existingManagerCount { (count) in
|
||
if let count = count, count > 0 {
|
||
DispatchQueue.main.asyncAfter(deadline: .now() + waitingSeconds) {
|
||
DDLogInfo("\(waitingSeconds) seconds passed, checking if Firewall is enabled")
|
||
guard getUserWantsFirewallEnabled() else {
|
||
// firewall shouldn't be enabled, no need to act
|
||
DDLogInfo("User doesn't want Firewall enabled, no action")
|
||
return
|
||
}
|
||
|
||
let status = FirewallController.shared.status()
|
||
switch status {
|
||
case .connecting, .disconnecting, .reasserting:
|
||
// check again in three seconds
|
||
DDLogInfo("Firewall is in transient state, will check again in 3 seconds")
|
||
self.ensureFirewallWorkingAfterEnabling(waitingSeconds: 3.0)
|
||
case .connected:
|
||
// all good
|
||
DDLogInfo("Firewall is connected, no action")
|
||
break
|
||
case .disconnected, .invalid:
|
||
// we suppose that the connection is somehow broken, trying to fix
|
||
DDLogInfo("Firewall is not connected even though it should be, attempting to fix")
|
||
self.showFixFirewallConnectionDialog {
|
||
FirewallController.shared.deleteConfigurationAndAddAgain()
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
DDLogInfo("No Firewall configurations in settings (likely fresh install): not checking")
|
||
return
|
||
}
|
||
}
|
||
}
|
||
|
||
func updateFirewallButtonWithStatus(status: NEVPNStatus) {
|
||
DDLogInfo("UpdateFirewallButton")
|
||
switch status {
|
||
case .connected:
|
||
LatestKnowledge.isFirewallEnabled = true
|
||
case .disconnected:
|
||
LatestKnowledge.isFirewallEnabled = false
|
||
default:
|
||
break
|
||
}
|
||
updateToggleButtonWithStatus(lastStatus: lastFirewallStatus,
|
||
newStatus: status,
|
||
switchControl: firewallSwitchControl)
|
||
}
|
||
|
||
func updateToggleButtonWithStatus(lastStatus: NEVPNStatus?,
|
||
newStatus: NEVPNStatus,
|
||
switchControl: CustomUISwitch) {
|
||
DDLogInfo("UpdateToggleButton")
|
||
if (newStatus == lastStatus) {
|
||
DDLogInfo("No status change from last time, ignoring.");
|
||
}
|
||
else {
|
||
DispatchQueue.main.async() {
|
||
switch newStatus {
|
||
case .connected:
|
||
switchControl.status = true
|
||
case .connecting:
|
||
switchControl.status = true
|
||
case .disconnected, .invalid:
|
||
switchControl.status = false
|
||
case .disconnecting:
|
||
switchControl.status = false
|
||
case .reasserting:
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|