iOS设备标识符及权限合集

1,214 阅读6分钟
  • 因为新app涉及到iOS设备信息收集和很多权限的判断和限制,所以写篇文章总结下。

  • IDFA: 广告标示符,它是由系统存储着的,iOS6及以后使用。但是如果用户还原位置与隐私的话这个广告标识符就会重新生成(设置程序 -》 通用 -》 还原 -》还原位置与隐私) 。或者用户更明确的将设置中的广告还原掉(设置程序 -》 通用-》 关于本机 -》广告 -》 还原广告标示符) ,IDFA也会重新生成。而且用户可以在设置-》隐私 -》广告里面限制IDFA获取,一般用户都不知道有这个,哈哈,不过还是不能用来做唯一标识的id呦。

#import <AdSupport/ASIdentifierManager.h>
ASIdentifierManager *asIM = [[ASIdentifierManager alloc] init];
NSString *idfa = [asIM.advertisingIdentifier UUIDString];
  • IDFV: iOS6.0及以后使用,是给Vendor标识用户用的,vendor:卖主,小贩。经过测试发现com.test.app1和com.test.app2具有相同的idfv,而如果是com.app1和com.app2则是两个不同的idfv。准确点说,就是通过BundleID的反转的前两部分进行匹配,如果相同就是同一个Vender,共享同一个idfv的值。
NSString *idfv = [[UIDevice currentDevice].identifierForVendor UUIDString];
  • IMEI,IMSI: IMEI(International Mobile Equipment Identity)是国际移动设备身份码的缩写,国际移动装备辨识码,是由15位数字组成的”电子串号”,它与每台手机一一对应,而且该码是全世界唯一的。每一部手机在组装完成后都将被赋予一个全球唯一的一组号码,这个号码从生产到交付使用都将被制造生产的厂商所记录。手机用户可以在手机中查到自己手机的IMEI码。 重点来了 !iOS5以后不能再获取了,但通过私有Api能获取,这是在网上能查到的。git上的erica的UIDevice扩展文件,以前可用但由于IOKit framework没有公开,所以也无法使用。就算手动导入,依旧无法使用,看来获取IMEI要失败了,同时失败的还有IMSI。不过还存在另外一种可能,Stack Overflow上有人提供采用com.apple.coretelephony.Identity.get entitlement方法,but device must be jailbroken;在此附上链接,供大家参考:stackoverflow.com/questions/1… 如果实现了,自己拿来玩就行,别上架,这是会被拒掉的。

  • mac地址: 这个好像也不能拿了,拿到的是无效的:

-(NSString *)macAddress {
    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");
        free(buf);
        return NULL;
    }
    ifm = (struct if_msghdr *)buf;
    sdl = (struct sockaddr_dl *)(ifm + 1);
    ptr = (unsigned char *)LLADDR(sdl);
    NSString *macStr = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X",*ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5)];
    free(buf);
    return macStr;
}
  • UUID ,获取唯一标识符 每次运行都会发生变化,最理想的就是保存在keychain里面,以此作为标识用户设备的唯一标识符
CFUUIDRef uuid = CFUUIDCreate(NULL);
 assert(uuid != NULL);
 CFStringRef uuidStr = CFUUIDCreateString(NULL, uuid); 

之前使用了Git上的一个第三方库,SSKeychain,将UUID保存在keychain里面,每次调用先检查钥匙串里面有没有,有则使用,没有则写进去,保证其唯一性,具体使用如下:

CFUUIDRef uuid = CFUUIDCreate(NULL);
assert(uuid != NULL);CFStringRef uuidStr = CFUUIDCreateString(NULL, uuid);
NSString *identifierNumber = [SSKeychain passwordForService:@"com.test.app1"account:@"user"];
if (!identifierNumber){
 [SSKeychain setPassword: [NSString stringWithFormat:@"%@", uuidStr] forService:@"com.test.app1"account:@"user"];
identifierNumber = [SSKeychain passwordForService:@"com.test.app1"account:@"user"];
}
  • 网络状态判断
