如何确定iOS设备的连接类型(Edge、3G、4G、Wifi)?

28

如何确定iOS的连接类型?

我想知道此刻我的设备是否有Edge、3G、4G或Wifi可用。

这些信息是我需要加载不同大小的不同数据。

因此,我不希望在“Edge”连接类型下加载4 MB的图像。


请查看以下链接:https://github.com/Isuru-Nanayakkara/Swift-Reachability - Kalpesh
9个回答

26

对于 Swift,我们可以使用以下函数:

func getNetworkType()->String {
    do{
        let reachability:Reachability = try Reachability.reachabilityForInternetConnection()
        do{
            try reachability.startNotifier()
            let status = reachability.currentReachabilityStatus
            if(status == .NotReachable){
                return ""
            }else if (status == .ReachableViaWiFi){
                return "Wifi"
            }else if (status == .ReachableViaWWAN){
                let networkInfo = CTTelephonyNetworkInfo()
                let carrierType = networkInfo.currentRadioAccessTechnology
                switch carrierType{
                case CTRadioAccessTechnologyGPRS?,CTRadioAccessTechnologyEdge?,CTRadioAccessTechnologyCDMA1x?: return "2G"
                case CTRadioAccessTechnologyWCDMA?,CTRadioAccessTechnologyHSDPA?,CTRadioAccessTechnologyHSUPA?,CTRadioAccessTechnologyCDMAEVDORev0?,CTRadioAccessTechnologyCDMAEVDORevA?,CTRadioAccessTechnologyCDMAEVDORevB?,CTRadioAccessTechnologyeHRPD?: return "3G"
                case CTRadioAccessTechnologyLTE?: return "4G"
                default: return ""
                }


            }else{
                return ""
            }
        }catch{
            return ""
        }

    }catch{
        return ""
    }


}

我相信这需要使用Reachability库和CoreTelephony。 - matt.writes.code
5
谢谢你的回复。我整理了所有内容,并引入了一个小枚举类型,因为我不喜欢(空)字符串返回类型。只是分享一下,以防有人想使用:https://gist.github.com/floriankrueger/46e6f9f4c910b5313a72b358fdad25cc - floriankrueger
在Swift 5中,这会抛出“String”类型的表达式模式无法匹配“[String:String]”类型值的错误。 - Dan
谢谢,这对我有用。currentRadioAccessTechnology现在是serviceCurrentRadioAccessTechnology。 - A.HEDDAR
有更简洁的方法,不需要可达性等等... - Kashkashio

9

Swift 5,支持iOS 12.0或更高版本:

import SystemConfiguration
import CoreTelephony
import Foundation

class func getConnectionType() -> String {
        guard let reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, "www.google.com") else {
            return "NO INTERNET"
        }

        var flags = SCNetworkReachabilityFlags()
        SCNetworkReachabilityGetFlags(reachability, &flags)

        let isReachable = flags.contains(.reachable)
        let isWWAN = flags.contains(.isWWAN)

        if isReachable {
            if isWWAN {
                let networkInfo = CTTelephonyNetworkInfo()
                let carrierType = networkInfo.serviceCurrentRadioAccessTechnology

                guard let carrierTypeName = carrierType?.first?.value else {
                    return "UNKNOWN"
                }

                switch carrierTypeName {
                case CTRadioAccessTechnologyGPRS, CTRadioAccessTechnologyEdge, CTRadioAccessTechnologyCDMA1x:
                    return "2G"
                case CTRadioAccessTechnologyLTE:
                    return "4G"
                default:
                    return "3G"
                }
            } else {
                return "WIFI"
            }
        } else {
            return "NO INTERNET"
        }
    }

