開發中經常使用工具 - 獲取設備的惟一標識、UDID、UUID、keychain保存UUID、判斷網絡

UDID

  • 全名:Unique Device Identifie(設備惟一標識符)
  • 說明:UDID,即設備惟一標識符,這是除序列號以外每臺iOS設備的獨一無二的號碼。UDID只是和設備相關的,是用來區分每個惟一的iOS設備(包括iPhone、iPad等),是由40個字符的字母和數字組成的。
  • 做用:能夠關聯其它各類數據到相關設備上。好比:程序發佈前的經過測試版本進行測試等都須要UDID。
  • 獲取方法: UDID能夠直接經過ITunes查看,手機鏈接上電腦以後點擊序列號就會變成UDID,以下圖。也能夠經過Xcode查看:點擊Window->Devices and Simulators->identifier就能看到,以下圖。

經過ITunes查看UDID

經過XCode查看UDID

  • 代碼獲取方法:
    在iOS5以後,蘋果就禁止了經過代碼獲取UDID。轉而用[UIDevice currentDevice].identifierForVendor.UUIDString替代。可是這個不是真正的UDID.關閉的緣由是由於隱私問題。以後蘋果禁止上架試圖獲取UDID的應用。

UUID

  • 全名:Universally Unique Identifier(通用惟一標識符)
  • 說明:UUID是一個經過小橫線鏈接起來的32位的十六進制序列。如0DEF9507-EB5A-471A-8BC7-638A0B0A327D。可是UUID並不像UDID同樣是唯一的,它只是在某一時空是惟一的,當每次寫在應用以後獲取到的UUID都是不同的。好比經過一個for循環打印一下UUID能就能看出不同:
for (int i = 0; i < 5; i ++) {
        NSLog(@"uuid %zd = %@", i,[NSUUID UUID].UUIDString);
    }

打印結果
那是否是這樣就不能惟一標識了呢?並非,開發者能夠將這個UUID保存在keychain裏面,以此做爲惟一標識符。接下來會講到。html

  • 代碼獲取UUID:
NSString * uuid = [NSUUID UUID].UUIDString;

用keychain保存UUID

keychain介紹

蘋果在OS X和IOS系統都有提供的一種安全存儲敏感信息的工具,即keychain。所謂銘感信息,即用戶ID、password、certificate等。keychain裏面存儲的數據是item。這些item是以key-value的形式存儲的,能夠理解爲Dictonary。利用keychain存儲這些信息能夠提升用戶體驗,免除用戶重複輸入用戶名和密碼等繁瑣的操做。同時,蘋果的這套keychain Service安全機制可以保障存儲的信息不會被竊取,因此能夠用來存儲UUID等。git

爲何要用keychain?

  1. keychain的數據並不是是存放在應用程序的沙盒中,因此即便當用戶刪除app,存儲的資料依然在keychain中。用戶再一次安裝該應用程序的時候又能夠從keychain中獲取數據。
  2. keychain的數據有通過加密,更安全。
  3. keychain提供了一個公共區"keychain access group",能夠經過這個group實現應用程序之間的數據共享。

keychain中的item

keychain中是存放的item。而且能夠存聽任意數量的item。keychain會對須要加密的item進行加密保護,好比:密碼。而對於像證書就就不會加密。github

在蘋果提供的API中能夠看到有五種類型的item:安全

kSecClassInternetPassword //Specifies Internet password items.
  kSecClassGenericPassword  //Specifies generic password items.
  kSecClassCertificate      //Specifies certificate items.
  kSecClassKey              //Specifies key items.
  kSecClassIdentity         //Specifies identity items.

蘋果提供了四種操做item的方法,即增、刪、改、查操做:網絡

// 1. 查詢已存在的item/items
 SecItemCopyMatching(CFDictionaryRef query, CFTypeRef * __nullable CF_RETURNS_RETAINED result)
 
 // 2. 添加 item/items到keychain
 SecItemAdd(CFDictionaryRef attributes, CFTypeRef * __nullable CF_RETURNS_RETAINED result)
 
 // 3. 更新已存在的item/items
 SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate)
 
 // 4. 刪除已存在的 item/items
 SecItemDelete(CFDictionaryRef query)

代碼環節

能夠寫一個KeychainWrapper工具類來實現keychain的操做。核心代碼以下app

// 根據特定的Service建立一個用於操做KeyChain的Dictionary
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service
{
    // 添加的字典不懂?
    return [NSMutableDictionary dictionaryWithObjectsAndKeys:
            (__bridge id)(kSecClassGenericPassword), kSecClass,
            service, kSecAttrService,
            service, kSecAttrAccount,
            kSecAttrAccessibleAfterFirstUnlock, kSecAttrAccessible,
            nil];
}