//监听网络状态
-(void)elm_networkTypeAsString{
    AFNetworkReachabilityManager *reachabilityManager = [AFNetworkReachabilityManager sharedManager];
    [reachabilityManager startMonitoring];
    [reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        switch (status) {
            case AFNetworkReachabilityStatusUnknown:
            {
                //未知网络
                self.networkType = 1;
            }
                break;
            case AFNetworkReachabilityStatusNotReachable:
            {
                //无法联网
                self.networkType = 1;
            }
                break;
            case AFNetworkReachabilityStatusReachableViaWWAN:
            {
                //手机自带网络
                self.networkType = @"蜂窝网络";
            }
                break;
            case AFNetworkReachabilityStatusReachableViaWiFi:
            {
                //WIFI
                self.networkType = @"wifi";
            }
        }
    }];
}
或者:
- (void)networkingStatesFromStatebar {
    UIApplication *app = [UIApplication sharedApplication];
    NSArray *children = [[[app valueForKeyPath:@"statusBar"] valueForKeyPath:@"foregroundView"] subviews];
    int type = 0;
    for (id child in children) {
        if ([child isKindOfClass:[NSClassFromString(@"UIStatusBarDataNetworkItemView") class]]) {
            type = [[child valueForKeyPath:@"dataNetworkType"] intValue];
        }
    }
    switch (type) {
        case 0:
            self.networkType = 1;
            break;
            
        case 1:
            self.networkType = 2;
            break;
            
        case 2:
            self.networkType = 3;
            break;
            
        case 3:
            self.networkType = 4;
            break;
            
        case 4:
            self.networkType = 4;
            break;
            
        case 5:
            self.networkType = 5;
            break;
            
        default:
            break;
    }
}
  • iOS通讯录权限 iOS通讯录权限有2个api,一个是iOS9之后出的,好处是不需要获取权限可以点击联系人直接获取不需要再点击进去选,而且有多选功能。因为我是兼容到iOS8,所以我把2个都集成了。
#import <AddressBookUI/ABPeoplePickerNavigationController.h>
#import <AddressBook/ABPerson.h>
#import <AddressBookUI/ABPersonViewController.h>
#import <ContactsUI/ContactsUI.h>
  • 获取单个联系人
if ([[UIDevice currentDevice].systemVersion integerValue] > 8) {
            [self showcontactsUI];
        }else {
            [self showPeople];
        }