1
我知道现在可能还为时过早,但是有没有办法在 iPhone 12+ 上检测到 5G 连接? - Ahmadreza
1
我认为目前还不可能,因为苹果定义的最后一个无线电接入技术值是CTRadioAccessTechnologyLTE。但是,您可以尝试以下内容:CTRadioAccessTechnologyGPRS、CTRadioAccessTechnologyEdge、CTRadioAccessTechnologyCDMA1x -> 2g。CTRadioAccessTechnologyWCDMA、CTRadioAccessTechnologyHSDPA、CTRadioAccessTechnologyHSUPA、CTRadioAccessTechnologyCDMAEVDORev0、CTRadioAccessTechnologyCDMAEVDORevA、CTRadioAccessTechnologyCDMAEVDORevB、CTRadioAccessTechnologyeHRPD -> 3GCTRadioAccessTechnologyLTE -> 4G否则 -> 5G。 - Gastón Antonio Montes
这对你来说有意义吗? - Gastón Antonio Montes
我无法测试它,因为在阿根廷我们仍然没有5G连接哈哈。 - Gastón Antonio Montes
4
对于5G,现在有CTRadioAccessTechnologyNRNSA和CTRadioAccessTechnologyNR。 - ReDetection

5

在Objective C中:

首先导入 @import CoreTelephony。

Reachability *reachability = [Reachability reachabilityForInternetConnection];

    [reachability startNotifier];

    NetworkStatus status = [reachability currentReachabilityStatus];

    if(status == NotReachable)
    {
        //No internet
    }
    else if (status == ReachableViaWiFi)
    {
        //WiFi
    }
    else if (status == ReachableViaWWAN)
    {
        CTTelephonyNetworkInfo *netinfo = [[CTTelephonyNetworkInfo alloc] init];
        NSString * carrierType = netinfo.currentRadioAccessTechnology;
        if ([carrierType isEqualToString:CTRadioAccessTechnologyGPRS]) {
//             @"2G";
        } else if ([carrierType isEqualToString:CTRadioAccessTechnologyEdge]) {
//             @"2G";
        } else if ([carrierType isEqualToString:CTRadioAccessTechnologyWCDMA]) {
//             @"3G";
        } else if ([carrierType isEqualToString:CTRadioAccessTechnologyHSDPA]) {
//             @"3G";
        } else if ([carrierType isEqualToString:CTRadioAccessTechnologyHSUPA]) {
//             @"3G";
        } else if ([carrierType isEqualToString:CTRadioAccessTechnologyCDMA1x]) {
//             @"2G";
        } else if ([carrierType isEqualToString:CTRadioAccessTechnologyCDMAEVDORev0]) {
//             @"3G";
        } else if ([carrierType isEqualToString:CTRadioAccessTechnologyCDMAEVDORevA]) {
//             @"3G";
        } else if ([carrierType isEqualToString:CTRadioAccessTechnologyCDMAEVDORevB]) {
//             @"3G";
        } else if ([carrierType isEqualToString:CTRadioAccessTechnologyeHRPD]) {
//             @"3G";
        } else if ([carrierType isEqualToString:CTRadioAccessTechnologyLTE]) {
//             @"4G";
        }

    }

4

需要使用CoreTelephony.framework。

let networkInfo = CTTelephonyNetworkInfo()
let networkString = networkInfo.currentRadioAccessTechnology

if networkString == CTRadioAccessTechnologyLTE{
  // LTE (4G)
}else if networkString == CTRadioAccessTechnologyWCDMA{
  // 3G 
}else if networkString == CTRadioAccessTechnologyEdge{
  // EDGE (2G)
}

"telefonyInfo" 是什么? - Umit Kaya
1
这是关于用户的移动服务提供商,这意味着我将无法检查连接类型是否为wifi,对吗? - Ahmad F

3

您可以使用苹果的CTTelephonyNetworkInfo类。 您可以在iOS 7+上使用此API

您需要导入它: @import CoreTelephony


1
这并不会告诉你手机是否连接了WiFi - 在这种情况下,如果没有运营商,你声明的CTTelephonyNetworkInfo对象将为nil。 - Fuad Kamal
1
提供此答案的链接或片段会更有帮助:现在它提供的信息比谷歌搜索还少(我们希望,提问者已经进行了谷歌搜索)。 - Marco Massenzio

