IOS基础:设备信息
原创:知识点总结性文章
创作不易,请珍惜,之后会持续更新,不断完善
个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈哈,这些文章记录了我的IOS成长历程,希望能与大家一起进步
温馨提示:由于简书不支持目录跳转,大家可通过command + F 输入目录标题后迅速寻找到你所需要的内容
目录
- 一、设备信息
- 1、真机运行效果
- 2、能够获取到的设备信息
- 3、获取设备信息接口的具体实现
- 4、UIDevice中对状态信息的监控
- 二、电量信息
- 1、真机运行效果
- 2、能够获取到的电量信息
- 3、获取电量信息接口的具体实现
- 三、磁盘内存信息
- 1、真机运行效果
- 2、能够获取到的磁盘内存信息
- 3、获取电量信息接口的具体实现
- 四、CPU信息
- 1、真机运行在控制台输出结果
- 2、能够获取到的CPU信息
- 3、获取CPU信息接口的具体实现
- 五、网络信息
- 1、概念解释
- 2、真机运行效果
- 3、能够获取到的网络信息
- 4、获取网络信息接口的具体实现
- 5、判断IPv4和IPv6并获取地址
- 六、判断iOS设备是否安装了特定的app并跳转
- 1、应用间相互跳转应用场景
- 2、应用间相互跳转实现原理
- 3、应用A跳转到应用B
- 4、跳转到特定界面
- 5、从应用B跳转回应用A
- 6、常见Scheme
- 7、跳转到APP Store更新APP
- Demo
- 参考文献
一、设备信息
1、真机运行效果
a、控制台输出结果
2020-09-07 15:25:22.604649+0800 DeviceInfoDemo[4543:775271] infoKey:device_model---infoValue:iPhone11,8
2020-09-07 15:25:22.605227+0800 DeviceInfoDemo[4543:775271] infoKey:localizedModel---infoValue:iPhone
2020-09-07 15:25:22.605661+0800 DeviceInfoDemo[4543:775271] infoKey:设备型号---infoValue:iPhone XR
2020-09-07 15:25:22.608200+0800 DeviceInfoDemo[4543:775271] infoKey:设备名称---infoValue:不才
2020-09-07 15:25:22.608756+0800 DeviceInfoDemo[4543:775271] infoKey:设备颜色(Private API)---infoValue:1
2020-09-07 15:25:22.609098+0800 DeviceInfoDemo[4543:775271] infoKey:设备外壳颜色(Private API)---infoValue:2
2020-09-07 15:25:22.609425+0800 DeviceInfoDemo[4543:775271] infoKey:app版本号---infoValue:1.0
2020-09-07 15:25:22.609687+0800 DeviceInfoDemo[4543:775271] infoKey:当前系统名称---infoValue:iOS
2020-09-07 15:25:22.609970+0800 DeviceInfoDemo[4543:775271] infoKey:当前系统版本号---infoValue:13.5.1
2020-09-07 15:25:22.610195+0800 DeviceInfoDemo[4543:775271] infoKey:设备支持最低系统版本---infoValue:12.0
2020-09-07 15:25:22.610417+0800 DeviceInfoDemo[4543:775271] infoKey:设备支持的最高系统版本---infoValue:(null)
2020-09-07 15:25:22.618473+0800 DeviceInfoDemo[4543:775271] infoKey:能否打电话---infoValue:能
2020-09-07 15:25:22.618668+0800 DeviceInfoDemo[4543:775271] infoKey:设备上次重启的时间---infoValue:Sun Sep 6 15:33:23 2020
2020-09-07 15:25:22.618792+0800 DeviceInfoDemo[4543:775271] infoKey:当前设备的总线频率---infoValue:0
2020-09-07 15:25:22.618895+0800 DeviceInfoDemo[4543:775271] infoKey:当前设备的主存大小---infoValue:0
b、APP显示的信息
Hardware2、能够获取到的设备信息
a、工具类提供的获取设备信息的接口
/** 获取用户的本地化信息:货币类型,国家,语言,数字,日期格式的格式化 */
- (void)localInfo;
/** 获取设备型号 */
- (const NSString *)getDeviceName;
/** 获取设备颜色 */
- (NSString *)getDeviceColor;
/** 获取设备外壳颜色 */
- (NSString *)getDeviceEnclosureColor;
/** 获取设备Model */
- (NSString *)getDeviceModel;
/** 获取设备装机时的系统版本(最低支持版本) */
- (const NSString *)getInitialFirmware;
/** 获取设备可支持的最高系统版本 */
- (const NSString *)getLatestFirmware;
/** 获取设备上次重启的时间 */
- (NSDate *)getSystemUptime;
/** 获取总线程频率 */
- (NSUInteger)getBusFrequency;
/** 获取当前设备主存 */
- (NSUInteger)getRamSize;
b、调用接口获取信息
- (void)setupHardwareInfo
{
// 获取用户的本地化信息:货币类型,国家,语言,数字,日期格式的格式化
[[DeviceInfoManager sharedManager] localInfo];
// 获取DeviceModel
NSString *device_model = [[DeviceInfoManager sharedManager] getDeviceModel];
[self addInfoWithKey:@"device_model" infoValue:device_model];
// 获取localizedModel
NSString *localizedModel = [UIDevice currentDevice].localizedModel;
[self addInfoWithKey:@"localizedModel" infoValue:localizedModel];
// 获取设备型号
const NSString *deviceName = [[DeviceInfoManager sharedManager] getDeviceName];
[self addInfoWithKey:@"设备型号" infoValue:[deviceName copy]];
// 获取设备名称
NSString *iPhoneName = [UIDevice currentDevice].name;
[self addInfoWithKey:@"设备名称" infoValue:iPhoneName];
// 获取设备颜色
NSString *deviceColor = [[DeviceInfoManager sharedManager] getDeviceColor];
[self addInfoWithKey:@"设备颜色(Private API)" infoValue:deviceColor];
// 获取设备外壳颜色
NSString *deviceEnclosureColor = [[DeviceInfoManager sharedManager] getDeviceEnclosureColor];
[self addInfoWithKey:@"设备外壳颜色(Private API)" infoValue:deviceEnclosureColor];
// 获取app名称
NSString *appName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"];
NSLog(@"App应用名称:%@", appName);
// 获取app版本号
NSString *appVerion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
[self addInfoWithKey:@"app版本号" infoValue:appVerion];
// 获取app应用Build版本号
NSString *appBuild = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
NSLog(@"app应用Build版本号:%@", appBuild);
// 获取当前系统名称
NSString *systemName = [UIDevice currentDevice].systemName;
[self addInfoWithKey:@"当前系统名称" infoValue:systemName];
// 当前系统版本号
NSString *systemVersion = [UIDevice currentDevice].systemVersion;
[self addInfoWithKey:@"当前系统版本号" infoValue:systemVersion];
// 设备支持最低系统版本
const NSString *initialFirmware = [[DeviceInfoManager sharedManager] getInitialFirmware];
[self addInfoWithKey:@"设备支持最低系统版本" infoValue:[initialFirmware copy]];
// 设备支持的最高系统版本
const NSString *latestFirmware = [[DeviceInfoManager sharedManager] getLatestFirmware];
[self addInfoWithKey:@"设备支持的最高系统版本" infoValue:[latestFirmware copy]];
// 能否打电话
BOOL canMakePhoneCall = [DeviceInfoManager sharedManager].canMakePhoneCall;
[self addInfoWithKey:@"能否打电话" infoValue:@(canMakePhoneCall ? "能" : "不能")];
// 获取设备上次重启的时间
NSDate *systemUptime = [[DeviceInfoManager sharedManager] getSystemUptime];
[self addInfoWithKey:@"设备上次重启的时间" infoValue:systemUptime];
// 当前设备的总线频率
NSUInteger busFrequency = [[DeviceInfoManager sharedManager] getBusFrequency];
[self addInfoWithKey:@"当前设备的总线频率" infoValue:@(busFrequency)];
// 当前设备的主存大小(随机存取存储器(Random Access Memory))
NSUInteger ramSize = [[DeviceInfoManager sharedManager] getRamSize];
[self addInfoWithKey:@"当前设备的主存大小" infoValue:@(ramSize)];
}
3、获取设备信息接口的具体实现
a、需要导入的头文件
// 下面是获取mac地址需要导入的头文件
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>
#import <sys/sockio.h>
#import <sys/ioctl.h>
#import <arpa/inet.h>
#import "sys/utsname.h" //获取设备Model
#import <AdSupport/AdSupport.h> //获取广告标识符
#include <ifaddrs.h> //获取ip需要的头文件
#include <mach/mach.h> //获取CPU信息所需要引入的头文件
b、方法的实现
// 获取用户的本地化信息:货币类型,国家,语言,数字,日期格式的格式化
- (void)localInfo
{
NSArray *languageArray = [NSLocale preferredLanguages];
NSString *language = [languageArray objectAtIndex:0];
NSLog(@"语言:%@", language);//en
NSLocale *local = [NSLocale currentLocale];
NSString *country = [local localeIdentifier];
NSLog(@"国家:%@", country); //en_US
}
// 获取设备型号
- (const NSString *)getDeviceName
{
return [[DeviceDataLibrery sharedLibrery] getDiviceName];
}
// 获取设备颜色
- (NSString *)getDeviceColor
{
return [self getDeviceColorWithKey:@"DeviceColor"];
}
// 获取设备外壳颜色,私有API,上线会被拒
- (NSString *)getDeviceEnclosureColor
{
return [self getDeviceColorWithKey:@"DeviceEnclosureColor"];
}
// 获取设备Model
- (NSString *)getDeviceModel
{
struct utsname systemInfo;
uname(&systemInfo);
NSString *deviceModel = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
return deviceModel;
}
// 获取设备装机时的系统版本(最低支持版本)
- (const NSString *)getInitialFirmware
{
return [[DeviceDataLibrery sharedLibrery] getInitialVersion];
}
// 获取设备可支持的最高系统版本
- (const NSString *)getLatestFirmware
{
return [[DeviceDataLibrery sharedLibrery] getLatestVersion];
}
// 能否打电话
#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
- (BOOL)canMakePhoneCall
{
__block BOOL can;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
can = [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel://"]];
});
return can;
}
#endif
// 获取设备上次重启的时间
- (NSDate *)getSystemUptime
{
NSTimeInterval time = [[NSProcessInfo processInfo] systemUptime];
return [[NSDate alloc] initWithTimeIntervalSinceNow:(0 - time)];
}
// 获取总线程频率
- (NSUInteger)getBusFrequency
{
return [self getSystemInfo:HW_BUS_FREQ];
}
// 获取当前设备主存
- (NSUInteger)getRamSize
{
return [self getSystemInfo:HW_MEMSIZE];
}
#pragma mark - Private Method
// 获取设备颜色,私有API,上线会被拒
- (NSString *)getDeviceColorWithKey:(NSString *)key
{
UIDevice *device = [UIDevice currentDevice];
SEL selector = NSSelectorFromString(@"deviceInfoForKey:");
if (![device respondsToSelector:selector])
{
selector = NSSelectorFromString(@"_deviceInfoForKey:");
}
if ([device respondsToSelector:selector])
{
// 消除警告“performSelector may cause a leak because its selector is unknown”
IMP imp = [device methodForSelector:selector];
NSString * (*func)(id, SEL, NSString *) = (void *)imp;
return func(device, selector, key);
}
return @"unKnown";
}
- (NSUInteger)getSystemInfo:(uint)typeSpecifier
{
size_t size = sizeof(int);
int result;
int mib[2] = {CTL_HW, typeSpecifier};
sysctl(mib, 2, &result, &size, NULL, 0);
return (NSUInteger)result;
}
4、UIDevice中对状态信息的监控
a、真机运行在控制台的输出结果
2020-10-28 18:17:33.877164+0800 DeviceInfoDemo[621:101454] 近距离
2020-10-28 18:17:36.834020+0800 DeviceInfoDemo[621:101454] 远距离
2020-10-28 18:17:36.954133+0800 DeviceInfoDemo[621:101454] 设备方向改变
b、添加状态通知:即将某种状态的监控信息添加到通知中心
- (void)registerNotification
{
// 添加设备方向的监控通知,状态发生变化是就会自动调用对应的方法执行
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeOrientation) name:UIDeviceOrientationDidChangeNotification object:nil];
// 添加距离状态的监控通知
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(changeProximityState) name:UIDeviceProximityStateDidChangeNotification object:nil];
}
c、开启监控开关: 状态通知都对应有一个开关来控制是否开启对应的监控和通知
- (void)openNotification
{
//打开设备方向监测,这是用方法控制
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
//不需要时可以关闭设备方向监控
[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
//打开电池状态和电池电量监测开关,不需要时可以关闭
[UIDevice currentDevice].batteryMonitoringEnabled=YES;
//打开手机距离传感器监测开关,不需要时可以关闭
[UIDevice currentDevice].proximityMonitoringEnabled=YES;
}
d、状态发生变化是就会自动调用对应的方法执行
// 设备方向改变时调用该方法
-(void)changeOrientation
{
NSLog(@"设备方向改变");
}
// 设备离用户的距离状态发生变化时调用该方法
- (void)changeProximityState
{
if ([UIDevice currentDevice].proximityState)
{
NSLog(@"近距离");
}
else
{
NSLog(@"远距离");
}
}
二、电量信息
1、真机运行效果
a、控制台输出结果
2020-09-07 16:07:41.215870+0800 DeviceInfoDemo[4630:791750] 电池状态改变,reload对应的cell,进行更新UI操作
2020-09-07 16:07:41.216055+0800 DeviceInfoDemo[4630:791750] 电池状态改变,reload对应的cell,进行更新UI操作
2020-09-07 16:07:41.216303+0800 DeviceInfoDemo[4630:791750] infoKey:电池电量---infoValue:0.85
2020-09-07 16:07:41.216456+0800 DeviceInfoDemo[4630:791750] infoKey:电池容量---infoValue:2942 mA
2020-09-07 16:07:41.216560+0800 DeviceInfoDemo[4630:791750] infoKey:当前电池剩余电量---infoValue:2500.70 mA
2020-09-07 16:07:41.216729+0800 DeviceInfoDemo[4630:791750] infoKey:电池电压---infoValue:0.00 V
2020-09-07 16:07:41.216871+0800 DeviceInfoDemo[4630:791750] infoKey:电池状态---infoValue:Charging
b、APP显示的信息
电量2、能够获取到的电量信息
a、工具类提供的获取电量信息的接口
@protocol BatteryInfoDelegate
- (void)batteryStatusUpdated;
@end
@property (nonatomic, weak) id<BatteryInfoDelegate> delegate;
#pragma mark - 电量术语
// 电池容量,单位 mA 毫安
@property (nonatomic, assign) NSUInteger capacity;
// 电池电压,单位 V 福特
@property (nonatomic, assign) CGFloat voltage;
// 电池电量
@property (nonatomic, assign) NSUInteger levelPercent;
// 当前电池剩余电量
@property (nonatomic, assign) NSUInteger levelMAH;
@property (nonatomic, copy) NSString *status;
#pragma mark - 监测电池电量
/** 开始监测电池电量 */
- (void)startBatteryMonitoring;
/** 停止监测电池电量 */
- (void)stopBatteryMonitoring;
b、调用接口获取信息
- (void)setupBatteryInfo
{
BatteryInfoManager *batteryManager = [BatteryInfoManager sharedManager];
batteryManager.delegate = self;
// 开始监测电池电量
[batteryManager startBatteryMonitoring];
// 获得电池电量
CGFloat batteryLevel = [[UIDevice currentDevice] batteryLevel];
NSString *levelValue = [NSString stringWithFormat:@"%.2f", batteryLevel];
[self addInfoWithKey:@"电池电量" infoValue:levelValue];
// 获得电池容量
NSInteger batteryCapacity = batteryManager.capacity;
NSString *capacityValue = [NSString stringWithFormat:@"%ld mA", batteryCapacity];
[self addInfoWithKey:@"电池容量" infoValue:capacityValue];
// 获得当前电池剩余电量
CGFloat batteryMAH = batteryCapacity * batteryLevel;
NSString *mahValue = [NSString stringWithFormat:@"%.2f mA", batteryMAH];
[self addInfoWithKey:@"当前电池剩余电量" infoValue:mahValue];
// 获得电池电压
CGFloat batteryVoltage = batteryManager.voltage;
NSString *voltageValue = [NSString stringWithFormat:@"%.2f V", batteryVoltage];
[self addInfoWithKey:@"电池电压" infoValue:voltageValue];
// 获得电池状态
NSString *batterStatus = batteryManager.status ? : @"unkonwn";
[self addInfoWithKey:@"电池状态" infoValue:batterStatus];
}
// 当电池状态改变时,会调用该方法,应该在此处reload对应的cell,进行更新UI操作
- (void)batteryStatusUpdated
{
NSLog(@"电池状态改变,reload对应的cell,进行更新UI操作");
}
3、获取电量信息接口的具体实现
a、监测电池电量
- (void)startBatteryMonitoring
{
if (!self.batteryMonitoringEnabled)
{
self.batteryMonitoringEnabled = YES;
UIDevice *device = [UIDevice currentDevice];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(batteryLevelUpdated:) name:UIDeviceBatteryLevelDidChangeNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(batteryStatusUpdated:) name:UIDeviceBatteryStateDidChangeNotification object:nil];
[device setBatteryMonitoringEnabled:YES];
// If by any chance battery value is available - update it immediately
if ([device batteryState] != UIDeviceBatteryStateUnknown)
{
[self doUpdateBatteryStatus];
}
}
}
b、电池状态更改
- (void)batteryLevelUpdated:(NSNotification*)notification
{
[self doUpdateBatteryStatus];
}
- (void)batteryStatusUpdated:(NSNotification*)notification
{
[self doUpdateBatteryStatus];
}
- (void)doUpdateBatteryStatus
{
float batteryMultiplier = [[UIDevice currentDevice] batteryLevel];
self.levelPercent = batteryMultiplier * 100;
self.levelMAH = self.capacity * batteryMultiplier;
switch ([[UIDevice currentDevice] batteryState])
{
case UIDeviceBatteryStateCharging:// 充电
if (self.levelPercent == 100)
{
self.status = @"Fully charged";
}
else
{
self.status = @"Charging";
}
break;
case UIDeviceBatteryStateFull:// 满电
self.status = @"Fully charged";
break;
case UIDeviceBatteryStateUnplugged:// 未插入
self.status = @"Unplugged";
break;
case UIDeviceBatteryStateUnknown:// 未知
self.status = @"Unknown";
break;
}
// 状态更改
[self.delegate batteryStatusUpdated];
}
三、磁盘内存信息
1、真机运行效果
a、控制台输出结果
2020-09-07 18:13:35.435340+0800 DeviceInfoDemo[5009:838778] infoKey:当前 App 所占内存空间---infoValue:320 bytes
2020-09-07 18:13:35.435774+0800 DeviceInfoDemo[5009:838778] infoKey:磁盘总空间---infoValue:== 122060.27 MB == 119.20 GB
2020-09-07 18:13:35.436224+0800 DeviceInfoDemo[5009:838778] infoKey:磁盘已使用空间---infoValue: == 88011.07 MB == 85.95 GB
2020-09-07 18:13:35.436491+0800 DeviceInfoDemo[5009:838778] infoKey:磁盘空闲空间---infoValue: 34049.20 MB == 33.25 GB
2020-09-07 18:13:35.436622+0800 DeviceInfoDemo[5009:838778] infoKey:系统总内存空间---infoValue: 2831.33 MB == 2.76 GB
2020-09-07 18:13:35.436802+0800 DeviceInfoDemo[5009:838778] infoKey:空闲的内存空间---infoValue: 101.25 MB == 0.10 GB
2020-09-07 18:19:32.184034+0800 DeviceInfoDemo[5032:841261] infoKey:已使用的内存空间---infoValue: 2083.86 MB == 2.03 GB
2020-09-07 18:13:35.437295+0800 DeviceInfoDemo[5009:838778] infoKey:活跃的内存---infoValue:正在使用或者很短时间内被使用过 814.98 MB == 0.79 GB
2020-09-07 18:13:35.437410+0800 DeviceInfoDemo[5009:838778] infoKey:最近使用过---infoValue:但是目前处于不活跃状态的内存 725.95 MB == 0.71 GB
2020-09-07 18:13:35.437514+0800 DeviceInfoDemo[5009:838778] infoKey:用来存放内核和数据结构的内存---infoValue:framework、用户级别的应用无法分配 612.47 MB == 0.60 GB
2020-09-07 18:13:35.437619+0800 DeviceInfoDemo[5009:838778] infoKey:可释放的内存空间:内存吃紧自动释放---infoValue:大对象存放所需的大块内存空间 9.88 MB == 0.01 GB
b、APP显示的信息
image.png2、能够获取到的磁盘内存信息
a、工具类提供的获取磁盘内存信息的接口
#pragma mark - 磁盘
/** 获取本 App 所占磁盘空间 */
- (NSString *)getApplicationSize;
/** 获取磁盘总空间 */
- (int64_t)getTotalDiskSpace;
/** 获取未使用的磁盘空间 */
- (int64_t)getFreeDiskSpace;
/** 获取已使用的磁盘空间 */
- (int64_t)getUsedDiskSpace;
#pragma mark - 内存
/** 获取总内存空间 */
- (int64_t)getTotalMemory;
/** 获取活跃的内存空间 */
- (int64_t)getActiveMemory;
/** 获取不活跃的内存空间 */
- (int64_t)getInActiveMemory;
/** 获取空闲的内存空间 */
- (int64_t)getFreeMemory;
/** 获取正在使用的内存空间 */
- (int64_t)getUsedMemory;
/** 获取存放内核的内存空间 */
- (int64_t)getWiredMemory;
/** 获取可释放的内存空间 */
- (int64_t)getPurgableMemory;
b、调用接口获取信息
- (void)setupDiskInfo
{
// 获得当前App所占内存空间
NSString *applicationSize = [[DeviceInfoManager sharedManager] getApplicationSize];
[self addInfoWithKey:@"当前 App 所占内存空间" infoValue:applicationSize];
// 获得磁盘总空间
int64_t totalDisk = [[DeviceInfoManager sharedManager] getTotalDiskSpace];
NSString *totalDiskInfo = [NSString stringWithFormat:@"== %.2f MB == %.2f GB", totalDisk/1024/1024.0, totalDisk/1024/1024/1024.0];
[self addInfoWithKey:@"磁盘总空间" infoValue:totalDiskInfo];
// 获得磁盘已使用空间
int64_t usedDisk = [[DeviceInfoManager sharedManager] getUsedDiskSpace];
NSString *usedDiskInfo = [NSString stringWithFormat:@" == %.2f MB == %.2f GB", usedDisk/1024/1024.0, usedDisk/1024/1024/1024.0];
[self addInfoWithKey:@"磁盘已使用空间" infoValue:usedDiskInfo];
// 获得磁盘空闲空间
int64_t freeDisk = [[DeviceInfoManager sharedManager] getFreeDiskSpace];
NSString *freeDiskInfo = [NSString stringWithFormat:@" %.2f MB == %.2f GB", freeDisk/1024/1024.0, freeDisk/1024/1024/1024.0];
[self addInfoWithKey:@"磁盘空闲空间" infoValue:freeDiskInfo];
// 系统总内存空间
int64_t totalMemory = [[DeviceInfoManager sharedManager] getTotalMemory];
NSString *totalMemoryInfo = [NSString stringWithFormat:@" %.2f MB == %.2f GB", totalMemory/1024/1024.0, totalMemory/1024/1024/1024.0];
[self addInfoWithKey:@"系统总内存空间" infoValue:totalMemoryInfo];
// 获得空闲的内存空间
int64_t freeMemory = [[DeviceInfoManager sharedManager] getFreeMemory];
NSString *freeMemoryInfo = [NSString stringWithFormat:@" %.2f MB == %.2f GB", freeMemory/1024/1024.0, freeMemory/1024/1024/1024.0];
[self addInfoWithKey:@"空闲的内存空间" infoValue:freeMemoryInfo];
// 获得已使用的内存空间
int64_t usedMemory = [[DeviceInfoManager sharedManager] getUsedMemory];
NSString *usedMemoryInfo = [NSString stringWithFormat:@" %.2f MB == %.2f GB", usedMemory/1024/1024.0, usedMemory/1024/1024/1024.0];
[self addInfoWithKey:@"已使用的内存空间" infoValue:usedMemoryInfo];
// 获得正在使用或者很短时间内被使用过的内存
int64_t activeMemory = [[DeviceInfoManager sharedManager] getActiveMemory];
NSString *activeMemoryInfo = [NSString stringWithFormat:@"正在使用或者很短时间内被使用过 %.2f MB == %.2f GB", activeMemory/1024/1024.0, activeMemory/1024/1024/1024.0];
[self addInfoWithKey:@"活跃的内存" infoValue:activeMemoryInfo];
// 获得最近使用过但是目前处于不活跃状态的内存
int64_t inActiveMemory = [[DeviceInfoManager sharedManager] getInActiveMemory];
NSString *inActiveMemoryInfo = [NSString stringWithFormat:@"但是目前处于不活跃状态的内存 %.2f MB == %.2f GB", inActiveMemory/1024/1024.0, inActiveMemory/1024/1024/1024.0];
[self addInfoWithKey:@"最近使用过" infoValue:inActiveMemoryInfo];
// 获得用来存放内核和数据结构的内存
int64_t wiredMemory = [[DeviceInfoManager sharedManager] getWiredMemory];
NSString *wiredMemoryInfo = [NSString stringWithFormat:@"framework、用户级别的应用无法分配 %.2f MB == %.2f GB", wiredMemory/1024/1024.0, wiredMemory/1024/1024/1024.0];
[self addInfoWithKey:@"用来存放内核和数据结构的内存" infoValue:wiredMemoryInfo];
// 获得大对象存放所需的大块内存空间,内存吃紧自动释放
int64_t purgableMemory = [[DeviceInfoManager sharedManager] getPurgableMemory];
NSString *purgableMemoryInfo = [NSString stringWithFormat:@"大对象存放所需的大块内存空间 %.2f MB == %.2f GB", purgableMemory/1024/1024.0, purgableMemory/1024/1024/1024.0];
[self addInfoWithKey:@"可释放的内存空间:内存吃紧自动释放" infoValue:purgableMemoryInfo];
}
3、获取电量信息接口的具体实现
a、获取磁盘信息
// 获取本 App 所占磁盘空间
- (NSString *)getApplicationSize
{
unsigned long long documentSize = [self getSizeOfFolder:[self getDocumentPath]];
unsigned long long librarySize = [self getSizeOfFolder:[self getLibraryPath]];
unsigned long long cacheSize = [self getSizeOfFolder:[self getCachePath]];
unsigned long long total = documentSize + librarySize + cacheSize;
NSString *applicationSize = [NSByteCountFormatter stringFromByteCount:total countStyle:NSByteCountFormatterCountStyleFile];
return applicationSize;
}
// 获取磁盘总空间
- (int64_t)getTotalDiskSpace
{
NSError *error = nil;
NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:&error];
if (error) return -1;
int64_t space = [[attrs objectForKey:NSFileSystemSize] longLongValue];
if (space < 0) space = -1;
return space;
}
// 获取未使用的磁盘空间
- (int64_t)getFreeDiskSpace
{
NSError *error = nil;
NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:&error];
if (error) return -1;
int64_t space = [[attrs objectForKey:NSFileSystemFreeSize] longLongValue];
if (space < 0) space = -1;
return space;
}
// 获取已使用的磁盘空间
- (int64_t)getUsedDiskSpace
{
int64_t totalDisk = [self getTotalDiskSpace];
int64_t freeDisk = [self getFreeDiskSpace];
if (totalDisk < 0 || freeDisk < 0) return -1;
int64_t usedDisk = totalDisk - freeDisk;
if (usedDisk < 0) usedDisk = -1;
return usedDisk;
}
b、获取内存信息
// 获取总内存空间
- (int64_t)getTotalMemory
{
int64_t totalMemory = [[NSProcessInfo processInfo] physicalMemory];
if (totalMemory < -1) totalMemory = -1;
return totalMemory;
}
// 获取活跃的内存空间
- (int64_t)getActiveMemory
{
mach_port_t host_port = mach_host_self();
mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
vm_size_t page_size;
vm_statistics_data_t vm_stat;
kern_return_t kern;
kern = host_page_size(host_port, &page_size);
if (kern != KERN_SUCCESS) return -1;
kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
if (kern != KERN_SUCCESS) return -1;
return vm_stat.active_count * page_size;
}
// 获取不活跃的内存空间
- (int64_t)getInActiveMemory
{
mach_port_t host_port = mach_host_self();
mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
vm_size_t page_size;
vm_statistics_data_t vm_stat;
kern_return_t kern;
kern = host_page_size(host_port, &page_size);
if (kern != KERN_SUCCESS) return -1;
kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
if (kern != KERN_SUCCESS) return -1;
return vm_stat.inactive_count * page_size;
}
// 获取空闲的内存空间
- (int64_t)getFreeMemory
{
mach_port_t host_port = mach_host_self();
mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
vm_size_t page_size;
vm_statistics_data_t vm_stat;
kern_return_t kern;
kern = host_page_size(host_port, &page_size);
if (kern != KERN_SUCCESS) return -1;
kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
if (kern != KERN_SUCCESS) return -1;
return vm_stat.free_count * page_size;
}
// 获取正在使用的内存空间
- (int64_t)getUsedMemory
{
mach_port_t host_port = mach_host_self();
mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
vm_size_t page_size;
vm_statistics_data_t vm_stat;
kern_return_t kern;
kern = host_page_size(host_port, &page_size);
if (kern != KERN_SUCCESS) return -1;
kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
if (kern != KERN_SUCCESS) return -1;
return page_size * (vm_stat.active_count + vm_stat.inactive_count + vm_stat.wire_count);
}
// 获取存放内核的内存空间
- (int64_t)getWiredMemory
{
mach_port_t host_port = mach_host_self();
mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
vm_size_t page_size;
vm_statistics_data_t vm_stat;
kern_return_t kern;
kern = host_page_size(host_port, &page_size);
if (kern != KERN_SUCCESS) return -1;
kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
if (kern != KERN_SUCCESS) return -1;
return vm_stat.wire_count * page_size;
}
// 获取可释放的内存空间
- (int64_t)getPurgableMemory
{
mach_port_t host_port = mach_host_self();
mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
vm_size_t page_size;
vm_statistics_data_t vm_stat;
kern_return_t kern;
kern = host_page_size(host_port, &page_size);
if (kern != KERN_SUCCESS) return -1;
kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
if (kern != KERN_SUCCESS) return -1;
return vm_stat.purgeable_count * page_size;
}
// DocumentPath
- (NSString *)getDocumentPath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *basePath = [paths firstObject];
return basePath;
}
// LibraryPath
- (NSString *)getLibraryPath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *basePath = [paths firstObject];
return basePath;
}
// CachePath
- (NSString *)getCachePath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *basePath = [paths firstObject];
return basePath;
}
// getSizeOfFolder
- (unsigned long long)getSizeOfFolder:(NSString *)folderPath
{
NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:folderPath error:nil];
NSEnumerator *contentsEnumurator = [contents objectEnumerator];
NSString *file;
unsigned long long folderSize = 0;
while (file = [contentsEnumurator nextObject])
{
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[folderPath stringByAppendingPathComponent:file] error:nil];
folderSize += [[fileAttributes objectForKey:NSFileSize] intValue];
}
return folderSize;
}
四、CPU信息
1、真机运行在控制台输出结果
2020-09-07 17:38:08.723640+0800 DeviceInfoDemo[4720:822749] infoKey:CPU 处理器名称---infoValue:A12 Bionic
2020-09-07 17:38:08.723983+0800 DeviceInfoDemo[4720:822749] infoKey:CPU总数目---infoValue:6
2020-09-07 17:38:08.724229+0800 DeviceInfoDemo[4720:822749] infoKey:CPU使用的总比例---infoValue:1.025270104408264
2020-09-07 17:38:08.724328+0800 DeviceInfoDemo[4720:822749] infoKey:CPU 频率---infoValue:1
2020-09-07 17:38:08.724492+0800 DeviceInfoDemo[4720:822749] infoKey:单个CPU使用比例---infoValue:0.05<-->0.27<-->0.21<-->0.05<-->0.19<-->0.25<-->
2、能够获取到的CPU信息
a、工具类提供的获取磁盘内存信息的接口
/** 获取CPU处理器名称 */
- (NSString *)getCPUProcessor;
/** 获取CPU数量 */
- (NSUInteger)getCPUCount;
/** 获取CPU总的使用百分比 */
- (float)getCPUUsage;
/** 获取单个CPU使用百分比 */
- (NSArray *)getPerCPUUsage;
/** 获取CPU频率 */
- (NSUInteger)getCPUFrequency;
b、调用接口获取信息
- (void)setupCPUInfo
{
// 获取CPU处理器名称
NSString *cpuName = [[DeviceInfoManager sharedManager] getCPUProcessor];
[self addInfoWithKey:@"CPU 处理器名称" infoValue:cpuName];
// 获取CPU总数目
NSUInteger cpuCount = [[DeviceInfoManager sharedManager] getCPUCount];
[self addInfoWithKey:@"CPU总数目" infoValue:@(cpuCount)];
// 获取CPU使用的总比例
CGFloat cpuUsage = [[DeviceInfoManager sharedManager] getCPUUsage];
[self addInfoWithKey:@"CPU使用的总比例" infoValue:@(cpuUsage)];
// 获取CPU 频率
NSUInteger cpuFrequency = [[DeviceInfoManager sharedManager] getCPUFrequency];
[self addInfoWithKey:@"CPU 频率" infoValue:@(cpuFrequency)];
// 获取单个CPU使用比例
NSArray *perCPUArr = [[DeviceInfoManager sharedManager] getPerCPUUsage];
NSMutableString *perCPUUsage = [NSMutableString string];
for (NSNumber *per in perCPUArr)
{
[perCPUUsage appendFormat:@"%.2f<-->", per.floatValue];
}
[self addInfoWithKey:@"单个CPU使用比例" infoValue:perCPUUsage];
}
3、获取CPU信息接口的具体实现
a、获取CPU处理器名称
- (NSString *)getCPUProcessor
{
return [[DeviceDataLibrery sharedLibrery] getCPUProcessor] ? : @"unKnown";
}
b、获取CPU数量
- (NSUInteger)getCPUCount
{
return [NSProcessInfo processInfo].activeProcessorCount;
}
c、获取CPU频率
- (NSUInteger)getCPUFrequency
{
return [self getSystemInfo:HW_CPU_FREQ];
}
d、获取单个CPU使用百分比
- (NSArray *)getPerCPUUsage
{
processor_info_array_t _cpuInfo, _prevCPUInfo = nil;
mach_msg_type_number_t _numCPUInfo, _numPrevCPUInfo = 0;
unsigned _numCPUs;
NSLock *_cpuUsageLock;
int _mib[2U] = { CTL_HW, HW_NCPU };
size_t _sizeOfNumCPUs = sizeof(_numCPUs);
int _status = sysctl(_mib, 2U, &_numCPUs, &_sizeOfNumCPUs, NULL, 0U);
if (_status)
_numCPUs = 1;
_cpuUsageLock = [[NSLock alloc] init];
natural_t _numCPUsU = 0U;
kern_return_t err = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &_numCPUsU, &_cpuInfo, &_numCPUInfo);
if (err == KERN_SUCCESS)
{
[_cpuUsageLock lock];
NSMutableArray *cpus = [NSMutableArray new];
for (unsigned i = 0U; i < _numCPUs; ++i)
{
Float32 _inUse, _total;
if (_prevCPUInfo)
{
_inUse = (
(_cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_USER] - _prevCPUInfo[(CPU_STATE_MAX * i) + CPU_STATE_USER])
+ (_cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_SYSTEM] - _prevCPUInfo[(CPU_STATE_MAX * i) + CPU_STATE_SYSTEM])
+ (_cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_NICE] - _prevCPUInfo[(CPU_STATE_MAX * i) + CPU_STATE_NICE])
);
_total = _inUse + (_cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_IDLE] - _prevCPUInfo[(CPU_STATE_MAX * i) + CPU_STATE_IDLE]);
}
else
{
_inUse = _cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_USER] + _cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_SYSTEM] + _cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_NICE];
_total = _inUse + _cpuInfo[(CPU_STATE_MAX * i) + CPU_STATE_IDLE];
}
[cpus addObject:@(_inUse / _total)];
}
[_cpuUsageLock unlock];
if (_prevCPUInfo)
{
size_t prevCpuInfoSize = sizeof(integer_t) * _numPrevCPUInfo;
vm_deallocate(mach_task_self(), (vm_address_t)_prevCPUInfo, prevCpuInfoSize);
}
return cpus;
}
else
{
return nil;
}
}
e、获取CPU总的使用百分比
- (float)getCPUUsage
{
float cpu = 0;
NSArray *cpus = [self getPerCPUUsage];
if (cpus.count == 0) return -1;
for (NSNumber *n in cpus)
{
cpu += n.floatValue;
}
return cpu;
}
五、网络信息
1、概念解释
广告位标识符:在同一个设备上的所有App都会取到相同的值,是苹果专门给各广告提供商用来追踪用户而设的,用户可以在 设置|隐私|广告追踪里重置此id的值,或限制此id
的使用,故此id
有可能会取不到值,但好在Apple默认是允许追踪的,而且一般用户都不知道有这么个设置,所以基本上用来监测推广效果,是戳戳有余了。
UUID:是Universally Unique Identifier
的缩写,中文意思是通用唯一识别码。它是让分布式系统中的所有元素,都能有唯一的辨识资讯,而不需要透过中央控制端来做辨识资讯的指 定。这样,每个人都可以建立不与其它人冲突的 UUID
。在此情况下,就不需考虑数据库建立时的名称重复问题。苹果公司建议使用UUID
为应用生成唯一标识字符串。
2、真机运行效果
a、控制台输出结果
2020-09-07 17:03:55.340667+0800 DeviceInfoDemo[4683:810709] infoKey:广告位标识符idfa:---infoValue:5BE8FE64-29FC-40C6-9EB1-36B2B5F65ECB
2020-09-07 17:03:55.341933+0800 DeviceInfoDemo[4683:810709] infoKey:唯一识别码uuid---infoValue:A4997507-9A41-4CFC-A9ED-25FB96B6AE51
2020-09-07 17:03:55.342129+0800 DeviceInfoDemo[4683:810709] infoKey:device_token_crc32真机测试才会显示---infoValue:
2020-09-07 17:03:55.342558+0800 DeviceInfoDemo[4683:810709] infoKey:macAddress---infoValue:020000000000
2020-09-07 17:03:55.342975+0800 DeviceInfoDemo[4683:810709] infoKey:deviceIP---infoValue:169.254.215.144
2020-09-07 17:03:55.343368+0800 DeviceInfoDemo[4683:810709] infoKey:蜂窝地址---infoValue:172.25.39.187
2020-09-07 17:03:55.343775+0800 DeviceInfoDemo[4683:810709] infoKey:WIFI IP地址---infoValue:fe80::14f5:22a1:b6df:3ead
b、APP显示的信息
获取IP地址3、能够获取到的网络信息
a、工具类提供的获取网络信息的接口
/** 获取广告标识符 */
- (NSString *)getIDFA;
/** 获取mac地址 */
- (NSString *)getMacAddress;
/** 获取ip地址 */
- (NSString *)getDeviceIPAddresses;
/** 获取WIFI IP地址 */
- (NSString *)getIpAddressWIFI;
/** 获取蜂窝地址 */
- (NSString *)getIpAddressCell;
b、调用接口获取信息
- (void)setupAddressInfo
{
// 获取广告位标识符
NSString *idfa = [[DeviceInfoManager sharedManager] getIDFA];
[self addInfoWithKey:@"广告位标识符idfa:" infoValue:idfa];
// 获取UUID
NSString *uuid = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
[self addInfoWithKey:@"唯一识别码uuid" infoValue:uuid];
// 这个 !还存在问题
NSString *device_token_crc32 = [[NSUserDefaults standardUserDefaults] objectForKey:@"device_token_crc32"] ? : @"";
[self addInfoWithKey:@"device_token_crc32真机测试才会显示" infoValue:device_token_crc32];
// 获取mac地址
NSString *macAddress = [[DeviceInfoManager sharedManager] getMacAddress];
[self addInfoWithKey:@"macAddress" infoValue:macAddress];
// 获取设备的IP地址
NSString *deviceIP = [[NetWorkInfoManager sharedManager] getDeviceIPAddresses];
[self addInfoWithKey:@"deviceIP" infoValue:deviceIP];
// 获取蜂窝地址
NSString *cellIP = [[NetWorkInfoManager sharedManager] getIpAddressCell];
[self addInfoWithKey:@"蜂窝地址" infoValue:cellIP];
// 获取WIFI IP地址
NSString *wifiIP = [[NetWorkInfoManager sharedManager] getIpAddressWIFI];
[self addInfoWithKey:@"WIFI IP地址" infoValue:wifiIP];
}
4、获取网络信息接口的具体实现
a、需要引入的头文件
#import "NSData+CRC32.h"
// 获取ip需要的头文件
#include <ifaddrs.h>
#include <sys/socket.h> // Per msqr
#import <sys/ioctl.h>
#include <net/if.h>
#import <arpa/inet.h>
b、获取广告标识符
- (NSString *)getIDFA
{
return [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
}
c、获取mac地址
- (NSString *)getMacAddress
{
int mib[6];
size_t len;
char *buf;
unsigned char *ptr;
struct if_msghdr *ifm;
struct sockaddr_dl *sdl;
mib[0] = CTL_NET;
mib[1] = AF_ROUTE;
mib[2] = 0;
mib[3] = AF_LINK;
mib[4] = NET_RT_IFLIST;
if ((mib[5] = if_nametoindex("en0")) == 0)
{
printf("Error: if_nametoindex error/n");
return NULL;
}
if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
{
printf("Error: sysctl, take 1/n");
return NULL;
}
if ((buf = malloc(len)) == NULL)
{
printf("Could not allocate memory. error!/n");
return NULL;
}
if (sysctl(mib, 6, buf, &len, NULL, 0) < 0)
{
printf("Error: sysctl, take 2");
return NULL;
}
ifm = (struct if_msghdr *)buf;
sdl = (struct sockaddr_dl *)(ifm + 1);
ptr = (unsigned char *)LLADDR(sdl);
NSString *outstring = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x", *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5)];
free(buf);
return [outstring uppercaseString];
}
d、获取ip地址
- (NSString *)getDeviceIPAddresses
{
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
NSMutableArray *ips = [NSMutableArray array];
int BUFFERSIZE = 4096;
struct ifconf ifc;
char buffer[BUFFERSIZE], *ptr, lastname[IFNAMSIZ], *cptr;
struct ifreq *ifr, ifrcopy;
ifc.ifc_len = BUFFERSIZE;
ifc.ifc_buf = buffer;
if (ioctl(sockfd, SIOCGIFCONF, &ifc) >= 0)
{
for (ptr = buffer; ptr < buffer + ifc.ifc_len; )
{
ifr = (struct ifreq *)ptr;
int len = sizeof(struct sockaddr);
if (ifr->ifr_addr.sa_len > len)
{
len = ifr->ifr_addr.sa_len;
}
ptr += sizeof(ifr->ifr_name) + len;
if (ifr->ifr_addr.sa_family != AF_INET) continue;
if ((cptr = (char *)strchr(ifr->ifr_name, ':')) != NULL) *cptr = 0;
if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) continue;
memcpy(lastname, ifr->ifr_name, IFNAMSIZ);
ifrcopy = *ifr;
ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy);
if ((ifrcopy.ifr_flags & IFF_UP) == 0) continue;
NSString *ip = [NSString stringWithFormat:@"%s", inet_ntoa(((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr)];
[ips addObject:ip];
}
}
close(sockfd);
NSString *deviceIP = @"";
for (int i=0; i < ips.count; I++)
{
if (ips.count > 0)
{
deviceIP = [NSString stringWithFormat:@"%@",ips.lastObject];
}
}
return deviceIP;
}
e、获取WIFI IP地址
- (NSString *)getIpAddressWIFI
{
return [self ipAddressWithIfaName:@"en0"];
}
f、获取蜂窝地址
- (NSString *)getIpAddressCell
{
return [self ipAddressWithIfaName:@"pdp_ip0"];
}
- (NSString *)ipAddressWithIfaName:(NSString *)name
{
if (name.length == 0) return nil;
NSString *address = nil;
struct ifaddrs *addrs = NULL;
if (getifaddrs(&addrs) == 0)
{
struct ifaddrs *addr = addrs;
while (addr)
{
if ([[NSString stringWithUTF8String:addr->ifa_name] isEqualToString:name])
{
sa_family_t family = addr->ifa_addr->sa_family;
switch (family)
{
case AF_INET:// IPv4
{
char str[INET_ADDRSTRLEN] = {0};
inet_ntop(family, &(((struct sockaddr_in *)addr->ifa_addr)->sin_addr), str, sizeof(str));
if (strlen(str) > 0)
{
address = [NSString stringWithUTF8String:str];
}
} break;
case AF_INET6:// IPv6
{
char str[INET6_ADDRSTRLEN] = {0};
inet_ntop(family, &(((struct sockaddr_in6 *)addr->ifa_addr)->sin6_addr), str, sizeof(str));
if (strlen(str) > 0)
{
address = [NSString stringWithUTF8String:str];
}
}
default: break;
}
if (address) break;
}
addr = addr->ifa_next;
}
}
freeifaddrs(addrs);
return address ? address : @"该设备不存在该ip地址";
}
5、判断IPv4和IPv6并获取地址
a、需要用到以下文件和宏
// 需要用到以下文件
# import <ifaddrs.h>
# import <arpa/inet.h>
# import <net/if.h>
// 宏
#define IOS_CELLULAR @"pdp_ip0"
#define IOS_WIFI @"en0"
#define IOS_VPN @"utun0"
#define IP_ADDR_IPv4 @"ipv4"
#define IP_ADDR_IPv6 @"ipv6"
b、获取到硬件设备的各个IP
// 获取到硬件设备的各个IP
+(NSDictionary *)getDeviceIPAddresses
{
NSMutableDictionary *addresses = [NSMutableDictionary dictionaryWithCapacity:8];
// retrieve the current interfaces - returns 0 on succes
struct ifaddrs *interfaces;
if(!getifaddrs(&interfaces))
{
// Loop through linked list of interfaces
struct ifaddrs *interface;
for(interface = interfaces; interface; interface = interface->ifa_next)
{
// deeply nested code harder to read
if(!(interface->ifa_flags & IFF_UP))
{
continue;
}
const struct sockaddr_in *addr = (const struct sockaddr_in*)interface->ifa_addr;
char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ];
if(addr && (addr->sin_family == AF_INET || addr->sin_family == AF_INET6))
{
NSString *name = [NSString stringWithUTF8String:interface->ifa_name];
NSString *type;
if(addr->sin_family == AF_INET)
{
if(inet_ntop(AF_INET, &addr->sin_addr, addrBuf, INET_ADDRSTRLEN))
{
type = IP_ADDR_IPv4;
}
}
else
{
const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*)interface->ifa_addr;
if(inet_ntop(AF_INET6, &addr6->sin6_addr, addrBuf, INET6_ADDRSTRLEN))
{
type = IP_ADDR_IPv6;
}
}
if(type)
{
NSString *key = [NSString stringWithFormat:@"%@/%@", name, type];
addresses[key] = [NSString stringWithUTF8String:addrBuf];
}
}
}
// Free memory
freeifaddrs(interfaces);
}
return [addresses count] ? addresses : nil;
}
c、把拿到的地址打印一下 ,会看到各种不同的地址,如pdp_ip0/ipv4
,en0
/ipv4
,en0
/ipv6
等。
2020-07-29 14:09:47.782043+0800 Demo[88428:24193761] addresses: {
"awdl0/ipv6" = "fe80::a8a9:aff:fef2:3196";
"en0/ipv4" = "10.130.11.159";
"en0/ipv6" = "fe80::1049:1471:c55e:6a1d";
"en1/ipv4" = "10.130.168.169";
"en1/ipv6" = "fe80::c59:8913:4837:6f5e";
"en5/ipv4" = "169.254.62.42";
"en5/ipv6" = "fe80::c17:b446:86c9:2269";
"llw0/ipv6" = "fe80::a8a9:aff:fef2:3196";
"lo0/ipv4" = "127.0.0.1";
"lo0/ipv6" = "fe80::1";
"utun0/ipv6" = "fe80::cfd6:352a:e0a:561d";
"utun1/ipv6" = "fe80::9b18:e1e0:3e6a:2b25";
"utun2/ipv6" = "fe80::3660:5e7:7b88:748c";
"utun3/ipv6" = "fe80::93ac:7e8b:9b5d:1208";
"utun4/ipv6" = "fe80::ec2a:d9b2:b54:1941";
"utun5/ipv6" = "fe80::a033:c7cf:1848:d69c";
"utun6/ipv6" = "fe80::d1bf:ed25:ef85:9880";
"utun7/ipv6" = "fe80::e82b:d4de:202b:79bb";
"utun8/ipv6" = "fe80::4043:b856:1f83:3b0e";
"utun9/ipv6" = "fe80::95b4:12c8:e493:397a";
}
顾名思义这就是不同网络环境下的IPv4
、IPv6
地址,其中我们需要用到这几个:
#define IOS_CELLULAR @"pdp_ip0" // 蜂窝网络
#define IOS_WIFI @"en0" // WiFi网络
#define IOS_VPN @"utun0" // VPN
#define IP_ADDR_IPv4 @"ipv4"
#define IP_ADDR_IPv6 @"ipv6"
其他的当然也有用,比如打开热点之后的也能在里面找到。
d、拿到地址之后判断我们常用的这三个地址中有没有IPv6
的可用地址,因为用不同设备多获取几次地址,你就会发现其中有不可用地址,比如IPv4
中 127.0.0.1
、169.254.120.241
,IPv6
中 以fe80
开头的地址。所以 ,如果地址中有可用的IPv6
那就说明当前设备的网络环境有支持IPv6
,接下来我们就按照这个规则来判断是否支持IPv6
:
// 判断是否支持IPv6
+(BOOL)isSupportIpv6
{
NSArray *searchArray =
@[ IOS_VPN @"/" IP_ADDR_IPv6,
IOS_VPN @"/" IP_ADDR_IPv4,
IOS_WIFI @"/" IP_ADDR_IPv6,
IOS_WIFI @"/" IP_ADDR_IPv4,
IOS_CELLULAR @"/" IP_ADDR_IPv6,
IOS_CELLULAR @"/" IP_ADDR_IPv4 ] ;
NSDictionary *addresses = [self getDeviceIPAddresses];
NSLog(@"addresses: %@", addresses);
__block BOOL isSupportIpv6 = NO;
[searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop) {
NSLog(@"key: %@---addresses[key]: %@",key, addresses[key] );
if ([key rangeOfString:@"ipv6"].length > 0 && ![[NSString stringWithFormat:@"%@",addresses[key]] hasPrefix:@"(null)"] )
{
if (![addresses[key] hasPrefix:@"fe80"])
{
isSupportIpv6 = YES;
}
}
}];
return isSupportIpv6;
}
输出结果为:
2020-07-29 14:17:51.637787+0800 Demo[88453:24198960] key: utun0/ipv6---addresses[key]: fe80::cfd6:352a:e0a:561d
2020-07-29 14:17:51.638453+0800 Demo[88453:24198960] key: utun0/ipv4---addresses[key]: (null)
2020-07-29 14:17:51.638522+0800 Demo[88453:24198960] key: en0/ipv6---addresses[key]: fe80::1049:1471:c55e:6a1d
2020-07-29 14:17:51.638593+0800 Demo[88453:24198960] key: en0/ipv4---addresses[key]: 10.130.11.159
2020-07-29 14:17:51.638657+0800 Demo[88453:24198960] key: pdp_ip0/ipv6---addresses[key]: (null)
2020-07-29 14:17:51.638723+0800 Demo[88453:24198960] key: pdp_ip0/ipv4---addresses[key]: (null)
e、已经判断好IPv6
了,然后该取IP
地址了,这个时候呢因为有IPv6
存在的原因,所以取地址也需要区分一下,下面是取IP
的代码:
// 获取IP地址
+ (NSString *)getNetworkIPAddress
{
NSArray *searchArray = [self isSupportIpv6] ?
@[ IOS_WIFI @"/" IP_ADDR_IPv6, IOS_WIFI @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4 ] :
@[ IOS_WIFI @"/" IP_ADDR_IPv4, IOS_WIFI @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6 ] ;
NSDictionary *addresses = [self getIPAddresses];
__block NSString *address;
[searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop)
{
address = addresses[key];
if([self isValidatIP:address])
{
*stop = YES;
}
}];
NSLog(@"IPAddress: %@", address);
return address ? address : @"0.0.0.0";
}
看到这个地方,肯定有人有疑问了,为啥这个取值的数组searchArray
里面还是包含有IPv4
和IPv6
?可以看到IPv4
和IPv6
的顺序是不一样的, 我的理解是这个地方的设备访问肯定是有顺序的,比如我蜂窝网络虽然支持IPv6
,但是我现在连上了WiFi
,这个WiFi
是IPv4
的,那设备肯定是优先使用WiFi
进行网络访问。
如何判断一个字符串是不是IP
地址呢?从IPv4
的特点入手:由4段数字组成,其中有三个'.'
符号,每个数字的范围都在0~255之间,且每个数都是合法的数字(前面没有零)。所以我们可以先把这个字符串用‘.'
分隔开,再统计一下'.'
的个数,最后判断每个数字是否合法即可。判断IPv6
的思路和IPv4
类似,拆分并且判断分隔符的个数,依次检查每个数的正确性,区别是IPv6
的地址不需要判断前导零,但是需要注意大小写都是合法的。
实现方案一:正则表达式
+ (BOOL)isText:(NSString *)text pattern:(NSString *)pattern
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",pattern];
return [predicate evaluateWithObject:text];
}
/**
* 判断字符串是否为IP地址
* param iPAddress IP地址字符串
* return BOOL 是返回YES,否返回NO
*/
+ (BOOL)isIPAddress:(NSString *)iPAddress
{
NSString *pattern = @"^(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5]).(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5]).(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5]).(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])$";
return [self isText:iPAddress pattern:pattern];
}
实现方案二:数字范围
// 判断字符串是否为IP地址
+ (BOOL)isValidIP:(NSString *)ipStr
{
if (nil == ipStr)
{
return NO;
}
NSArray *ipArray = [ipStr componentsSeparatedByString:@"."];
if (ipArray.count == 4)
{
for (NSString *ipnumberStr in ipArray)
{
int ipnumber = [ipnumberStr intValue];
if (!(ipnumber>=0 && ipnumber<=255))
{
return NO;
}
}
return YES;
}
return NO;
}
输出结果为:
2020-07-29 14:17:51.640431+0800 Demo[88453:24198960] IPAddress: 10.130.11.159
2020-07-29 14:17:51.640534+0800 Demo[88453:24198960] IP地址为:10.130.11.159
这是在模拟器在不支持IPv6
的情况下获得的结果。
六、判断iOS设备是否安装了特定的app并跳转
1、应用间相互跳转应用场景
- 使用第三方用户登录,跳转到需授权的App,还需要返回到调用的程序,同时返回授权的用户名、密码,如QQ登录,微信登录等。
- 应用程序推广,跳转到另一个应用程序(本机已经安装),或者跳转到
APP Store
并显示应用程序下载页面(本机没有安装)。 - 第三方支付,跳转到第三方支付App,如支付宝支付,微信支付。
- 内容分享,跳转到分享App的对应页面,如分享给微信好友、分享给微信朋友圈、分享到微博。
- 显示位置、地图导航,跳转到地图应用。
- 使用系统内置程序,跳转到打电话、发短信、发邮件、
Safari
打开网页等内置App中。
2、应用间相互跳转实现原理
在iOS中打开一个应用程序只需要拿到这个应用程序的协议头即可,所以我们只需配置应用程序的协议头即可。通过设置跳转到应用B的URL Schemes
(自定义的协议头),应用B将其自身“绑定”到一个自定义URL Schemes
上,就可以从应用A中利用应用B的URL Schemes
启动应用B了。
知道app
的URL Schemes
可以使用openUrl
来获取iOS
是否安装了某款软件,比如这样[[UIApplication sharedApplication] canOpenURL:@"damon://"]
,会返回一个bool
值,为true
则安装了,否则没有安装。
获取app的url schemes 的方法 :把相应的app
的ipa
安装文件下载下来,把文件 .ipa
的后缀改成.zip
,然后解压,打开 Payload/xxx.app/Info.plist
这个文件,找到 URL types
下的URL Schemes
下的数组对应的值就是这个 app
的 URL Scheme
了 。
验证一个 URL Scheme 是否正确的方法:在真机设备(此设备要安装了待验证的 app)里面打开 Safari
,然后在地址栏中键入该应用的 URL Scheme
,后加 ://
,比如 Weico
的,在地址栏中键入 weico://
,然后点击确定,如果能正常调用出 Weico
,即代表这个 URL Scheme
正确可用。
3、应用A跳转到应用B
判断某个特定的App是否被安装一般都是用来进行跳转或打开的,如果安装了,我们该如何实现跳转呢?
步骤一:用Xcode创建两个iOS应用程序项目,项目名称分别为App-A
、App-B
。
步骤二:选择项目App-B
-> TARGETS
-> Info
-> URL Types
-> URL Schemes
,设置App-B
的URL Schemes
为AppB
。
步骤三:在应用程序App-A
中添加一个用来点击跳转的Button
,并监听点击事件,添加跳转代码。
- (void)jumpToAppB
{
// 1.获取应用程序App-B的URL Scheme
NSURL *appBUrl = [NSURL URLWithString:@"AppB://?AppA"];
// 2.判断手机中是否安装了对应程序
// iOS10 以后,canOpenURL与openURL合并为一个,可以实现动态跳转不用配置
if ([[UIApplication sharedApplication] canOpenURL:appBUrl])
{
NSLog(@"打开应用程序App-B");
}
else
{
NSLog(@"没有安装");
}
// 3.实现动态跳转并且加上配置
/*
[[UIApplication sharedApplication] openURL:appBUrl options:@{UIApplicationOpenURLOptionsSourceApplicationKey : @YES} completionHandler:^(BOOL success) {
if (!success)
{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"不能完成跳转" message:@"请确认App已经安装" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"确定"style:UIAlertActionStyleCancel handler:nil];
[alertController addAction:cancelAction];
[self presentViewController:alertController animated:YES completion:nil];
}
else if(back)
{
[self dismissViewControllerAnimated:YES completion:nil];
}
}];
*/
}
步骤四:需要将跳转的UrlScheme
添加到白名单即可。添加方法:info.plist
增加LSApplicationQueriesSchemes
(array
类型),把要跳转的app的UrlScheme
加进去。
运行效果如下:
从A跳转到B4、跳转到特定界面
很多时候,我们做应用程序之间的跳转并不只是跳转到其他程序就可以了,而是要跳转到其他程序的特定页面上。比如我们在浏览网页时,会有分享到微信朋友圈或是分享给微信朋友,这就需要跳转到微信朋友圈界面或是微信朋友选择界面。具体如何做呢?
步骤一:在APP-B中创建两个界面WriterViewController
和WasteMaterialsViewController
。
步骤二:在应用程序App-A中添加两个用来点击跳转的Button
,一个跳转到WriterViewController
,一个跳转到WasteMaterialsViewController
,并监听点击事件,添加跳转代码。
- (void)jumpToAppBWriterViewController
{
// 1.获取应用程序App-B的Page1页面的URL
NSURL *appBUrl = [NSURL URLWithString:@"AppB://WriterViewController"];
// 2.判断手机中是否安装了对应程序并跳转
if ([[UIApplication sharedApplication] canOpenURL:appBUrl])
{
[[UIApplication sharedApplication] openURL:appBUrl options:@{UIApplicationOpenURLOptionsSourceApplicationKey : @YES} completionHandler:^(BOOL success) {
if (!success)
{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"不能完成跳转" message:@"请确认App已经安装" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"确定"style:UIAlertActionStyleCancel handler:nil];
[alertController addAction:cancelAction];
[self presentViewController:alertController animated:YES completion:nil];
}
else if(self.isBack)
{
[self dismissViewControllerAnimated:YES completion:nil];
}
}];
}
else
{
NSLog(@"没有安装");
}
}
- (void)jumpToAppBWasteMaterialsViewController
{
// 1.获取应用程序App-B的Page1页面的URL
NSURL *appBUrl = [NSURL URLWithString:@"AppB://WasteMaterialsViewController"];
// 2.判断手机中是否安装了对应程序并跳转
if ([[UIApplication sharedApplication] canOpenURL:appBUrl])
{
[[UIApplication sharedApplication] openURL:appBUrl options:@{UIApplicationOpenURLOptionsSourceApplicationKey : @YES} completionHandler:^(BOOL success) {
if (!success)
{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"不能完成跳转" message:@"请确认App已经安装" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"确定"style:UIAlertActionStyleCancel handler:nil];
[alertController addAction:cancelAction];
[self presentViewController:alertController animated:YES completion:nil];
}
else if(self.isBack)
{
[self dismissViewControllerAnimated:YES completion:nil];
}
}];
}
else
{
NSLog(@"没有安装");
}
}
步骤三:在应用App-B中通过AppDelegate
监听跳转,进行判断,执行不同页面的跳转。
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
NSString *sourceId = options[@"UIApplicationOpenURLOptionsSourceApplicationKey"];
NSLog(@"openURL:url=%@;sourceId=%@;options=%@",url,sourceId,options);
// 1.获取导航栏控制器
UINavigationController *rootNavigationVC = (UINavigationController *)self.window.rootViewController;
// 2.每次跳转前必须是在根控制器(细节)
[rootNavigationVC popToRootViewControllerAnimated:NO];
// 3.根据字符串关键字来跳转到不同页面
if ([url.absoluteString containsString:@"WriterViewController"]) //跳转到应用App-B的WriterViewController页面
{
// 进行跳转
WriterViewController *vc = [[WriterViewController alloc] init];
[rootNavigationVC pushViewController:vc animated:YES];
}
else if ([url.absoluteString containsString:@"WasteMaterialsViewController"])// 跳转到应用App-B的WasteMaterialsViewController页面
{
// 进行跳转
WasteMaterialsViewController *vc = [[WasteMaterialsViewController alloc] init];
[rootNavigationVC pushViewController:vc animated:YES];
}
return YES;
}
运行效果如下:
跳转到特定界面5、从应用B跳转回应用A
a、实现方案
我们想要从应用B再跳转回应用A,那么在跳转到应用B的时候,还应将应用A的URL Schemes
传递过来,这样我们才能判断应该跳转回哪个应用程序。我们指定一个传递URL
的规则:协议头://应用B的URL Schemes?应用A的URL Schemes
。即:AppB://WriterViewController?AppA
。AppB
是跳转过来的应用App-B
的URL Schemes
;WriterViewController
是用来区别跳转页面的标识;? 是分割符;AppA
是跳转回的应用App-A
的URL Schemes
。
我们根据传递来的数据,进行反跳回去。之前我们在应用App-B
中通过AppDelegate
执行不同页面的跳转。在对应方法中我们可以拿到完整的URL
,在主控制器ViewController
中设定一个属性,将该URL
保存在主控制器中。在将要跳转的页面控制器中定义一个属性,用于接受、截取出跳转回的应用(即App-A
)的URL Schemes
,执行跳转。
b、具体步骤
步骤一:因为我们想要跳转回应用A,首先我们要先设置应用App-A
的URL Schemes
,将其设置为AppA
。同时在应用App-B
中添加白名单。
步骤二:在App-B
项目中的两个页面各添加一个Button
,用于跳转回App-A
。
步骤三:在App-A
中修改传递的URL
,分别修改为:AppB://WriterViewController?AppA
、AppB://WasteMaterialsViewController?AppA
。
步骤四:在将要跳转的页面控制器WasteMaterialsViewController
和WriterViewController
中定义一个属性@property (nonatomic, copy) NSString *urlString;
,用于接受、截取出跳转回的应用(即App-A
)的URL Schemes
,执行跳转。
步骤五:在App-B
的AppDelegate
中传递URL
。
// 进行跳转
WriterViewController *vc = [[WriterViewController alloc] init];
// 保存完整的App-A的URL给主控制器
vc.urlString = url.absoluteString;
[rootNavigationVC pushViewController:vc animated:YES];
步骤六:实现跳转回APP-A
的方法。
- (void)backToAppA
{
// 1.拿到对应应用程序的URL Scheme
NSString *urlSchemeString = [[self.urlString componentsSeparatedByString:@"?"] lastObject];
NSString *urlString = [urlSchemeString stringByAppendingString:@"://"];
// 2.获取对应应用程序的URL
NSURL *appAUrl = [NSURL URLWithString:urlString];
// 3.判断是否可以打开
if (appAUrl && [[UIApplication sharedApplication] canOpenURL:appAUrl])
{
[[UIApplication sharedApplication] openURL:appAUrl options:@{UIApplicationOpenURLOptionsSourceApplicationKey : @YES} completionHandler:^(BOOL success) {
if (!success)
{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"不能完成跳转" message:@"请确认App已经安装" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"确定"style:UIAlertActionStyleCancel handler:nil];
[alertController addAction:cancelAction];
[self presentViewController:alertController animated:YES completion:nil];
}
else if(self.isBack)
{
[self dismissViewControllerAnimated:YES completion:nil];
}
}];
}
else
{
NSLog(@"没有安装");
}
}
运行效果如下:
跳转回App-A6、常见Scheme
//调用邮件客户端(Apple Mail)
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"mailto://admin@eyecm.com"]];
//拨号(Phone Number)
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"tel://10086"]];
//调用短信(SMS)
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"sms:10086"]];
//调用浏览器(Safari Browser)
NSURL*url =[NSURL URLWithString:@"http://eyecm.com"];
[[UIApplication sharedApplication] openURL:url];
//调用应用商店(AppStore)
NSURL*appStoreUrl =[NSURL URLWithString:@"http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=291586600&amp;mt=8"];
[[UIApplication sharedApplication] openURL:appStoreUrl];
7、跳转到APP Store更新APP
a、真机运行效果
控制台输出结果
2020-10-29 10:39:19.310354+0800 DeviceInfoDemo[749:64988] 接口返回的APP信息包括:{
advisories = (
"\U9891\U7e41/\U5f3a\U70c8\U7684\U6210\U4eba/\U6027\U6697\U793a\U9898\U6750"
);
appletvScreenshotUrls = (
);
artistId = 614694882;
artistName = WeChat;
artistViewUrl = "https://apps.apple.com/cn/developer/wechat/id614694882?uo=4";
averageUserRating = "4.4047900000000002052047420875169336795";
averageUserRatingForCurrentVersion = "4.4047900000000002052047420875169336795";
bundleId = "com.tencent.xin";
contentAdvisoryRating = "17+";
currency = CNY;
currentVersionReleaseDate
2020-10-29 10:39:19.311923+0800 DeviceInfoDemo[749:64988] APP的当前版本:1.0,AppStore 上软件的最新版本:7.0.17
2020-10-29 10:39:23.520783+0800 DeviceInfoDemo[749:64988] 新版本更新内容:本次更新:
- 可前往“我 > 设置”开启青少年模式,并设置青少年模式下的访问范围。
- 可以在会话的表情面板搜索表情。
最近更新:
- 可跟随系统的设置,切换为深色模式。
- 优化了语音消息的发送体验,上滑转文字更方便了。
2020-10-29 10:39:31.858534+0800 DeviceInfoDemo[749:64926] AppStore 上APP的地址:https://apps.apple.com/cn/app/%E5%BE%AE%E4%BF%A1/id414478124?uo=4
2020-10-29 10:39:31.882374+0800 DeviceInfoDemo[749:64926] 进入到了APP Store更新APP
APP显示的信息
更新内容的提示 跳转到App Storeb、判断是否最新版本号(大于或等于为最新)
- (BOOL)isLastestVersion:(NSString *)currentVersion compare:(NSString *)lastestVersion
{
if (currentVersion && lastestVersion)
{
// 拆分成数组
NSMutableArray *currentItems = [[currentVersion componentsSeparatedByString:@"."] mutableCopy];
NSMutableArray *lastestItems = [[lastestVersion componentsSeparatedByString:@"."] mutableCopy];
// 如果数量不一样补0
NSInteger currentCount = currentItems.count;
NSInteger lastestCount = lastestItems.count;
if (currentCount != lastestCount)
{
// 取绝对值
NSInteger count = labs(currentCount - lastestCount);
for (int i = 0; i < count; ++i)
{
if (currentCount > lastestCount)
{
[lastestItems addObject:@"0"];
}
else
{
[currentItems addObject:@"0"];
}
}
}
// 依次比较
BOOL isLastest = YES;
for (int i = 0; i < currentItems.count; ++i)
{
NSString *currentItem = currentItems[i];
NSString *lastestItem = lastestItems[i];
if (currentItem.integerValue != lastestItem.integerValue)
{
isLastest = currentItem.integerValue > lastestItem.integerValue;
break;
}
}
return isLastest;
}
return NO;
}
c、更新APP
// 跳转到App Store的链接itms-apps://itunes.apple.com/cn/app/id(你的APPID)
// AppID的查询方法为APP Store里面的分享链接 https://apps.apple.com/cn/app/%E7%9F%A5%E4%B9%8E-%E6%9C%89%E9%97%AE%E9%A2%98-%E4%B8%8A%E7%9F%A5%E4%B9%8E/id432274380
- (void)checkVersion
{
// 你的项目ID
NSString *storeAppID = @"414478124";// 微信的AppID
// 参数appid指的是你在app在创建后的唯一标识,在iTunes Connect里可以查找到此信息
NSString *storeAppIDString = [NSString stringWithFormat:@"http://itunes.apple.com/cn/lookup?id=%@",storeAppID];
NSURL *url = [NSURL URLWithString:storeAppIDString];
// 联网检测项目在AppStore上的版本号
// 此接口将返回一个JSON格式的字串内容,其中一个就是版本信息,如"version":"1.2.0"
[[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary * dataDic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
NSArray *results = dataDic[@"results"];
if (results && results.count > 0)
{
NSDictionary *response = results.firstObject;
NSLog(@"接口返回的APP信息包括:%@",response);
// APP的当前版本
NSString *currentVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
// AppStore 上软件的最新版本
NSString *lastestVersion = response[@"version"];
NSLog(@"APP的当前版本:%@,AppStore 上软件的最新版本:%@",currentVersion,lastestVersion);
if (currentVersion && lastestVersion && ![self isLastestVersion:currentVersion compare:lastestVersion])
{
// 新版本更新内容
NSString *releaseNotes = response[@"releaseNotes"];
NSString *alertContent = [NSString stringWithFormat:@"%@\n\n是否前往 AppStore 更新版本?",releaseNotes];
NSLog(@"新版本更新内容:%@",releaseNotes);
// 给出提示是否前往 AppStore 更新
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"检测到有版本更新" message:alertContent preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"前往" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
// AppStore 上APP的地址
NSString *trackViewUrl = response[@"trackViewUrl"];
NSLog(@"AppStore 上APP的地址:%@",trackViewUrl);
if (trackViewUrl)
{
NSURL *appStoreURL = [NSURL URLWithString:trackViewUrl];
if ([[UIApplication sharedApplication] canOpenURL:appStoreURL])
{
[[UIApplication sharedApplication] openURL:appStoreURL options:@{} completionHandler:nil];
NSLog(@"进入到了APP Store更新APP");
}
}
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]];
dispatch_async(dispatch_get_main_queue(), ^{
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alert animated:YES completion:nil];
});
}
}
}] resume];
}
Demo
Demo在我的Github上,欢迎下载。
DeviceInfoDemo
参考文献
[iOS]判断IPv4和IPv6并获取地址,以及获取运营商、信号强度
iOSAPP开发项目搭建
iOS应用之间的跳转
史上最全的iOS各种设备信息获取总结