-(void)showPeople{
    ABPeoplePickerNavigationController *nav = [[ABPeoplePickerNavigationController alloc] init];
    nav.peoplePickerDelegate = self;
    nav.predicateForSelectionOfPerson = [NSPredicate predicateWithValue:false];
    [self presentViewController:nav animated:YES completion:nil];
}
-(void)showcontactsUI {
    // 1. 创建控制器
    CNContactPickerViewController * picker = [CNContactPickerViewController new];
    // 2. 设置代理
    picker.delegate = self;
    // 4. 弹出
    [self presentViewController: picker  animated:YES completion:nil];
}
-(void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact {
    CNLabeledValue *labeledValue = contact.phoneNumbers[0];
    CNPhoneNumber *phoneNumber = labeledValue.value;
    NSString *phoneNO = phoneNumber.stringValue;
    phoneNO = [phoneNO stringByReplacingOccurrencesOfString:@"-" withString:@""];
    NSString *name = [CNContactFormatter stringFromContact:contact style:CNContactFormatterStyleFullName];
    UITableViewCell *cell = [self.personTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:self.contactIndexPath.section]];
    cell.detailTextLabel.textColor = [UIColor colorWithHexString:FontColor333333];
    cell.detailTextLabel.text = phoneNO;
    if (self.contactIndexPath.section == 0) {
        self.firstModel.phone = phoneNO;
        self.firstModel.isEmergencyContact = YES;
        self.firstModel.tookTime = 0;
        self.nameTextFieldone.text = name;
        self.firstModel.contactName = name;
    }else if(self.contactIndexPath.section == 1){
        self.secendModel.phone =phoneNO;
        self.secendModel.isEmergencyContact = YES;
        self.secendModel.tookTime = 0;
        self.nameTextFieldtwo.text = name;
        self.secendModel.contactName = name;
    }
}
-(void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker
{
    [peoplePicker dismissViewControllerAnimated:YES completion:nil];
}
-(void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier {
    ABMultiValueRef phone = ABRecordCopyValue(person, kABPersonPhoneProperty);
    
    long index = ABMultiValueGetIndexForIdentifier(phone,identifier);
    NSString *phoneNO = (__bridge NSString *)ABMultiValueCopyValueAtIndex(phone, index);
    
    if ([phoneNO hasPrefix:@"+"]) {
        phoneNO = [phoneNO substringFromIndex:3];
    }
    
    phoneNO = [phoneNO stringByReplacingOccurrencesOfString:@"-" withString:@""];
    
    NSString *firstName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonFirstNameProperty));
    NSString *lastName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonLastNameProperty));
    
    if (phone ) {
        UITableViewCell *cell = [self.personTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:self.contactIndexPath.section]];
        cell.detailTextLabel.textColor = [UIColor colorWithHexString:FontColor333333];
        cell.detailTextLabel.text = phoneNO;
        [peoplePicker dismissViewControllerAnimated:YES completion:nil];
    }
    
    if (self.contactIndexPath.section == 0) {
        self.firstModel.phone = phoneNO;
        self.firstModel.isEmergencyContact = YES;
        self.firstModel.tookTime = 0;
        self.nameTextFieldone.text = [NSString stringWithFormat:@"%@%@", firstName, lastName];
        self.firstModel.contactName = [NSString stringWithFormat:@"%@%@", firstName, lastName];
    }else if(self.contactIndexPath.section == 1){
        self.secendModel.phone = phoneNO;
        self.secendModel.isEmergencyContact = YES;
        self.secendModel.tookTime = 0;
        self.nameTextFieldtwo.text = [NSString stringWithFormat:@"%@%@", firstName, lastName];
        self.secendModel.contactName = [NSString stringWithFormat:@"%@%@", firstName, lastName];
    }
}
  • 获取全部联系人
-(void)getAddressBook {
    //2. 创建通讯录
    ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
    //3. 获取所有联系人
    CFArrayRef peosons = ABAddressBookCopyArrayOfAllPeople(addressBook);
    //4. 遍历所有联系人来获取数据(姓名和电话)
    CFIndex count = CFArrayGetCount(peosons);
    for (CFIndex i = 0 ; i < count; i++) {
        KZWConactPersonModel *personModel = [KZWConactPersonModel new];
        personModel.isEmergencyContact = NO;
        personModel.type = @"OTHER";
        personModel.source = @"TELEPHONE_BOOK";
        personModel.tookTime = [[NSDate date] timeIntervalSince1970] - self.startTime;
        //5. 获取单个联系人
        ABRecordRef person = CFArrayGetValueAtIndex(peosons, i);
        //6. 获取姓名
        NSString *lastName = CFBridgingRelease(ABRecordCopyValue(person, kABPersonLastNameProperty));
        NSString *firstName  = CFBridgingRelease(ABRecordCopyValue(person, kABPersonFirstNameProperty));
        if (lastName && !firstName) {
            personModel.contactName = [NSString stringWithFormat:@"%@", lastName];
        }else if (firstName && !lastName) {
            personModel.contactName = [NSString stringWithFormat:@"%@", firstName];
        }else if (firstName && lastName) {
            personModel.contactName = [NSString stringWithFormat:@"%@%@", firstName,lastName];
        }
        //7. 获取电话
        ABMultiValueRef phones = ABRecordCopyValue(person, kABPersonPhoneProperty);
        //7.1 获取电话的count数
        CFIndex phoneCount = ABMultiValueGetCount(phones);
        //7.2 遍历所有电话号码
        for (CFIndex i = 0; i < phoneCount; i++) {
            NSString *value = CFBridgingRelease(ABMultiValueCopyValueAtIndex(phones, i));
            // 打印标签和电话号
            if ([value hasPrefix:@"+"]) {
                value = [value substringFromIndex:3];
            }
            
            value = [value stringByReplacingOccurrencesOfString:@"-" withString:@""];
            personModel.phone = value;
            if (!firstName && !lastName) {
                personModel.contactName = value;
            }
            [self.personArray addObject:personModel];
        }
        //8.1 释放 CF 对象
        CFRelease(phones);
    }
    //8.1 释放 CF 对象
    CFRelease(peosons);
    CFRelease(addressBook);
    [self uploadAddressBook];
}
  • 权限判断 因为需求需要在某些权限没有得到的情况下不能打开默写页面,所以你需要在判断前让用户去点击同意权限,所以我把这部分代码放在了appdelegate中,