2
import CoreTelephony

enum RadioAccessTechnology: String {
    case cdma = "CTRadioAccessTechnologyCDMA1x"
    case edge = "CTRadioAccessTechnologyEdge"
    case gprs = "CTRadioAccessTechnologyGPRS"
    case hrpd = "CTRadioAccessTechnologyeHRPD"
    case hsdpa = "CTRadioAccessTechnologyHSDPA"
    case hsupa = "CTRadioAccessTechnologyHSUPA"
    case lte = "CTRadioAccessTechnologyLTE"
    case rev0 = "CTRadioAccessTechnologyCDMAEVDORev0"
    case revA = "CTRadioAccessTechnologyCDMAEVDORevA"
    case revB = "CTRadioAccessTechnologyCDMAEVDORevB"
    case wcdma = "CTRadioAccessTechnologyWCDMA"

    var description: String {
        switch self {
        case .gprs, .edge, .cdma:
            return "2G"
        case .lte:
            return "4G"
        default:
            return "3G"
        }
    }
}    

// Example:
let networkInfo = CTTelephonyNetworkInfo()
let networkString = networkInfo.currentRadioAccessTechnology
let tecnology = RadioAccessTechnology(rawValue: networkString)

print(tecnology.description)

当您通过Wi-Fi连接时,此代码将返回4G。 - Sam Bing
你应该在你的RadioAccessTechnology声明中添加CustomStringConvertible,以便能够打印你的对象,而不是访问它的description属性。 - Leo Dabus

2
快速更新 Xcode:10.2.1
更新 Swift 4.2:
func updateUserInterface() {
    switch Network.reachability.status {
    case .unreachable:
        view.backgroundColor = .black
        alertNoReachable("Network Unreachable", "Please check The Settings")
    case .wwan:
        self.imageNoConnection.isHidden = true
        let networkInfo = CTTelephonyNetworkInfo()
        let carrierType = networkInfo.serviceCurrentRadioAccessTechnology
        //print (carrierType?[0])
        // CTRadioAccessTechnologyLTE
        if carrierType!.first(where: { (_, value) in value.contains("CTRadioAccessTechnologyLTE") }) != nil {
            self.networkType = "4G"
             print ("Wwan : 4G")
           // view.backgroundColor = .magenta
        }
            // 2g CTRadioAccessTechnologyGPRS?,CTRadioAccessTechnologyEdge?,CTRadioAccessTechnologyCDMA1x?
        else if carrierType!.first(where: { (_, value) in value.contains("CTRadioAccessTechnologyGPRS") }) != nil || carrierType!.first(where: { (_, value) in value.contains("CTRadioAccessTechnologyEdge") }) != nil  || carrierType!.first(where: { (_, value) in value.contains("CTRadioAccessTechnologyCDMA1x") }) != nil {
            self.networkType = "2G"
            print("Wwan : 2G")
            //view.backgroundColor = .gray
        }
          // 3g CTRadioAccessTechnologyWCDMA?,CTRadioAccessTechnologyHSDPA?,CTRadioAccessTechnologyHSUPA?,CTRadioAccessTechnologyCDMAEVDORev0?,CTRadioAccessTechnologyCDMAEVDORevA?,CTRadioAccessTechnologyCDMAEVDORevB?,CTRadioAccessTechnologyeHRPD?
        else if carrierType!.first(where: { (_, value) in value.contains("CTRadioAccessTechnologyWCDMA") }) != nil || carrierType!.first(where: { (_, value) in value.contains("CTRadioAccessTechnologyHSDPA") }) != nil  || carrierType!.first(where: { (_, value) in value.contains("CTRadioAccessTechnologyHSUPA") }) != nil || carrierType!.first(where: { (_, value) in value.contains("CTRadioAccessTechnologyCDMAEVDORev0") }) != nil  || carrierType!.first(where: { (_, value) in value.contains("CTRadioAccessTechnologyCDMAEVDORevA") }) != nil || carrierType!.first(where: { (_, value) in value.contains("CTRadioAccessTechnologyCDMAEVDORevB") }) != nil || carrierType!.first(where: { (_, value) in value.contains("CTRadioAccessTechnologyeHRPD") }) != nil  {
            self.networkType = "3G"
            print ("Wwan : 3G")
            //view.backgroundColor = .red
        }
    case .wifi:
        self.imageNoConnection.isHidden = true
        self.networkType = "WiFi"
       // view.backgroundColor = .green
    }
    print("Reachability Summary")
    print("Status:", Network.reachability.status)
    print("HostName:", Network.reachability.hostname ?? "nil")
    print("Reachable:", Network.reachability.isReachable)
    print("Wifi:", Network.reachability.isReachableViaWiFi)
}
@objc func statusManager(_ notification: Notification) {
    updateUserInterface()
}

