iOS Swift/Network

[Swift] vpn 연결 확인 iOS detect vpn NetworkCopySystemProxySettings

주니어코더 2024. 6. 18. 20:00

 

 

 

 

 

 

 

 

iOS vpn 연결 상태 확인

 

 

오늘은 iOS에서 VPN 연결 확인 방법을 알아보자

 

아이폰 설정 → VPN → VPN 상태에서 VPN의 연결상태를 알 수 있다

 

VPN연결 상태를 앱에서 가져올 수 있는 방법이 여러 가지 있는데,

그중 "시스템 프록시 설정 값을 파싱"하는 방법을 사용하여

VPN 연결 상태를 알아보자

 

 

 

 

 

 

CFNetworkCopySystemProxySettings


 

CFNetworkCopySystemProxySettings()는 무엇일까?

이름 그대로 시스템 프록시 설정 값을 파싱 하는 함수이다

 

Apple Developer Documentation을 살펴보자

 

https://developer.apple.com/documentation/cfnetwork/cfnetworkcopysystemproxysettings()

 

CFNetworkCopySystemProxySettings() | Apple Developer Documentation

Returns a CFDictionary containing the current systemwide internet proxy settings.

developer.apple.com

 

 

오늘 우리가 사용할 함수 

func CFNetworkCopySystemProxySettings() -> Unmanaged<CFDictionary>?

 

 

CFNetworkCopySystemProxySettings

Returns a CFDictionary containing the current systemwide internet proxy settings.

 

The dictionary returned contains key-value pairs that represent the current internet proxy settings. The keys in this dictionary are defined in Global Proxy Settings Constants.

The caller is responsible for releasing the returned dictionary.

 

 

🌏 번역 🌏

CFNetworkCopySystemProxySettings는

현재 시스템 전체 인터넷 프록시 설정이 포함된 CFDictionary를 반환합니다.

 

반환된 사전에는 현재 인터넷 프록시 설정을 나타내는 키-값 쌍이 포함되어 있습니다. 이 사전의 키는 전역 프록시 설정 상수에 정의되어 있습니다.

호출자는 반환된 사전을 해제할 책임이 있습니다.

 

 

한마디로 해석하면
CFNetworkCopySystemProxySettings함수는 현재 시스템 전체 인터넷 프록시 설정을 Dictionary로 반환한다

 

이 함수의 반환값을 통해 프록시 설정 값을 알 수 있다.

 

 

반환된 Dictionary의 프록시 설정값을 파싱 하여 VPN 상태를 알아보자!

 

 

 

 

 

Chap1.  SystemConfiguration 라이브러리 Import


 

CFNetworkCopySystemProxySettings() 함수를 사용하려는 Class에 SystemConfiguration를 import 한다

 

import SystemConfiguration

 


Apple Developer Documentation에서 SystemConfiguration을 살펴보면,

 

SystemConfiguration은 애플리케이션이 기기의 네트워크 구성 설정에 액세스 할 수 있도록 허용합니다. Wi-Fi 또는 셀 연결이 활성화되어 있는지 등 장치의 연결 가능성을 확인합니다.

 

VPN 외에도 네트워크 구성 설정 정보를 얻어올 수 있다.

 

 

 

 

 

Chap2.  CFNetworkCopySystemProxySettings 함수로 네트워크 구성 확인



CFNetworkCopySystemProxySettings() 함수를 사용하여 네트워크 구성이 존재하는지 알아보자

 

 

    guard let cfDict = CFNetworkCopySystemProxySettings() else {
        print("Failed to get proxy settings")
        return false
    }

 

 

위 코드를 설명하면

 

CFNetworkCopySystemProxySettings()를 호출하여 네트워크 구성 요소가 존재하면 구성 요소 Dictionary를 반환하고,
없다면 false를 반환한다.

 

 

 

 

 

Chap3.  네트워크 구성 요소 분석


 

 

위에서 CFNetworkCopySystemProxySettings() 함수를 호출하여 return 받은 Dictionary 값을 분석하자

 

 

class CheckVpnStatus {
    func isVPNConnected() -> Bool {
        guard let cfDict = CFNetworkCopySystemProxySettings() else {
            print("Failed to get proxy settings")
            return false
        }
        
        let nsDict = cfDict.takeRetainedValue() as NSDictionary
        print("Proxy settings: \(nsDict)")
        
        guard let keys = nsDict["__SCOPED__"] as? NSDictionary else {
            print("No __SCOPED__ key found in proxy settings")
            return false
        }
        
        print("Scoped keys: \(keys.allKeys)")
        
        for key in keys.allKeys as! [String] {
            if key == "tap" || key == "tun" || key == "ppp" || key == "ipsec" || key == "ipsec0" || key.starts(with: "utun") {
                print("VPN is connected via \(key)")
                return true
            }
        }
        print("No VPN connection detected")
        return false
    }
}

 


❌ VPN 연결되지 않은 경우

CFNetworkCopySystemProxySettings() 함수 return X → VPN 연결: false

 