-(void)getAbleAddressBock {
    [self.locationManager requestWhenInUseAuthorization]; //位置权限提示的弹起

    ABAuthorizationStatus status = ABAddressBookGetAuthorizationStatus();
    //2. 创建 AddrssBook
    ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
    //3. 没有授权时就授权
    if (status == kABAuthorizationStatusNotDetermined) {
        ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
            //3.1 判断是否出错
            if (error) {
                CFRelease(addressBook);
                return;
            }
            //3.2 判断是否授权
            if (granted) {
                NSLog(@"已经授权");
                CFRelease(addressBook);
            } else {
                NSLog(@"没有授权");
                CFRelease(addressBook);
            }
        });
    }
    
    AVAuthorizationStatus avstatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    switch (avstatus) {
        case AVAuthorizationStatusNotDetermined:{
            // 许可对话没有出现,发起授权许可
            
            [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
                
                if (granted) {
                    //第一次用户接受
                }else{
                    //用户拒绝
                }
            }];
            break;
        }
        case AVAuthorizationStatusAuthorized:{
            // 已经开启授权,可继续
            
            break;
        }
        case AVAuthorizationStatusDenied:
        case AVAuthorizationStatusRestricted:
            // 用户明确地拒绝授权,或者相机设备无法访问
            
            break;
        default:
            break;
    }
}
  • 获取权限,并判断 我这里的处理是先获取用户需要的权限,如果用户拒绝了这些权限,我就用一个数组收集起来然后页面展示你这些权限没有给,不能继续使用,点击去设置权限那里。
-(void)authorizations {
    AVAuthorizationStatus avstatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    self.camera = [KZWAuthorizationModel new];
    self.camera.name = @"允许访问相机";
    self.camera.isTure = (avstatus == AVAuthorizationStatusAuthorized)?YES:NO;
    
    CLAuthorizationStatus CLstatus = [CLLocationManager authorizationStatus];
    self.location = [KZWAuthorizationModel new];
    self.location.name = @"允许访问地理位置";
    self.location.isTure = (CLstatus == kCLAuthorizationStatusAuthorizedWhenInUse)?YES:NO;
    
    self.abadress = [KZWAuthorizationModel new];
    self.abadress.name = @"允许访问通讯录";
    self.abadress.isTure = (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized)?YES:NO;
    
    if (avstatus != AVAuthorizationStatusAuthorized || CLstatus != kCLAuthorizationStatusAuthorizedWhenInUse || ABAddressBookGetAuthorizationStatus() != kABAuthorizationStatusAuthorized) {
        [self.authorizationsArray removeAllObjects];
        [self.authorizationsArray addObject:self.camera];
        [self.authorizationsArray addObject:self.location];
        [self.authorizationsArray addObject:self.abadress];
        [self showAuthorizationView:self.authorizationsArray];
    }else {
       todo
    }
}
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
  • 获取app版本
+(NSString *)elm_version {
  return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
}
  • 获取系统版本
[UIDevice currentDevice].systemVersion 

参考:http://blog.csdn.net/Sir_Coding/article/details/68943033