Reachability.swift

import Foundation
import SystemConfiguration

class Reachability {
    var hostname: String?
    var isRunning = false
    var isReachableOnWWAN: Bool
    var reachability: SCNetworkReachability?
    var reachabilityFlags = SCNetworkReachabilityFlags()
    let reachabilitySerialQueue = DispatchQueue(label: "ReachabilityQueue")
    init?(hostname: String) throws {
        guard let reachability = SCNetworkReachabilityCreateWithName(nil, hostname) else {
            throw Network.Error.failedToCreateWith(hostname)
        }
        self.reachability = reachability
        self.hostname = hostname
        isReachableOnWWAN = true
        try start()
    }
    init() throws {
        var zeroAddress = sockaddr_in()
        zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
        zeroAddress.sin_family = sa_family_t(AF_INET)
        guard let reachability = withUnsafePointer(to: &zeroAddress, {
            $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
                SCNetworkReachabilityCreateWithAddress(nil, $0)
            }
        }) else {
            throw Network.Error.failedToInitializeWith(zeroAddress)
        }

        self.reachability = reachability
        isReachableOnWWAN = true

        try start()
    }

    var status: Network.Status {
        return  !isConnectedToNetwork ? .unreachable :
                isReachableViaWiFi    ? .wifi :
                isRunningOnDevice     ? .wwan : .unreachable
    }

    var isRunningOnDevice: Bool = {
        #if targetEnvironment(simulator)
            return false
        #else
            return true
        #endif
    }()
    deinit { stop() }
}


extension Reachability {

    func start() throws {
        guard let reachability = reachability, !isRunning else { return }
        var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
        context.info = Unmanaged<Reachability>.passUnretained(self).toOpaque()
        guard SCNetworkReachabilitySetCallback(reachability, callout, &context) else { stop()
            throw Network.Error.failedToSetCallout
        }
        guard SCNetworkReachabilitySetDispatchQueue(reachability, reachabilitySerialQueue) else { stop()
            throw Network.Error.failedToSetDispatchQueue
        }
        reachabilitySerialQueue.async { self.flagsChanged() }
        isRunning = true
    }

    func stop() {
        defer { isRunning = false }
        guard let reachability = reachability else { return }
        SCNetworkReachabilitySetCallback(reachability, nil, nil)
        SCNetworkReachabilitySetDispatchQueue(reachability, nil)
        self.reachability = nil
    }

    var isConnectedToNetwork: Bool {
        return isReachable &&
               !isConnectionRequiredAndTransientConnection &&
               !(isRunningOnDevice && isWWAN && !isReachableOnWWAN)
    }

    var isReachableViaWiFi: Bool {
        return isReachable && isRunningOnDevice && !isWWAN
    }

    /// Flags that indicate the reachability of a network node name or address, including whether a connection is required, and whether some user intervention might be required when establishing a connection.
    var flags: SCNetworkReachabilityFlags? {
        guard let reachability = reachability else { return nil }
        var flags = SCNetworkReachabilityFlags()
        return withUnsafeMutablePointer(to: &flags) {
            SCNetworkReachabilityGetFlags(reachability, UnsafeMutablePointer($0))
            } ? flags : nil
    }

