iOS應用開發中的設備標識

對於iOS應用開發者來說,蘋果所提供的官方後臺系統實際上就是iTunes Connect了。通過iTunes Connect我們創建應用記錄,提交應用給蘋果審覈,發佈應用,通過iTunes Connect我們可以配置銀行卡收錢( 這個很重要:) ),我們可以看到應用的下載量和收據數據報表。

但總體來說iTunes Connect提供的功能還比較有限,而且基本不能定製(除非你能說服蘋果)。

對於應用發佈後的跟蹤和數據收集,很多時候是iTunes Connect之外的事情,甚至有些開發者對於閃退日誌收集等也拋棄了iTunes Connect的crash report。那麼一個識別具體設備的標誌,或者說能夠區分不同設備的方法就顯得很重要。這篇文章我簡要整理一下。

大家可以明確一點,爲了保護用戶隱私,蘋果並沒開放太多API給開發者,使得設備的數據追蹤變得越來越難。

IMEI、IMSI、ICCID這類在iTunes Mac客戶端可以直接看到的東西,現在都不要想着能通過API在程序中獲取到。

0. UDID

在iOS6.1及之前,我們可以再UIKit.framework的UIDevice類中看到一個屬性,那就是uniqueIdentifier,也就是我們通常所提到的UDID。

但這個屬性的聲明後面,有NS_DEPRECATED_IOS(2_0, 5_0),意思就是5.0開始就是deprecated的了,是過時的,不建議再繼續使用。

到了iOS SDK的7.0版本,在UIDevice類中,就再也找不到這個uniqueIdentifier屬性了。而且蘋果方面明確表示在2013年5月份之後不再對此支持。即使使用老版本的SDK,也不一定能通過蘋果審覈,聽說有人還嚐到過Rejected的苦頭。

1. identifierForVendor (IDFV)

貌似也有人簡略爲IDFV,這是蘋果安撫大家的一個UDID的替代品,也是UIDevice類的屬性。

按照蘋果的文檔說明,這個IDFV在同一設備上的所有同Vendor應用得到的ID是相同的,而不同的設備就有不同的IDFV。當這個設備上,同Vendor的所有應用都被卸載掉之後,不能保證同一設備再次安裝這個Vendor的應用時,得到同樣的ID。

簡單來說,如果一個設備上只裝了你一個應用,卸載掉再裝ID也許就不同了。這樣,對於唯一設備的定義就和原來UDID的很不同。這點並不令廣大開發者感到滿意。

2. MAC地址

如上所述,identifierForVendor不是很令大家滿意,於是各種民間方法就出現了。一個方案就是用MAC地址。

學過計算機網絡課程的同學們應該瞭解,要先完成底層網絡通信實現MAC地址是必須有的,而這個在網卡製造時要保證全球唯一的,一個設備通常一個網卡就夠用了,所以這個在一定程度上可以作爲設備標識。

於是乎,拿出了各種底層庫,做各種計算,拿到一個MAC地址字符串。

下面這段是網絡上比較流行的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
- (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 *outstring = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X",
                           *ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5)];
    free(buf);
 
    return outstring;
}

而這段需要引入幾個系統庫:

1
2
3
4
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>

嗯,不錯,這樣貌似就可以獲取比較有意義的設備標識了。如果這個時候,你沾沾自喜了,那你還是不瞭解蘋果爲了保護用戶隱私,下了多大力氣。

很遺憾第告訴大家,在裝有iOS7的設備上,這段代碼計算出的值,永遠是一個恆定的:

02:00:00:00:00:00

所以,夢又碎了。。

3. advertisingIdentifier (IDFA)

蘋果爲了完善自己的生態圈,在2010年前後推出了iAd廣告網絡。那麼這個advertisingIdentifier和這個iAd的關係就不言自喻了。如果不瞭解廣告也沒關係,簡單來講,現在的互聯網廣告精準投放需要了解用戶數據,基於這些信息是的廣告更有效率,唯一標識就很重要,就用到了IDFA。

advertisingIdentifier在AdSupport.framework的ASIdentifierManager類中,是其中兩個屬性中的一個。

可以說,用這個IDFA標識設備應該還是很精準的(不然iAd就徹底不用玩了),很多開發者還在使用。

但再次,非常遺憾地說,貌似最近蘋果對這個的要求也越來越嚴格了。

我這裏收到過的一次拒審原因是:

PLA 3.3.12

In addition, we found your app uses the iOS Advertising Identifier for purposes other than to serve ads. This does not comply with the terms of the iOS Developer Program License Agreement, as required by the App Store Review Guidelines.

Specifically, section 3.3.12 of the iOS Developer Program License Agreement states:

"You and Your Applications (and any third party with whom you have contracted to serve advertising) may use the Advertising Identifier, and any information obtained through the use of the Advertising Identifier, only for the purpose of serving advertising. If a user resets the Advertising Identifier, then You agree not to combine, correlate, link or otherwise associate, either directly or indirectly, the prior Advertising Identifier and any derived information with the reset Advertising Identifier."

Please check your code - including any third-party libraries - to remove inappropriate uses of:

class: ASIdentifierManager
selector: advertisingIdentifier
framework: AdSupport.framework

所以,還在“濫用”IDFA標識設備的朋友們要小心了。

4. 接下來怎麼辦?

“這是個很好的問題”一般演講者覺得提問者的問題很難回答的時候會這麼說,然後接下來繞彎子兜圈子。。。當然這裏我不會。

原則上來說,iOS的開發者是生長在蘋果的生態圈,需要與蘋果合作,尊重其理念。所以這裏的結論是,最好的辦法,就是不去標識設備ID。

但人是活的,所以這裏有一個比較俗套的辦法:

前面講到IDFV不能令人滿意,很大程度上是因爲卸載之後重裝ID就變了。我們在卸載一個應用的時候,其沙箱內的數據應該會一起不見,那有沒有沙箱之外的地方呢?答案自然是肯定的,不過按照蘋果的說法,沙箱之外需要系統的API協助。一個比較直觀的選擇就是Key Chain。所以通俗的方案就是自己生成可見範圍內的唯一ID,存入Key Chain,下次再來重裝依然可以讀到。

在蘋果對各種ID封殺後,很多民間的做法就是這種思路。這個做法對於一般條件下的數據收集應該足夠了,但如果考慮到設備黑名單等安全識別,還差得遠。

後面有機會我們再來討論這個問題,設備標識的事兒就整理到這裏。歡迎大家給出更好的方案來唯一標識設備,歡迎跟帖。

附一張轉自Cocoachina的圖片,不考慮現今實際的可用情況,對比一下這些ID們的差異:

各類Device ID相關標識的比較

各類Device ID相關標識的比較

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章