Dictionary에 "__SCOPED__"가 존재하지 X  VPN 연결: false

 

"__SCOPED__"Key에서 "ppp", "tun", "ipsec", "ipsec0" 값 중 하나도 존재하지 X VPN 연결: false

 

 


⭕️ VPN 연결된 경우

 

CFNetworkCopySystemProxySettings()의 Dictionary가 존재하며,

 

Dictionary에 "__SCOPED__"가 존재하며,

 

"__SCOPED__"Key에서 "ppp", "tun", "ipsec", "ipsec0" 값 중 하나도 존재하면 → VPN 연결: true

 

 

VPN이 연결되면 네트워크 구성 요소에서 "ppp", "tun", "ipsec", "ipsec0" 중 값이 하나라도 존재한다.

 

 

 

 

 

Chap4.  예제


 

위 내용을 활용해 버튼을 클릭하여 VPN 연결 상태를 확인하는 예제를 만들어보자

 

 

- CheckVpnStatus.Swift

//
//  CheckVpnStatus.swift
//  VpnStatus
//
//  Created by hyeebin on 6/17/24.
//

import Foundation
import SystemConfiguration

class CheckVpnStatus {
    func isVPNConnected() -> Bool {
        guard let cfDict = CFNetworkCopySystemProxySettings() else {
            print("Failed to get proxy settings")
            return false
        }
        
        let nsDict = cfDict.takeRetainedValue() as NSDictionary
        print("Proxy settings: \(nsDict)")
        
        guard let keys = nsDict["__SCOPED__"] as? NSDictionary else {
            print("No __SCOPED__ key found in proxy settings")
            return false
        }
        
        print("Scoped keys: \(keys.allKeys)")
        
        for key in keys.allKeys as! [String] {
            if key == "tap" || key == "tun" || key == "ppp" || key == "ipsec" || key == "ipsec0" || key.starts(with: "utun") {
                print("VPN is connected via \(key)")
                return true
            }
        }
        print("No VPN connection detected")
        return false
    }
}

 

 

 

 

- ViewController.Swift

//
//  ViewController.swift
//  VpnStatus
//
//  Created by hyeebin on 6/17/24.
//

import UIKit

class ViewController: UIViewController {
    
    lazy var lbResult: UILabel = {
        let lbResult = UILabel()
        lbResult.translatesAutoresizingMaskIntoConstraints = false
        lbResult.text = "VPN 상태"
        lbResult.numberOfLines = 0
        lbResult.textAlignment = .center
        lbResult.font = UIFont.systemFont(ofSize: 28)
        return lbResult
    }()
    
    lazy var btnStatus: UIButton = {
        let button = UIButton(type: .system)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setTitle("VPN 상태 확인", for: .normal)
        button.backgroundColor = .systemBlue
        button.setTitleColor(.white, for: .normal)
        button.layer.cornerRadius = 10
        button.addTarget(self, action: #selector(clickStatus), for: .touchUpInside)
        return button
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        addViews()
        applyConstraints()
    }
    
    fileprivate func addViews() {
        view.addSubview(lbResult)
        view.addSubview(btnStatus)
    }
    
    fileprivate func applyConstraints() {
        let lbResultConstraints = [
            lbResult.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            lbResult.topAnchor.constraint(equalTo: view.topAnchor,constant: 300),
         ]
        
        let btnStatusConstraints = [
            btnStatus.topAnchor.constraint(equalTo: lbResult.bottomAnchor,constant: 60),
            btnStatus.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -60),
            btnStatus.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 60),
            btnStatus.heightAnchor.constraint(equalToConstant: 50)
         ]
        
        NSLayoutConstraint.activate(btnStatusConstraints)
        NSLayoutConstraint.activate(lbResultConstraints)
    }

    @objc fileprivate func clickStatus() {
        getVpnStatus()
    }
    
    func getVpnStatus() {
        let checkVpnStatus = CheckVpnStatus()
        let status = checkVpnStatus.isVPNConnected() ? "Connected" : "Not Connected"
        DispatchQueue.main.async { [self] in
            lbResult.text = "VPN 상태: \(status)"
        }
    }
}

 

 

 

 

 

✨ 결과 화면 ✨

 

VPN 상태 - Not Connected

 

 

 

 

 

 VPN 상태 - Connected ⭕️

 

 

 

 

VPN 연결 상태이면,
아이폰 상단에 VPN 아이콘이 표시된다!

 

VPN상태이고 VPN 상태: Connected라고 가져온다

 

 

 

 

 

 

 

 

 

 👇 깃허브에서 전체 소스코드 보기 👇

https://github.com/hyeebin/vpnstatus-ios

 

GitHub - hyeebin/vpnstatus-ios: ios에서 vpn 상태 가져오기

ios에서 vpn 상태 가져오기. Contribute to hyeebin/vpnstatus-ios development by creating an account on GitHub.

github.com

 

 

 

참조: https://forums.developer.apple.com/forums/thread/650650

반응형