    /// compares the current flags with the previous flags and if changed posts a flagsChanged notification
    func flagsChanged() {
        guard let flags = flags, flags != reachabilityFlags else { return }
        reachabilityFlags = flags
        NotificationCenter.default.post(name: .flagsChanged, object: self)
    }

    /// The specified node name or address can be reached via a transient connection, such as PPP.
    var transientConnection: Bool { return flags?.contains(.transientConnection) == true }

    /// The specified node name or address can be reached using the current network configuration.
    var isReachable: Bool { return flags?.contains(.reachable) == true }

    /// The specified node name or address can be reached using the current network configuration, but a connection must first be established. If this flag is set, the kSCNetworkReachabilityFlagsConnectionOnTraffic flag, kSCNetworkReachabilityFlagsConnectionOnDemand flag, or kSCNetworkReachabilityFlagsIsWWAN flag is also typically set to indicate the type of connection required. If the user must manually make the connection, the kSCNetworkReachabilityFlagsInterventionRequired flag is also set.
    var connectionRequired: Bool { return flags?.contains(.connectionRequired) == true }

    /// The specified node name or address can be reached using the current network configuration, but a connection must first be established. Any traffic directed to the specified name or address will initiate the connection.
    var connectionOnTraffic: Bool { return flags?.contains(.connectionOnTraffic) == true }

    /// The specified node name or address can be reached using the current network configuration, but a connection must first be established.
    var interventionRequired: Bool { return flags?.contains(.interventionRequired) == true }

    /// The specified node name or address can be reached using the current network configuration, but a connection must first be established. The connection will be established "On Demand" by the CFSocketStream programming interface (see CFStream Socket Additions for information on this). Other functions will not establish the connection.
    var connectionOnDemand: Bool { return flags?.contains(.connectionOnDemand) == true }

    /// The specified node name or address is one that is associated with a network interface on the current system.
    var isLocalAddress: Bool { return flags?.contains(.isLocalAddress) == true }

    /// Network traffic to the specified node name or address will not go through a gateway, but is routed directly to one of the interfaces in the system.
    var isDirect: Bool { return flags?.contains(.isDirect) == true }

    /// The specified node name or address can be reached via a cellular connection, such as EDGE or GPRS.
    var isWWAN: Bool { return flags?.contains(.isWWAN) == true }

    /// The specified node name or address can be reached using the current network configuration, but a connection must first be established. If this flag is set
    /// The specified node name or address can be reached via a transient connection, such as PPP.
    var isConnectionRequiredAndTransientConnection: Bool {
        return (flags?.intersection([.connectionRequired, .transientConnection]) == [.connectionRequired, .transientConnection]) == true
    }
}



func callout(reachability: SCNetworkReachability, flags: SCNetworkReachabilityFlags, info: UnsafeMutableRawPointer?) {
    guard let info = info else { return }
    DispatchQueue.main.async {
        Unmanaged<Reachability>
            .fromOpaque(info)
            .takeUnretainedValue()
            .flagsChanged()
    }
}

extension Notification.Name {
    static let flagsChanged = Notification.Name("FlagsChanged")
}


struct Network {
    static var reachability: Reachability!
    enum Status: String {
        case unreachable, wwan, wifi

    }
    enum Error: Swift.Error {
        case failedToSetCallout
        case failedToSetDispatchQueue
        case failedToCreateWith(String)
        case failedToInitializeWith(sockaddr_in)
    }
}

AppDelegate.swift

import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?


func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.

    // network reachable
    do {
        try Network.reachability = Reachability(hostname: "www.google.com")
    }
    catch {
        switch error as? Network.Error {
        case let .failedToCreateWith(hostname)?:
            print("Network error:\nFailed to create reachability object With host named:", hostname)
        case let .failedToInitializeWith(address)?:
            print("Network error:\nFailed to initialize reachability object With address:", address)
        case .failedToSetCallout?:
            print("Network error:\nFailed to set callout")
        case .failedToSetDispatchQueue?:
            print("Network error:\nFailed to set DispatchQueue")
        case .none:
            print(error)
        }
    }
    //
    return true
}