// 保存數據到keychain中
+ (BOOL)saveDate:(id)date withService:(NSString *)service
{
    // 1. 建立dictonary
    NSMutableDictionary * keychainQuery = [self getKeychainQuery:service];
    // 2. 先刪除
    SecItemDelete((CFDictionaryRef)keychainQuery);
    // 3. 添加到date到query中
    [keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:date] forKey:(id<NSCopying>)kSecValueData];
    // 4. 存儲到到keychain中
    OSStatus status = SecItemAdd((CFDictionaryRef)keychainQuery, NULL);
    
    return status == noErr ? YES : NO;
}

// 從keychain中查找數據
+ (id)searchDateWithService:(NSString *)service
{
    id retsult = nil;
    NSMutableDictionary * keychainQuery = [self getKeychainQuery:service];
    [keychainQuery setObject:(id)kCFBooleanTrue forKey:(id<NSCopying>)kSecReturnData];
    [keychainQuery setObject:(id)kSecMatchLimitOne forKey:(id<NSCopying>)kSecMatchLimit];
    
    CFTypeRef resultDate = NULL;
    if (SecItemCopyMatching((CFDictionaryRef)keychainQuery, &resultDate)== noErr) {
        @try{
            retsult = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)resultDate];
        }
        @catch(NSException *e){
            NSLog(@"查找數據不存在");
        }
        @finally{
            
        }
    }
    if (resultDate) {
        CFRelease(resultDate);
    }
    return retsult;
}

// 更新keychain中的數據
+ (BOOL)updateDate:(id)date withService:(NSString *)service
{
    NSMutableDictionary * searchDictonary = [self getKeychainQuery:service];
    
    if (!searchDictonary) {return  NO;}
    
    NSMutableDictionary * updateDictonary = [NSMutableDictionary dictionary];
    [updateDictonary setObject:[NSKeyedArchiver archivedDataWithRootObject:date] forKey:(id<NSCopying>)kSecValueData];
    OSStatus status = SecItemUpdate((CFDictionaryRef)searchDictonary, (CFDictionaryRef)updateDictonary);
    return status == noErr ? YES : NO;
}

// 刪除keychain中的數據
+ (BOOL)deleteDateiWithService:(NSString *)service
{
    NSMutableDictionary * keychainQuery = [self getKeychainQuery:service];
    OSStatus status = SecItemDelete((CFDictionaryRef)keychainQuery);
    return status == noErr ? YES : NO;
}

使用keychain保存UUID

有了上面的方法,接下來就操做就很簡單了:框架

/**
  先從keychain裏面加載uuid 若是沒有 就獲取uuid並加載到keychain中
 */
+ (NSString *)getUUIDfromKeychain
{
    NSString * uuid = NULL;
    uuid = [KeychainWrapper searchDateWithService:DEMO_UUID];
    if (uuid) {
        return uuid;
    }else{
        uuid = [self getRandomUUID];
        if([KeychainWrapper saveDate:uuid withService:DEMO_UUID]){
            return uuid;
        }else{
            return NULL;
        }
    }
}

+ (NSString *)getRandomUUID
{
    return [NSUUID UUID].UUIDString;
}

打印出來發現獲取的uuid是同樣的,說明keychain保存成功了:

dom


IDFA

  • 全名:Identifier for Advertising(廣告標示符)
  • 來源:iOS6.0+
  • 說明:IDFA,即廣告標示符。這是蘋果專門用來給廣告商追蹤用戶而設置的,在同一個設備上的全部APP都會取到相同的值。固然,是否開啓IDFA取決於用戶的心情,用戶能夠在:設置 -> 隱私 -> 廣告 中關閉廣告追蹤。


因此IDFA就存在取不到的狀況,因此通常不會只用IDFA識別用戶。ide

  • 代碼獲取方式:
/**
 *  獲取IDFA,若是用戶關閉此功能,就會存在娶不到的狀況
 */
