How to save and get Udid using keychain
This article is the first article in the IOS7 series, which mainly introduces the use of keychain to save and get app data, to solve the problem of iOS7 to get the same udid. and give a tool to get udid, easy to use, only need to replace two places.
One, iOS does not use version to obtain Udid method comparison
1) IOS 5.0
After IOS 2.0, Uidevice provides a method for acquiring a unique identifier for a device uniqueidentifier, which allows us to obtain the serial number of the device, which is the only identifier that can be confirmed so far. Not for the time being, because the unique identifier corresponds to phone one by one, Apple feels it may reveal user privacy, so the method is discarded after IOS 5.0.
And Apple to do more ruthless, after May this year to submit the App Store products are not allowed to use uniqueidentifier interface, and even some friends because the code has UDID still be called back, it seems that the road was sealed dead.
2) IOS 6.0
The IOS 6.0 system has two new interfaces for replacing uniqueidentifier, namely: Identifierforvendor,advertisingidentifier.
The official documentation for the Identifierforvendor interface is described below:
The value of this property is the same for apps, come from the same vendor running on the same device. A different value is returned for apps on the same device this come from different vendors, and to apps on different Devi Ces regardless of vendor. The value of this property may be nil if the app was running in the background, before the user have unlocked the device the First time after the device has been restarted. If The value is nil, wait and get the value again later. The value of the remains the same while the app (or another app from the same vendor) are installed on the IOS de Vice. The value changes when the user deletes any of that vendor's apps from the device and subsequently reinstalls one or more of them. Therefore, if your app stores the value of this property anywhere, you should gracefully handle situations where the ident Ifier changes.
Probably means "the same developer app will get the same ID on the specified machine." When we delete all the apps from a developer on one device, the next fetch will get a different ID. "That means we can't get the ID that uniquely identifies the device through this interface, and the problem is that it's always hard to be smart programmers, so we think of the MAC address of WiFi to replace the deprecated uniqueidentifier method." Specific methods at night a lot of people are interested can find their own, here is a website: http://stackoverflow.com/questions/677530/ How-can-i-programmatically-get-the-mac-address-of-an-iphone
3) IOS 7.0
Apple once again ruthlessly banned MAC addresses in IOS 7, and all of the MAC addresses that were acquired using the previous method became 02:00:00:00:00:00. There are problems in the total solution ah, so look around for information, and finally have the idea whether you can use Keychain to save the unique identifier obtained, so that even if the app is deleted and then loaded back, you can also read back from the keychain. After having the direction to do, look at the official documents about keychain, see the official use of Keychain demo, probably spent an afternoon time, the problem finally solved.
Ii. Introduction of Keychain
We engage in iOS development, must know OS X inside the keychain (keychain), usually to township and debugging, all have to install certificates and the like, these certificates are stored in keychain, and we usually browse the page records of the account password is also recorded in the keychain. Keychain in iOS is simpler than OS X, and the whole system has only one keychain, and each program can record data in keychain, and can only read the data recorded in keychain by its own program. The Security.framework framework in iOS provides four main ways to manipulate keychain:
Query Osstatus secitemcopymatching (cfdictionaryref query, Cftyperef *result);//Add Osstatus Secitemadd (CFDictionaryRef attributes, Cftyperef *result);//update Itemosstatus secitemupdate in keychain (cfdictionaryref query, Cfdictionaryref attributestoupdate)//delete Itemosstatus Secitemdelete in keychain (cfdictionaryref query)
These four method parameters are more complex, once the error will cause the Operation keychain failure, this piece of documentation described in more detail, you can check the official documents Keychain Services Reference.
As mentioned earlier that each app only allows access to the data it records in keychain, is there no other way to access the data that exists in other apps keychain?
Apple provides a way to allow multiple apps from the same sender to access each app, specifying Accessgroup, which is the Access group, when Secitemadd adds data. An app can belong to a colleague belonging to multiple groupings, and adding a Keychain data access group requires two things:
A. Set the code Signing entitlements inside the app Target's bulibsetting and point to the plist file that contains the grouping information for the Aceessgroup. The file must be in the same directory as the project file, I add access to the group because of the Plist file location problem, operation Keychain failure, it took a long time to find the problem.
b, create a new Keychainaccessgroups.plist file in the project directory, the topmost node in the structure of the file must be an array named "Keychain-access-groups", And each item in the array is a nsstring that describes the grouping. There are also requirements for the format of string: "appidentifier.com.***", where Appidentifier is the ID of your developer account.
C, in the code to add data to keychain, set Ksecattraccessgroup, the code is as follows:
NSString *accessgroup = [NSString stringwithutf8string: "APPIdentifier.com.cnblogs.smileEvday"]; if (accessgroup! = nil) {#if target_iphone_simulator //Ignore The Access group if running on the IPHONE SIMULATOR . //Apps that is built for the simulator aren ' t signed, so there ' s no Keychain Access group //For the Simulato R to check. This means, all apps can see all keychain items if run //on the simulator. //If a Secitem contains an Access group attribute, Secitemadd and secitemupdate on the //simulator would retur n-25243 (Errsecnoaccessforitem). #else [Dictforquery setobject:accessgroup forkey: (ID) ksecattraccessgroup];# endif }
This code is directly copied from the official demo, according to the note we can see that the simulator is not supported Accessgroup, so it is only a precompiled macro to add selectively.
Note: Appidentifer is the serial ID of the developer account, as shown in:
Open Xcode's organizer, select the Device tab, connect devices can see the device installed on the Developer Account description file list, where the fifth column of the first 10 characters is the app Identifier, this piece of writing is not very clear, many friends add me QQ asked me , and make up today.
Third, use Keychain to save and obtain Udid
Say so much finally get to the point, how to get the Udid on iOS 7. The second part of the knowledge we put into the direct application can easily achieve the effect we want, we first look at how to add the obtained Identifierforvendor to the code in the keychain.
+ (BOOL) Settudidtokeychain: (nsstring*) udid{nsmutabledictionary *dictforadd = [[Nsmutabledictionary alloc] init]; [Dictforadd SetValue: (ID) Ksecclassgenericpassword Forkey: (ID) ksecclass]; [Dictforadd setvalue:[nsstring Stringwithutf8string:kkeychainudiditemidentifier] forKey:kSecAttrDescription]; [Dictforadd setvalue:@ "UUID" Forkey: (ID) ksecattrgeneric]; Default attributes for keychain item. [Dictforadd setobject:@ "" Forkey: (ID) ksecattraccount]; [Dictforadd setobject:@ "" Forkey: (ID) Ksecattrlabel]; The Keychain Access group attribute determines if this item can be shared//amongst multiple apps whose code signin g entitlements contain the same Keychain Access group. NSString *accessgroup = [NSString Stringwithutf8string:kkeychainudidaccessgroup]; if (accessgroup! = nil) {#if target_iphone_simulator//Ignore The Access group if running on the IPHONE Simulat Or. Apps that is built for the simulator aren ' tSigned, so there's no Keychain Access group//for the simulator to check. This means, all apps can see all keychain items if run//on the simulator. If A Secitem contains an Access group attribute, Secitemadd and secitemupdate on the//simulator would return-25243 (Errsecnoaccessforitem). #else [Dictforadd setobject:accessgroup forkey: (ID) ksecattraccessgroup]; #end if} const char *udidstr = [Udid utf8string]; NSData *keychainitemvalue = [NSData datawithbytes:udidstr Length:strlen (UDIDSTR)]; [Dictforadd setvalue:keychainitemvalue Forkey: (ID) ksecvaluedata]; Osstatus writeerr = NOERR; if ([Svudidtools Getudidfromkeychain]) {//There is item in keychain [Svudidtools Updateudidinkeychain:udid ]; [Dictforadd release]; return YES; } else {//Add item to Keychain Writeerr = Secitemadd ((cfdictionaryref) Dictforadd, NULL); if (writeerr! = errsecsuccess) { NSLog (@ "ADD KeyChain Item Error!!! Error code:%ld ", Writeerr); [Dictforadd release]; return NO; } else {NSLog (@ "ADD KeyChain Item Success!!!"); [Dictforadd release]; return YES; }} [Dictforadd release]; return NO;}
In the above code, first build a dictionary to add to the data in the keychain, including some basic keychain item data type, description, Access grouping and the most important data and other information, Finally, the UUID we need to save is saved to keychain by calling the Secitemadd method.
The code for obtaining the corresponding data in keychain is as follows:
+ (nsstring*) getudidfromkeychain{nsmutabledictionary *dictforquery = [[Nsmutabledictionary alloc] init]; [Dictforquery SetValue: (ID) Ksecclassgenericpassword Forkey: (ID) ksecclass]; Set Attr Description for query [Dictforquery setvalue:[nsstring Stringwithutf8string:kkeychainudiditemidentifier] Forkey:ksecattrdescription]; Set Attr Identity for query NSData *keychainitemid = [NSData datawithbytes:kkeychainudiditemidentifier Length:strlen (Kkeychainudiditemidentifier)]; [Dictforquery setobject:keychainitemid Forkey: (ID) ksecattrgeneric]; The Keychain Access group attribute determines if this item can be shared//amongst multiple apps whose code signin g entitlements contain the same Keychain Access group. NSString *accessgroup = [NSString Stringwithutf8string:kkeychainudidaccessgroup]; if (accessgroup! = nil) {#if target_iphone_simulator//Ignore the Access Group IF running on the IPhone simulator. Apps that is built for the simulator aren ' t signed, so there ' s no Keychain Access group//For the SI Mulator to check. This means, all apps can see all keychain items if run//on the simulator. If A Secitem contains an Access group attribute, Secitemadd and secitemupdate on the//simulator would return-25243 (Errsecnoaccessforitem). #else [Dictforquery setobject:accessgroup forkey: (ID) ksecattraccessgroup]; #e NDIF} [Dictforquery SetValue: (ID) kcfbooleantrue Forkey: (ID) ksecmatchcaseinsensitive]; [Dictforquery SetValue: (ID) ksecmatchlimitone Forkey: (ID) ksecmatchlimit]; [Dictforquery SetValue: (ID) kcfbooleantrue Forkey: (ID) ksecreturndata]; Osstatus queryerr = NOERR; NSData *udidvalue = nil; NSString *udid = nil; Queryerr = secitemcopymatching ((cfdictionaryref) Dictforquery, (cftyperef*) &udidvalue); Nsmutabledictionary *dict = nil; [Dictforquery SetValue: (ID) kcfbooleantrue Forkey: (ID) ksecreturnattributes]; Queryerr = secitemcopymatching ((cfdictionaryref) Dictforquery, (cftyperef*) &dict); if (Queryerr = = Errsecitemnotfound) {NSLog (@ "KeyChain Item:%@ not Found!!!", [NSString STRINGWITHUTF8STRING:KKEYC Hainudiditemidentifier]); } else if (queryerr! = errsecsuccess) {NSLog (@ "KeyChain Item query Error!!! Error code:%ld ", Queryerr); } if (Queryerr = = errsecsuccess) {NSLog (@ "KeyChain Item:%@", udidvalue); if (udidvalue) {udid = [nsstring stringWithUTF8String:udidValue.bytes]; }} [Dictforquery release]; return udid;}
The process of the above code is similar, first create a dictionary, which sets the search criteria, and then through the Secitemcopymatching method to get to our previous saved to keychain data.
Iv. Summary
This article describes a workaround that allows the same udid information to be obtained after the app is deleted using keychain.
You may have questions about whether you can still get the UDID data that was previously recorded if the system is upgraded?
The answer is yes, I did the test specifically. Even if we remove the program, the system after upgrading and then installed back, still can get to the same udid. But when we restore the entire system to the previous record of the Udid, this point I think it should not, but the phone is too much data, no test, if you are interested in testing, to verify my guess.
Full code Address: Https://github.com/smileEvday/SvUDID
If you want to be in a real running, you need to replace two places:
The first place is the Appidentifier in Accessgroup in the plist file.
The second place is the appidentifier of the SVUDIDTOOLS.M in Kkeychainudidaccessgroup appidentity for the profile you are using.
If there is something wrong in the article and in the code, please correct me, thank you here first.
Original address: http://www.cnblogs.com/smileEvday/p/UDID.html
Note: If you think this article helps you, don't forget to recommend.
Reprint please famous source, have any questions welcome message
Welcome to discuss the knowledge of iOS development!!!
IOS7: How to get the unchanging Udid