func applicationWillResignActive(_ application: UIApplication) {
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}

func applicationDidEnterBackground(_ application: UIApplication) {
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}

func applicationWillEnterForeground(_ application: UIApplication) {
    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}

func applicationDidBecomeActive(_ application: UIApplication) {
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}

func applicationWillTerminate(_ application: UIApplication) {
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

}

来源


0
我找到了一个不依赖于第三方库的很好的模块化 Swift 实现,它非常直观易懂,而且非常轻量级。可以在这里获得,但为了完整起见,在此附上代码:
//
//  NetStatus.swift
//  NetStatusDemo
//
//  Created by Gabriel Theodoropoulos.
//  Copyright © 2019 Appcoda. All rights reserved.
//

import Foundation
import Network

class NetStatus {

    // MARK: - Properties

    static let shared = NetStatus()

    var monitor: NWPathMonitor?

    var isMonitoring = false

    var didStartMonitoringHandler: (() -> Void)?

    var didStopMonitoringHandler: (() -> Void)?

    var netStatusChangeHandler: (() -> Void)?


    var isNetStatusConnected: Bool {
        guard let monitor = monitor else { return false }
        return monitor.currentPath.status == .satisfied
    }


    var interfaceType: NWInterface.InterfaceType? {
        guard let monitor = monitor else { return nil }

        return monitor.currentPath.availableInterfaces.filter {
            monitor.currentPath.usesInterfaceType($0.type) }.first?.type
    }


    var availableInterfacesTypes: [NWInterface.InterfaceType]? {
        guard let monitor = monitor else { return nil }
        return monitor.currentPath.availableInterfaces.map { $0.type }
    }


    var isExpensive: Bool {
        return monitor?.currentPath.isExpensive ?? false
    }


    // MARK: - Init & Deinit

    private init() {

    }


    deinit {
        stopMonitoring()
    }


    // MARK: - Method Implementation

    func startMonitoring() {
        guard !isMonitoring else { return }

        monitor = NWPathMonitor()
        let queue = DispatchQueue(label: "NetStatus_Monitor")
        monitor?.start(queue: queue)

        monitor?.pathUpdateHandler = { _ in
            self.netStatusChangeHandler?()
        }

        isMonitoring = true
        didStartMonitoringHandler?()
    }


    func stopMonitoring() {
        guard isMonitoring, let monitor = monitor else { return }
        monitor.cancel()
        self.monitor = nil
        isMonitoring = false
        didStopMonitoringHandler?()
    }

}

这是一个非常好的和干净的解决方案,用于检查设备是否连接到互联网,但并没有解决用户所提出的问题。 - AntonioWar

0

我发现这个实现方式,使用iOS 12.0+的serviceCurrentRadioAccessTechnology和switch语句,非常适合我的需求。

@objc var getCellularNetworkType: String {  
    guard (monitor != nil) else { return "" }
    guard isCellular else { return "" }
    let networkInfo = CTTelephonyNetworkInfo()
    let carrierTypeString = networkInfo.serviceCurrentRadioAccessTechnology!.values.first!
    switch carrierTypeString {
    case CTRadioAccessTechnologyGPRS,CTRadioAccessTechnologyEdge,CTRadioAccessTechnologyCDMA1x: return "2G"
    case CTRadioAccessTechnologyWCDMA,CTRadioAccessTechnologyHSDPA,CTRadioAccessTechnologyHSUPA,CTRadioAccessTechnologyCDMAEVDORev0,CTRadioAccessTechnologyCDMAEVDORevA,CTRadioAccessTechnologyCDMAEVDORevB,CTRadioAccessTechnologyeHRPD: return "3G"
    case CTRadioAccessTechnologyLTE: return "4G"
    default: return ""
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接