+ (NSString *)getIDFA
{
    return [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
}

IDFV

  • 全名:Identifier For Vendor
  • 來源:iOS6.0+
  • 說明:IDFV是給供應商(Vender)標識用戶用的,也就是說屬於同一個供應商的應用的IDFV都是相同的。好比com.vender.app1com.vender.app2這兩個BundleID都是屬於同一個供應商,那麼這兩個應用的IDFV都是相同的。原理是經過BundleID的反轉的前兩部分進行匹配,若是相同就是同一個Vender,共享同一個idfv的值。值得一提的是,IDFV是必定能取到的。可是若是用戶將屬於同一個Vender的全部App卸載,則IDFV的值會被重置,當再重裝此Vender的App時IDFV的值和以前不一樣。
  • 代碼獲取方式:
/**
 *  獲取IDFV
 */
+ (NSString *)getIDFV
{
   return [[[UIDevice currentDevice] identifierForVendor] UUIDString];
}

獲取運營商&&判斷網絡類型

獲取運營商

獲取運營商很簡單,只須要用到CTTelephonyNetworkInfoCTCarrier兩個類便可,值得注意的是,須要導入兩個頭文件:工具

#import <CoreTelephony/CTTelephonyNetworkInfo.h> 
#import <CoreTelephony/CTCarrier.h>

代碼:

/**
 *  獲取設備運營商
 */
+ (NSString *)getCarrier
{
    CTTelephonyNetworkInfo * info = [[CTTelephonyNetworkInfo alloc]init];
    CTCarrier * carrier = [info subscriberCellularProvider];
    
    NSString * mobile;
    if (!carrier.isoCountryCode) {
        NSLog(@"沒有SIM卡");
        mobile = @"無運營商";
    }else{
        mobile = [carrier carrierName];
    }
    
    return mobile;
}

判斷網絡類型

判斷網絡類型的方式有幾種:

  1. 經過狀態欄進行判斷:
    缺點:狀態欄能夠隱藏,一旦隱藏就沒法獲取。
  2. 用三方框架AFNetworking判斷
    缺點:必須導入該框架。
  3. Reachability + CTTelephonyNetworkInfo
    缺點:代碼較多

這裏使用第三種方式獲取網絡狀態類型Reachability + CTTelephonyNetworkInfo。Reachability能夠到官網去下載Reachability
Reachability中有三種類型的網絡狀態:

NotReachable  // 無網絡鏈接
    ReachableViaWiFi // WIFI
    ReachableViaWWAN // 蜂窩移動類型

因此還須要經過CTTelephonyNetworkInfo對蜂窩移動網絡類型判斷。CTTelephonyNetworkInfo中蜂窩移動網絡類型有:

CTRadioAccessTechnologyGPRS          
CTRadioAccessTechnologyEdge          
CTRadioAccessTechnologyWCDMA         
CTRadioAccessTechnologyHSDPA         
CTRadioAccessTechnologyHSUPA         
CTRadioAccessTechnologyCDMA1x        
CTRadioAccessTechnologyCDMAEVDORev0  
CTRadioAccessTechnologyCDMAEVDORevA  
CTRadioAccessTechnologyCDMAEVDORevB  
CTRadioAccessTechnologyeHRPD         
CTRadioAccessTechnologyLTE

完整代碼:

/**
 *  判斷當前網絡類型
 */
+ (NSString *)getNetworkType
{
    
    Reachability * reachability = [Reachability reachabilityWithHostName:@"www.baidu.com"];
    NetworkStatus netStatus = [reachability currentReachabilityStatus];
    NSString * networkType = @"";
    
    switch (netStatus) {
        case ReachableViaWiFi:
            networkType = @"WIFI";
            break;
            
        case ReachableViaWWAN:
        {
            // 判斷蜂窩移動類型
            CTTelephonyNetworkInfo * networkInfo = [[CTTelephonyNetworkInfo alloc]init];
            if ([networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyGPRS]) {
                networkType = @"2G";
            } else if ([networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyEdge]) {
                networkType = @"2G";
            } else if ([networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyWCDMA]) {
                networkType = @"3G";
            } else if ([networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyHSDPA]) {
                networkType = @"3G";
            } else if ([networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyHSUPA]) {
                networkType = @"3G";
            } else if ([networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMA1x]) {
                networkType = @"3G";
            } else if ([networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORev0]) {
                networkType = @"3G";
            } else if ([networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORevA]) {
                networkType = @"3G";
            } else if ([networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORevB]) {
                networkType = @"3G";
            } else if ([networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyeHRPD]) {
                networkType = @"3G";
            } else if ([networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyLTE]) {
                networkType = @"4G";
            }
        }
            break;
            
        case NotReachable:
            networkType = @"當前無網絡鏈接";
            break;
    }
   
    return networkType;
}

結語

我把以上代碼都封裝到了DeviceInfo中,須要的能夠直接拖入這個文件便可使用。github連接:DeviceInfo

參考博客:

  1. keychian

  2. Reachability:

相關文章
相關標籤/搜索