iPhone X獲取裝置網路型別錯誤分析

搶手的哥發表於2017-12-14

背景

在公司專案開發中,需要在網路請求中包含當前裝置的網路狀態引數,發請求的時候手機網路的型別有2G,3G,4G,WIFI等等,獲取引數的方法如下:

獲取網路狀態
- (NSString *)getNetStatus {
    
    NSArray *children = [[[[UIApplication sharedApplication] valueForKeyPath:@"statusBar"] valueForKeyPath:@"foregroundView"] subviews];
    NSString *state = nil;
    int netType = 0;
    for (id child in children) {
        
        if ([child isKindOfClass:NSClassFromString(@"UIStatusBarDataNetworkItemView")]) {
            netType = [[child valueForKeyPath:@"dataNetworkType"] intValue];
            
            switch (netType) {
                    case 0:
                    state = @"無網路";
                    break;
                    case 1:
                    state =  @"2G";
                    break;
                    case 2:
                    state =  @"3G";
                    break;
                    case 3:
                    state =   @"4G";
                    break;
                    case 5:
                    state =  @"WIFI";
                    break;
                default:
                    state =  @"未知網路";
                    break;
            }
        }
    }
    return state;
}

複製程式碼

從程式碼可以看出,基本原理是讀取手機的狀態列,然後遍歷狀態列的子控制元件,檢視看一下蘋果私有屬性UIStatusBarDataNetworkItemView,根據屬性的值來判斷當前手機處於的什麼樣的網路狀態。

iPhone X錯誤&原因分析

當專案程式碼選擇iPhone X模擬器執行的時候,會奔潰在上面的函式。 由於iPhone X獨特的全面屏+劉海的設計(我覺得很醜),手機頂部的狀態列完全是全新的設計,如下面對比截圖:

一般iPhone的狀態列.png

iPhone X的狀態列

於是,想自己來hack一下這個iPhone X的狀態列

//obj 即為被遍歷物件
unsigned int outCount = 0;
Ivar *ivars = class_copyIvarList([obj class], &outCount); 
for (int i = 0; i < outCount; i++) {
     Ivar ivar = ivars[i];
     printf("======== |%s\n", ivar_getName(ivar));
}

之後獲取到以下Path,發現類似dataNetworkType
的_networkTypeView

NSArray *items = [[[[UIApplication sharedApplication] valueForKeyPath:@"statusBar"] valueForKeyPath:@"statusBar"] items];id obj = [[items valueForKeyPath:@"_UIStatusBarCellularExpandedItem"] valueForKeyPath:@"_networkTypeView"];

複製程式碼

image.png

但縱覽整個屬性列表,沒有發現可用的NetworkType,只能棄用。沒辦法,誰讓蘋果爸爸用這樣的特(sha)立(bi)獨(nao)行(can)的設計,所以要另闢蹊徑。

解決方法

通過翻閱文件發現可以通過CTTelephonyNetworkInfo獲取網路運營商相關資訊,這不就是3G,4G技術名稱嘛!!!

匯入 #import <CoreTelephony/CTTelephonyNetworkInfo.h>
點進去檢視:
CORETELEPHONY_EXTERN NSString * const CTRadioAccessTechnologyGPRS          __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_7_0);
CORETELEPHONY_EXTERN NSString * const CTRadioAccessTechnologyEdge          __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_7_0);
CORETELEPHONY_EXTERN NSString * const CTRadioAccessTechnologyWCDMA         __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_7_0);
CORETELEPHONY_EXTERN NSString * const CTRadioAccessTechnologyHSDPA         __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_7_0);
CORETELEPHONY_EXTERN NSString * const CTRadioAccessTechnologyHSUPA         __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_7_0);
CORETELEPHONY_EXTERN NSString * const CTRadioAccessTechnologyCDMA1x        __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_7_0);
CORETELEPHONY_EXTERN NSString * const CTRadioAccessTechnologyCDMAEVDORev0  __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_7_0);
CORETELEPHONY_EXTERN NSString * const CTRadioAccessTechnologyCDMAEVDORevA  __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_7_0);
CORETELEPHONY_EXTERN NSString * const CTRadioAccessTechnologyCDMAEVDORevB  __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_7_0);
CORETELEPHONY_EXTERN NSString * const CTRadioAccessTechnologyeHRPD         __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_7_0);
CORETELEPHONY_EXTERN NSString * const CTRadioAccessTechnologyLTE           __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_7_0);

複製程式碼

之後再次發現蘋果的一個開源類Reachability中已對此做了相應封裝,拿來即用。

typedef enum : NSInteger {
    NotReachable = 0,
    ReachableViaWiFi,
    kReachableVia2G,
    kReachableVia3G,
    kReachableVia4G
} NetworkStatus;

PS:最新版Reachability已簡化列舉
typedef enum : NSInteger {
	NotReachable = 0,
	ReachableViaWiFi,
	ReachableViaWWAN
} NetworkStatus;

複製程式碼

經過改寫後的方法如下

+ (int)getNetStatus {
    
    NSString *stateString = nil;
    int netStatusNumber = 0;
    switch ([[Reachability reachabilityForInternetConnection] currentReachabilityStatus]) {
            
        case NotReachable: {
            
            stateString = @"無網路";
            netStatusNumber = 0;
        }
            break;
        case kReachableVia2G: {
            
            stateString = @"2G";
            netStatusNumber = 2;
        }
            break;
        case kReachableVia3G: {
         
            stateString = @"3G";
            netStatusNumber = 3;
        }
            break;
        case kReachableVia4G: {
            
            stateString = @"4G";
            netStatusNumber = 4;
        }
            break;
        case ReachableViaWiFi: {
            
            stateString = @"WIFI";
            netStatusNumber = 1;
        }
            break;
        default: {
            
            stateString = @"不可識別的網路";
            netStatusNumber = -1;
        }
            break;
    }
    return netStatusNumber;
}
複製程式碼

這樣處理完之後,不管是不是iPhone X都可以獲取到正確的裝置網路型別了,相比之前通過狀態列來判斷,利用蘋果自己的類更加的準確。

儘管這款劉海iPhone X在此刻尚未發售,能早點填一個坑就早點填。

相關文章