iOS 获取设备的一些信息
由于各个前端系统的权限设置,很多设备信息都不能得到有效的获取;所以,公司后台同学经常询问前端同学,你们都能获取到一些什么字段?发现自己也很长一段时间没有记录技术上的知识点了,借此时机,将能获取设备的信息来统一记录。
对于收集设备信息来说分成两大块,第一当然是设备的一些私有和唯一的信息,第二就是App的信息,我们先从简单的App的信息来说。
第一部分:App的信息
关于App的信息,我们大概就需要获取App的版本号、App的build版本号、包名、App显示名称等等。
//DeviceUtils.h中定义
#pragma mark - About App Config Info Method
/**
* 获取当前App的版本号信息
*/
+ (NSString *)getAppVersion;
/**
* 获取当前App的build版本号信息
*/
+ (NSString *)getAppBuildVersion;
/**
* 获取当前App的包名信息
*/
+ (NSString *)getAppBundleId;
/**
* 获取当前App的名称信息
*/
+ (NSString *)getAppDisplayName;
//DeviceUtils.m中实现
#pragma mark - About App Config Info Method
/*获取当前App的版本号信息*/
+ (NSString *)getAppVersion
{
NSBundle *currentBundle = [NSBundle mainBundle];
NSDictionary *infoDictionary = [currentBundle infoDictionary];
return [infoDictionary objectForKey:@"CFBundleShortVersionString"];
}
/*获取当前App的build版本号信息*/
+ (NSString *)getAppBuildVersion
{
NSBundle *currentBundle = [NSBundle mainBundle];
NSDictionary *infoDictionary = [currentBundle infoDictionary];
return [infoDictionary objectForKey:@"CFBundleVersion"];
}
/*获取当前App的包名信息*/
+ (NSString *)getAppBundleId
{
NSBundle *currentBundle = [NSBundle mainBundle];
NSDictionary *infoDictionary = [currentBundle infoDictionary];
return [infoDictionary objectForKey:@"CFBundleIdentifier"];
}
/*获取当前App的名称信息*/
+ (NSString *)getAppDisplayName
{
NSBundle *currentBundle = [NSBundle mainBundle];
NSDictionary *infoDictionary = [currentBundle infoDictionary];
return [infoDictionary objectForKey:@"CFBundleDisplayName"];
}
第二部分:设备的一些私有和唯一的信息
关于获取设备各种标识符,大概统计了一下。基本上有IDFA、IDFV、IMEI、IMSI、UUID、UDID、MAC地址等等。但随着对用户隐私信息的保护,苹果系统对信息的获取进行权限设置,譬如IMEI、IMSI等的获取都明确禁止了,除非你发布的App不通过苹果商店的渠道。不管获取权限,先来介绍下这些字段的含义。
1、IMEI、IMSI、UUID、UDID、MAC地址
-
IMEI:英文名称是:International Mobile Equipment Identity,是国际移动设备身份码的缩写,国际移动装备辨识码,是由15位数字组成的”电子串号”,它与每台手机一一对应,而且该码是全世界唯一的。每一部手机在组装完成后都将被赋予一个全球唯一的一组号码,这个号码从生产到交付使用都将被制造生产的厂商所记录。手机用户可以在手机中查到自己手机的IMEI码。因为隐私问题,苹果用户在iOS5以后不能再获取IMEI的值了。如果开发的App不上架苹果商店,又想获取IMEI值,可参考此链接,获取IMEI值。
-
IMSI:英文名称是:International Mobile Subscriber Identification Number,是国际移动用户识别码的缩写,IMSI分为两部分:一部分叫MCC(Mobile Country Code移动国家码),MCC的资源由国际电联(ITU)统一分配,唯一识别移动用户所属的国家,MCC共3位,中国地区的MCC为460;另一部分叫MNC(Mobile Network Code 移动网络号码),MNC用于识别移动客户所属的移动网络运营商。MNC由二到三个十进制数组成,例如中国移动MNC为00、02、07,中国联通的MNC为01、06、09,中国电信的MNC为03、05、11。
-
UUID:英文名称是:Universally Unique Identifier,翻译过来就是通用唯一标识符。UUID是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。通常平台会提供生成的API,是一个32位的十六进制序列,使用小横线来连接:8-4-4-4-12。由于UUID的本身特性,它保证对在同一时空中的所有机器都是唯一的。所以,需要作为唯一标识码的话,你可以通过保存在keychain或者NSUserDefaults中。
-
UDID:所谓UDID指的是设备的唯一设备识别符,移动广告商和游戏网络运营商往往需要通过UDID用来识别玩家用户,并对用户活动进行跟踪。UDID 在iOS5.0 的时候已经被抛弃使用了。因为隐私问题,苹果宣称如果第三方应用开发者继续分享或者使用iPhone、Mac、AppleWatch的UDID的话,那么他们的应用将会禁止上架。比如我开发了5款App,很多用户都下载了这5款App并使用。如果我能轻易的获取这些用户的UDID,其实我能拼凑出用户的很多信息。由于UDID本身的隐私属性,之前常常用来做第三方统计和其他的目的。苹果就推出了一个UDID的替代品identifierForVendor的属性,关于IDFV的详细内容在下一点中查看。UDID目前主要用于配置真机调试证书。
-
MAC地址:Mac地址是唯一的,unix有系统调用可以获取Mac地址。iPhone可能有多个Mac地址,wifi的地址、以及SIM卡的地址。一般来讲,我们取en0的地址,因为它是iPhone的wifi的地址,是肯定存在的(例外情况依然有:市面上依然存在一部分联通的阉割版无wifi的iPhone)。Mac地址涉及到隐私,不应该胡乱将用户的Mac地址传播,所以我们需要将Mac地址进行hash之后再去使用。同样因为隐私问题,在iOS7以后,苹果禁止获取MAC地址,系统现在只会返回02:00:00:00:00:00虚拟的地址。
2、IDFA、IDFV
- IDFA:广告标示符,iOS6及以后使用。用户可以在设置中控制IDFA是否允许获取以及重置,虽然一般用户都不知道有这个权限设置,所以IDFA是不能拿来当做设备唯一标识(UUID)的替代品。
关闭IDFA获取权限操作:设置 → 隐私 → 广告 → 选择禁止获取IDFA值
重新生成IDFA操作:
操作一:设置程序 → 通用 → 还原 → 还原位置与隐私
操作二:设置程序 → 通用 → 关于本机 → 广告 → 还原广告标示符
//导入获取IDFA的头文件
#import <AdSupport/ASIdentifierManager.h>
- (NSString *)getDeviceIDFAValue
{
ASIdentifierManager *object = nil;
object = [ASIdentifierManager sharedManager];
return [[object advertisingIdentifier] UUIDString];
}
关于IDFA的一张很好的图解:
- IDFV:iOS6.0及以后使用,是给Vendor标识用户用的,主要用于公司内部统计用户。如果隶属一个相同的组织下的App获取到的IDFV是同一个值。例如:
编号 | 包名 | 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不同 |
准确点说,就是通过BundleID的反转的前两部分进行匹配,如果相同就是同一个Vender,共享同一个IDFV的值。
NSString *idfv = [[UIDevice currentDevice].identifierForVendor UUIDString];
3、设备运营商名称、运营商网络
-
设备运营商名称:当前设备上的SIM卡代理商名称。
-
运营商网络:当前设备上的蜂窝网络名称或WiFi。
4、设备型号、设备的系统名称、设备的系统版本号
-
设备型号:主要用来区分设备是iPhone 3gs、iPhone 4、iPhone 4s、iPhone 5、iPhone 5s、iPhone 6、iPhone 6s、iPhone 7、iPhone 8、iPhone X等,还有iPad、iWatch等苹果产品型号。苹果没有提供直接获取型号名称的API方法,所以这个值获取出来后,需要在后台进行映射成对应的设备型号。
-
设备的系统名称:苹果设备目前只有iOS系统。
-
设备的系统版本号:苹果设备的系统版本号,可通过UIDevice类直接获取到一个浮点值。
5、设备的尺寸、设备的分辨率
-
设备的尺寸:苹果设备的物理尺寸,可通过UIScreen类直接获取到bounds属性进行读取。
-
设备的分辨率:苹果设备的分辨率,可通过UIScreen类直接获取到bounds属性和scale属性进行计算。
下面直接上代码:
//DeviceUtils.h中定义
#pragma mark - About Device Config Info Method
/**
* 获取当前设备的IMSI值
*/
+ (NSString *)getDeviceIMSIValue;
/**
* 获取当前设备的IMEI值
*/
+ (NSString *)getDeviceIMEIValue;
/**
* 获取当前设备的MacId值
*/
+ (NSString *)getDeviceMacIdValue;
/**
* 获取当前设备的通讯运营商名称
*/
+ (NSString *)getDeviceCarrierName;
/**
* 获取当前设备的网络通讯名称值
*/
+ (NSString *)getDeviceNetworkName;
/**
* 获取当前设备的横向最大值
*/
+ (NSNumber *)getDeviceXMaxValue;
/**
* 获取当前设备的纵向最大值
*/
+ (NSNumber *)getDeviceYMaxValue;
/**
* 获取当前设备的横向分辨率值
*/
+ (NSNumber *)getDeviceXResolution;
/**
* 获取当前设备的纵向分辨率值
*/
+ (NSNumber *)getDeviceYResolution;
/**
* 获取当前设备的型号名称
*/
+ (NSString *)getDeviceModel;
/**
* 获取当前设备的操作系统名称
*/
+ (NSString *)getDeviceOSName;
/**
* 获取当前设备的操作系统版本号
*/
+ (NSString *)getDeviceOSVersion;
/**
* 获取当前设备的唯一编号
*/
+ (NSString *)getDeviceTerminalId;
//DeviceUtils.m中实现
#pragma mark - About Device Config Info Method
/*获取当前设备的IMSI值*/
+ (NSString *)getDeviceIMSIValue
{
return nil;
}
/*获取当前设备的IMEI值*/
+ (NSString *)getDeviceIMEIValue
{
return nil;
}
/*获取当前设备的MacId值*/
+ (NSString *)getDeviceMacIdValue
{
return nil;
}
/*获取当前设备的通讯运营商名称*/
+ (NSString *)getDeviceCarrierName
{
CTTelephonyNetworkInfo *info = [CTTelephonyNetworkInfo new];
CTCarrier *carrier = [info subscriberCellularProvider];
return [carrier carrierName];
}
/*获取当前设备的网络通讯名称值*/
+ (NSString *)getDeviceNetworkName
{
/*
CTRadioAccessTechnologyGPRS //介于2G和3G之间(2.5G)
CTRadioAccessTechnologyEdge //GPRS到第三代移动通信的过渡(2.75G)
CTRadioAccessTechnologyWCDMA
CTRadioAccessTechnologyHSDPA //亦称为3.5G(3?G)
CTRadioAccessTechnologyHSUPA //3G到4G的过度技术
CTRadioAccessTechnologyCDMA1x //3G
CTRadioAccessTechnologyCDMAEVDORev0 //3G标准
CTRadioAccessTechnologyCDMAEVDORevA
CTRadioAccessTechnologyCDMAEVDORevB
CTRadioAccessTechnologyeHRPD //电信一种3G到4G的演进技术(3.75G)
CTRadioAccessTechnologyLTE //接近4G
*/
CTTelephonyNetworkInfo *info = [CTTelephonyNetworkInfo new];
return [info currentRadioAccessTechnology];
}
/*获取当前设备的横向最大值*/
+ (NSNumber *)getDeviceXMaxValue
{
CGFloat xoffset = CGRectZero.origin.x;
xoffset = CGRectGetWidth([[UIScreen mainScreen] bounds]);
return [NSNumber numberWithFloat:xoffset];
}
/*获取当前设备的纵向最大值*/
+ (NSNumber *)getDeviceYMaxValue
{
CGFloat yoffset = CGRectZero.origin.x;
yoffset = CGRectGetHeight([[UIScreen mainScreen] bounds]);
return [NSNumber numberWithFloat:yoffset];
}
/*获取当前设备的横向分辨率值*/
+ (NSNumber *)getDeviceXResolution
{
CGFloat width = CGRectZero.size.width;
CGFloat scale = [[UIScreen mainScreen] scale];
width = [[[self class] getDeviceXMaxValue] floatValue];
return [NSNumber numberWithFloat:(width * scale)];
}
/*获取当前设备的纵向分辨率值*/
+ (NSNumber *)getDeviceYResolution
{
CGFloat height = CGRectZero.size.height;
CGFloat scale = [[UIScreen mainScreen] scale];
height = [[[self class] getDeviceYMaxValue] floatValue];
return [NSNumber numberWithFloat:(height * scale)];
}
/*获取当前设备的型号名称*/
+ (NSString *)getDeviceModel
{
NSString *platform = nil;
struct utsname systemInfo;
uname(&systemInfo);
platform = [NSString stringWithCString:systemInfo.machine
encoding:NSUTF8StringEncoding];
return [NSString stringWithFormat:@"%@",platform];
}
/*获取当前设备的操作系统名称*/
+ (NSString *)getDeviceOSName
{
return @"ios";
}
/*获取当前设备的操作系统版本号*/
+ (NSString *)getDeviceOSVersion
{
return [[UIDevice currentDevice] systemVersion];
}
/*获取当前设备的唯一编号*/
+ (NSString *)getDeviceTerminalId
{
UIDevice *device = [UIDevice currentDevice];
NSString *package = [[self class] getAppBundleId];
NSString *vendor = [[device identifierForVendor] UUIDString];
return [NSString stringWithFormat:@"%@%@",package,vendor];
}