由于各個(gè)前端系統(tǒng)的權(quán)限設(shè)置,很多設(shè)備信息都不能得到有效的獲??;所以,公司后臺(tái)同學(xué)經(jīng)常詢問前端同學(xué),你們都能獲取到一些什么字段?發(fā)現(xiàn)自己也很長一段時(shí)間沒有記錄技術(shù)上的知識(shí)點(diǎn)了,借此時(shí)機(jī),將能獲取設(shè)備的信息來統(tǒng)一記錄。
對(duì)于收集設(shè)備信息來說分成兩大塊,第一當(dāng)然是設(shè)備的一些私有和唯一的信息,第二就是App的信息,我們先從簡單的App的信息來說。
第一部分:App的信息
關(guān)于App的信息,我們大概就需要獲取App的版本號(hào)、App的build版本號(hào)、包名、App顯示名稱等等。
//DeviceUtils.h中定義
#pragma mark - About App Config Info Method
/**
* 獲取當(dāng)前App的版本號(hào)信息
*/
+ (NSString *)getAppVersion;
/**
* 獲取當(dāng)前App的build版本號(hào)信息
*/
+ (NSString *)getAppBuildVersion;
/**
* 獲取當(dāng)前App的包名信息
*/
+ (NSString *)getAppBundleId;
/**
* 獲取當(dāng)前App的名稱信息
*/
+ (NSString *)getAppDisplayName;
//DeviceUtils.m中實(shí)現(xiàn)
#pragma mark - About App Config Info Method
/*獲取當(dāng)前App的版本號(hào)信息*/
+ (NSString *)getAppVersion
{
NSBundle *currentBundle = [NSBundle mainBundle];
NSDictionary *infoDictionary = [currentBundle infoDictionary];
return [infoDictionary objectForKey:@"CFBundleShortVersionString"];
}
/*獲取當(dāng)前App的build版本號(hào)信息*/
+ (NSString *)getAppBuildVersion
{
NSBundle *currentBundle = [NSBundle mainBundle];
NSDictionary *infoDictionary = [currentBundle infoDictionary];
return [infoDictionary objectForKey:@"CFBundleVersion"];
}
/*獲取當(dāng)前App的包名信息*/
+ (NSString *)getAppBundleId
{
NSBundle *currentBundle = [NSBundle mainBundle];
NSDictionary *infoDictionary = [currentBundle infoDictionary];
return [infoDictionary objectForKey:@"CFBundleIdentifier"];
}
/*獲取當(dāng)前App的名稱信息*/
+ (NSString *)getAppDisplayName
{
NSBundle *currentBundle = [NSBundle mainBundle];
NSDictionary *infoDictionary = [currentBundle infoDictionary];
return [infoDictionary objectForKey:@"CFBundleDisplayName"];
}
第二部分:設(shè)備的一些私有和唯一的信息
關(guān)于獲取設(shè)備各種標(biāo)識(shí)符,大概統(tǒng)計(jì)了一下。基本上有IDFA、IDFV、IMEI、IMSI、UUID、UDID、MAC地址等等。但隨著對(duì)用戶隱私信息的保護(hù),蘋果系統(tǒng)對(duì)信息的獲取進(jìn)行權(quán)限設(shè)置,譬如IMEI、IMSI等的獲取都明確禁止了,除非你發(fā)布的App不通過蘋果商店的渠道。不管獲取權(quán)限,先來介紹下這些字段的含義。
1、IMEI、IMSI、UUID、UDID、MAC地址
IMEI:英文名稱是:International Mobile Equipment Identity,是國際移動(dòng)設(shè)備身份碼的縮寫,國際移動(dòng)裝備辨識(shí)碼,是由15位數(shù)字組成的”電子串號(hào)”,它與每臺(tái)手機(jī)一一對(duì)應(yīng),而且該碼是全世界唯一的。每一部手機(jī)在組裝完成后都將被賦予一個(gè)全球唯一的一組號(hào)碼,這個(gè)號(hào)碼從生產(chǎn)到交付使用都將被制造生產(chǎn)的廠商所記錄。手機(jī)用戶可以在手機(jī)中查到自己手機(jī)的IMEI碼。因?yàn)殡[私問題,蘋果用戶在iOS5以后不能再獲取IMEI的值了。如果開發(fā)的App不上架蘋果商店,又想獲取IMEI值,可參考此鏈接,獲取IMEI值。
IMSI:英文名稱是:International Mobile Subscriber Identification Number,是國際移動(dòng)用戶識(shí)別碼的縮寫,IMSI分為兩部分:一部分叫MCC(Mobile Country Code移動(dòng)國家碼),MCC的資源由國際電聯(lián)(ITU)統(tǒng)一分配,唯一識(shí)別移動(dòng)用戶所屬的國家,MCC共3位,中國地區(qū)的MCC為460;另一部分叫MNC(Mobile Network Code 移動(dòng)網(wǎng)絡(luò)號(hào)碼),MNC用于識(shí)別移動(dòng)客戶所屬的移動(dòng)網(wǎng)絡(luò)運(yùn)營商。MNC由二到三個(gè)十進(jìn)制數(shù)組成,例如中國移動(dòng)MNC為00、02、07,中國聯(lián)通的MNC為01、06、09,中國電信的MNC為03、05、11。
UUID:英文名稱是:Universally Unique Identifier,翻譯過來就是通用唯一標(biāo)識(shí)符。UUID是指在一臺(tái)機(jī)器上生成的數(shù)字,它保證對(duì)在同一時(shí)空中的所有機(jī)器都是唯一的。通常平臺(tái)會(huì)提供生成的API,是一個(gè)32位的十六進(jìn)制序列,使用小橫線來連接:8-4-4-4-12。由于UUID的本身特性,它保證對(duì)在同一時(shí)空中的所有機(jī)器都是唯一的。所以,需要作為唯一標(biāo)識(shí)碼的話,你可以通過保存在keychain或者NSUserDefaults中。
UDID:所謂UDID指的是設(shè)備的唯一設(shè)備識(shí)別符,移動(dòng)廣告商和游戲網(wǎng)絡(luò)運(yùn)營商往往需要通過UDID用來識(shí)別玩家用戶,并對(duì)用戶活動(dòng)進(jìn)行跟蹤。UDID 在iOS5.0 的時(shí)候已經(jīng)被拋棄使用了。因?yàn)殡[私問題,蘋果宣稱如果第三方應(yīng)用開發(fā)者繼續(xù)分享或者使用iPhone、Mac、AppleWatch的UDID的話,那么他們的應(yīng)用將會(huì)禁止上架。比如我開發(fā)了5款A(yù)pp,很多用戶都下載了這5款A(yù)pp并使用。如果我能輕易的獲取這些用戶的UDID,其實(shí)我能拼湊出用戶的很多信息。由于UDID本身的隱私屬性,之前常常用來做第三方統(tǒng)計(jì)和其他的目的。蘋果就推出了一個(gè)UDID的替代品identifierForVendor的屬性,關(guān)于IDFV的詳細(xì)內(nèi)容在下一點(diǎn)中查看。UDID目前主要用于配置真機(jī)調(diào)試證書。
MAC地址:Mac地址是唯一的,unix有系統(tǒng)調(diào)用可以獲取Mac地址。iPhone可能有多個(gè)Mac地址,wifi的地址、以及SIM卡的地址。一般來講,我們?nèi)n0的地址,因?yàn)樗莍Phone的wifi的地址,是肯定存在的(例外情況依然有:市面上依然存在一部分聯(lián)通的閹割版無wifi的iPhone)。Mac地址涉及到隱私,不應(yīng)該胡亂將用戶的Mac地址傳播,所以我們需要將Mac地址進(jìn)行hash之后再去使用。同樣因?yàn)殡[私問題,在iOS7以后,蘋果禁止獲取MAC地址,系統(tǒng)現(xiàn)在只會(huì)返回02:00:00:00:00:00虛擬的地址。
2、IDFA、IDFV
- IDFA:廣告標(biāo)示符,iOS6及以后使用。用戶可以在設(shè)置中控制IDFA是否允許獲取以及重置,雖然一般用戶都不知道有這個(gè)權(quán)限設(shè)置,所以IDFA是不能拿來當(dāng)做設(shè)備唯一標(biāo)識(shí)(UUID)的替代品。
關(guān)閉IDFA獲取權(quán)限操作:設(shè)置 → 隱私 → 廣告 → 選擇禁止獲取IDFA值
重新生成IDFA操作:
操作一:設(shè)置程序 → 通用 → 還原 → 還原位置與隱私
操作二:設(shè)置程序 → 通用 → 關(guān)于本機(jī) → 廣告 → 還原廣告標(biāo)示符
//導(dǎo)入獲取IDFA的頭文件
#import <AdSupport/ASIdentifierManager.h>
- (NSString *)getDeviceIDFAValue
{
ASIdentifierManager *object = nil;
object = [ASIdentifierManager sharedManager];
return [[object advertisingIdentifier] UUIDString];
}
關(guān)于IDFA的一張很好的圖解:

- IDFV:iOS6.0及以后使用,是給Vendor標(biāo)識(shí)用戶用的,主要用于公司內(nèi)部統(tǒng)計(jì)用戶。如果隸屬一個(gè)相同的組織下的App獲取到的IDFV是同一個(gè)值。例如:
| 編號(hào) | 包名 | IDFV |
|---|---|---|
| 1 | com.test.app1 | 與2相同,與3、4不同 |
| 2 | com.test.app2 | 與1相同,與3、4不同 |
| 3 | com.test.develop.app1 | 與4相同,與1、2不同 |
| 4 | com.test.develop.app2 | 與3相同,與1、2不同 |
準(zhǔn)確點(diǎn)說,就是通過BundleID的反轉(zhuǎn)的前兩部分進(jìn)行匹配,如果相同就是同一個(gè)Vender,共享同一個(gè)IDFV的值。
NSString *idfv = [[UIDevice currentDevice].identifierForVendor UUIDString];
3、設(shè)備運(yùn)營商名稱、運(yùn)營商網(wǎng)絡(luò)
設(shè)備運(yùn)營商名稱:當(dāng)前設(shè)備上的SIM卡代理商名稱。
運(yùn)營商網(wǎng)絡(luò):當(dāng)前設(shè)備上的蜂窩網(wǎng)絡(luò)名稱或WiFi。
4、設(shè)備型號(hào)、設(shè)備的系統(tǒng)名稱、設(shè)備的系統(tǒng)版本號(hào)
設(shè)備型號(hào):主要用來區(qū)分設(shè)備是iPhone 3gs、iPhone 4、iPhone 4s、iPhone 5、iPhone 5s、iPhone 6、iPhone 6s、iPhone 7、iPhone 8、iPhone X等,還有iPad、iWatch等蘋果產(chǎn)品型號(hào)。蘋果沒有提供直接獲取型號(hào)名稱的API方法,所以這個(gè)值獲取出來后,需要在后臺(tái)進(jìn)行映射成對(duì)應(yīng)的設(shè)備型號(hào)。
設(shè)備的系統(tǒng)名稱:蘋果設(shè)備(手機(jī)以及平板)目前只有iOS系統(tǒng),蘋果電腦是Mac OS系統(tǒng)等。
設(shè)備的系統(tǒng)版本號(hào):蘋果設(shè)備的系統(tǒng)版本號(hào),可通過UIDevice類直接獲取到一個(gè)浮點(diǎn)值。
5、設(shè)備的尺寸、設(shè)備的分辨率
設(shè)備的尺寸:蘋果設(shè)備的物理尺寸,可通過UIScreen類直接獲取到bounds屬性進(jìn)行讀取。
設(shè)備的分辨率:蘋果設(shè)備的分辨率,可通過UIScreen類直接獲取到bounds屬性和scale屬性進(jìn)行計(jì)算。
下面直接上代碼:
//DeviceUtils.h中定義
#pragma mark - About Device Config Info Method
/**
* 獲取當(dāng)前設(shè)備的IMSI值
*/
+ (NSString *)getDeviceIMSIValue;
/**
* 獲取當(dāng)前設(shè)備的IMEI值
*/
+ (NSString *)getDeviceIMEIValue;
/**
* 獲取當(dāng)前設(shè)備的MacId值
*/
+ (NSString *)getDeviceMacIdValue;
/**
* 獲取當(dāng)前設(shè)備的通訊運(yùn)營商名稱
*/
+ (NSString *)getDeviceCarrierName;
/**
* 獲取當(dāng)前設(shè)備的網(wǎng)絡(luò)通訊名稱值
*/
+ (NSString *)getDeviceNetworkName;
/**
* 獲取當(dāng)前設(shè)備的橫向最大值
*/
+ (NSNumber *)getDeviceXMaxValue;
/**
* 獲取當(dāng)前設(shè)備的縱向最大值
*/
+ (NSNumber *)getDeviceYMaxValue;
/**
* 獲取當(dāng)前設(shè)備的橫向分辨率值
*/
+ (NSNumber *)getDeviceXResolution;
/**
* 獲取當(dāng)前設(shè)備的縱向分辨率值
*/
+ (NSNumber *)getDeviceYResolution;
/**
* 獲取當(dāng)前設(shè)備的型號(hào)名稱
*/
+ (NSString *)getDeviceModel;
/**
* 獲取當(dāng)前設(shè)備的操作系統(tǒng)名稱
*/
+ (NSString *)getDeviceOSName;
/**
* 獲取當(dāng)前設(shè)備的操作系統(tǒng)版本號(hào)
*/
+ (NSString *)getDeviceOSVersion;
/**
* 獲取當(dāng)前設(shè)備的唯一編號(hào)
*/
+ (NSString *)getDeviceTerminalId;
//DeviceUtils.m中實(shí)現(xiàn)
#pragma mark - About Device Config Info Method
/*獲取當(dāng)前設(shè)備的IMSI值*/
+ (NSString *)getDeviceIMSIValue
{
return nil;
}
/*獲取當(dāng)前設(shè)備的IMEI值*/
+ (NSString *)getDeviceIMEIValue
{
return nil;
}
/*獲取當(dāng)前設(shè)備的MacId值*/
+ (NSString *)getDeviceMacIdValue
{
return nil;
}
/*獲取當(dāng)前設(shè)備的通訊運(yùn)營商名稱*/
+ (NSString *)getDeviceCarrierName
{
CTTelephonyNetworkInfo *info = [CTTelephonyNetworkInfo new];
CTCarrier *carrier = [info subscriberCellularProvider];
return [carrier carrierName];
}
/*獲取當(dāng)前設(shè)備的網(wǎng)絡(luò)通訊名稱值*/
+ (NSString *)getDeviceNetworkName
{
/*
CTRadioAccessTechnologyGPRS //介于2G和3G之間(2.5G)
CTRadioAccessTechnologyEdge //GPRS到第三代移動(dòng)通信的過渡(2.75G)
CTRadioAccessTechnologyWCDMA
CTRadioAccessTechnologyHSDPA //亦稱為3.5G(3?G)
CTRadioAccessTechnologyHSUPA //3G到4G的過度技術(shù)
CTRadioAccessTechnologyCDMA1x //3G
CTRadioAccessTechnologyCDMAEVDORev0 //3G標(biāo)準(zhǔn)
CTRadioAccessTechnologyCDMAEVDORevA
CTRadioAccessTechnologyCDMAEVDORevB
CTRadioAccessTechnologyeHRPD //電信一種3G到4G的演進(jìn)技術(shù)(3.75G)
CTRadioAccessTechnologyLTE //接近4G
*/
CTTelephonyNetworkInfo *info = [CTTelephonyNetworkInfo new];
return [info currentRadioAccessTechnology];
}
/*獲取當(dāng)前設(shè)備的橫向最大值*/
+ (NSNumber *)getDeviceXMaxValue
{
CGFloat xoffset = CGRectZero.origin.x;
xoffset = CGRectGetWidth([[UIScreen mainScreen] bounds]);
return [NSNumber numberWithFloat:xoffset];
}
/*獲取當(dāng)前設(shè)備的縱向最大值*/
+ (NSNumber *)getDeviceYMaxValue
{
CGFloat yoffset = CGRectZero.origin.x;
yoffset = CGRectGetHeight([[UIScreen mainScreen] bounds]);
return [NSNumber numberWithFloat:yoffset];
}
/*獲取當(dāng)前設(shè)備的橫向分辨率值*/
+ (NSNumber *)getDeviceXResolution
{
CGFloat width = CGRectZero.size.width;
CGFloat scale = [[UIScreen mainScreen] scale];
width = [[[self class] getDeviceXMaxValue] floatValue];
return [NSNumber numberWithFloat:(width * scale)];
}
/*獲取當(dāng)前設(shè)備的縱向分辨率值*/
+ (NSNumber *)getDeviceYResolution
{
CGFloat height = CGRectZero.size.height;
CGFloat scale = [[UIScreen mainScreen] scale];
height = [[[self class] getDeviceYMaxValue] floatValue];
return [NSNumber numberWithFloat:(height * scale)];
}
/*獲取當(dāng)前設(shè)備的型號(hào)名稱*/
+ (NSString *)getDeviceModel
{
NSString *platform = nil;
struct utsname systemInfo;
uname(&systemInfo);
platform = [NSString stringWithCString:systemInfo.machine
encoding:NSUTF8StringEncoding];
return [NSString stringWithFormat:@"%@",platform];
}
/*獲取當(dāng)前設(shè)備的操作系統(tǒng)名稱*/
+ (NSString *)getDeviceOSName
{
return @"ios";
}
/*獲取當(dāng)前設(shè)備的操作系統(tǒng)版本號(hào)*/
+ (NSString *)getDeviceOSVersion
{
return [[UIDevice currentDevice] systemVersion];
}
/*獲取當(dāng)前設(shè)備的唯一編號(hào)*/
+ (NSString *)getDeviceTerminalId
{
UIDevice *device = [UIDevice currentDevice];
NSString *package = [[self class] getAppBundleId];
NSString *vendor = [[device identifierForVendor] UUIDString];
return [NSString stringWithFormat:@"%@%@",package,vendor];
}