For an iPhone app that submits images to a server I need somehow to tie all the images from a particular phone together. With every submit I'd like to send some unique phone id. Looked at
[[UIDevice mainDevice] uniqueIdentifier]
and
[[NSUserDefaults standardDefaults] stringForKey:#"SBFormattedPhoneNumber"]
but getting errors in the simulator.
Is there an Apple sanctioned way of doing this?
What errors are you getting? [[UIDevice currentDevice] uniqueIdentifier] (edited to fix API, thanks Martin!) is the officially recommended way of doing this.
You can also use CFUUID to generate a UUID. Here's some code:
NSString *uuid = nil;
CFUUIDRef theUUID = CFUUIDCreate(kCFAllocatorDefault);
if (theUUID) {
uuid = NSMakeCollectable(CFUUIDCreateString(kCFAllocatorDefault, theUUID));
[uuid autorelease];
CFRelease(theUUID);
}
By far the easiest and most appropriate way to obtain a unique identifier is to use the mechanisms Apple explicitly provides for obtaining one - [[UIDevice currentDevice] uniqueIdentifier]. You can not guarantee that the phone number will be unique to the device or that the device will even have a phone number. Beyond that, doing so is a horrible idea as it is a definite invasion of the user's privacy. Even the uniqueidentifier should be hashed if you are going to store it in any way.
In order to Persist the Unique Identifier you create between installations, you could use the Keychain Made easy with SSKeychain: Simply set your UUID as follows:
[SSKeychain setPassword:#"Your UUID" forService:#"com.yourapp.yourcompany" account:#"user"];
and then call it again anytime you need it:
NSString *retrieveuuid = [SSKeychain passwordForService:#"com.yourapp.yourcompany" account:#"user"];
Note: The services and accounts must match exactly.
Then, if the App is deleted and reinstalled, the UUID will persist with reinstallation.
If you then want to share this UUID across devices, set up your app to use iCloud. You can then store the UUID in NSUserDefaults, sync with KeyValueStore, and then set the UUID in the new devices keychain with the code above.
This answer would get extremely long if I typed code for all the above, but plenty of sample code around here to figure it all out.
Don't forget that in iOS 5 uniqueIdentifier will be deprecated you should use CFUUID instead of that
Interestingly, Apple has since deprecated the uniqueIdentifier in iOS 5 (as gN0Me mentioned). Here's the relevant TechCrunch article:
http://techcrunch.com/2011/08/19/apple-ios-5-phasing-out-udid/
Apple suggests that you no longer uniquely identify the device but instead identify the user. In most cases, this is excellent advice though there are some situations which still require a globally unique device ID. These scenarios are quite common in advertising. Hence, I wrote an extremely simple drop-in library which replicates the existing behavior exactly.
In a shameless plug of self promotion, I'll link it here in the hope that someone finds it useful. Also, I welcome all and any feedback/criticism:
http://www.binpress.com/app/myid/591
Nevertheless, in your particular situation I would advise skipping the globally unique ID functionality my library provides as it's a bit overkill for your situation. Instead, I would generate a simple CFUUID and store it in NSUserDefaults. This ID would be specific to your application but would allow you to group all the photos for that "app install" in your database.
In other words, by deprecating the uniqueIdentifier method, Apple is suggesting that you don't identify per device but instead per app install. Unless you are operating under specific conditions, chances are the per app ID fits your product better anyway.
This is an interesting problem that I am also looking into solving. Here is a scenario that I would like to address.
What happens when you sell your phone to another person... that Device ID will then belong to somebody else, so even if the app is removed from the iPhone, it could be re-added and all that data would then be re-associated to a new user... this is bad.
Using the Phone number with the Device ID MD5 would be a great solution. Another we came up with is having a SQL Lite DB with some token Hashed with the Device ID. Then when the app is removed the DB is killed and all the data is disassociated. I think that might be too brittle.
Any other ideas?
Rob Ellis (PhoneGap/Nitobi)
Use Apple's GenericKeyChain which is the best solution . Here is the working sample >>https://developer.apple.com/library/prerelease/ios/samplecode/GenericKeychain/Introduction/Intro.html
Have idea about KeyChainAccess >>https://developer.apple.com/library/mac/documentation/Security/Conceptual/keychainServConcepts/02concepts/concepts.html#//apple_ref/doc/uid/TP30000897-CH204-TP9
Haven't done iphone work, but how about taking a hash of something unique to the phone ... oh, say the phone number?
Getting iphone number
snippit:
NSString *phoneNumber = (NSString *) [[NSUserDefaults standardUserDefaults] objectForKey:#"SBFormattedPhoneNumber"]; // Will return null in simulator!
NSLog(#"Formatted phone number [%#]", phoneNumber);
I [recently] ran this code as-is on OS 2.2.1 [and OS 3.0].
It works as expected when run on the device, and returns my phone number with the full international dialing codes [ 1 in my case].
When run on the simulator, the value [returned] is a null string, so it only works on an actual iPhone device.
I did not test it on an iPod Touch.
...
Ran this code on a different device this week, and got a null value instead of the number.
On further research, it appears that the number returned by this code snippit is the number that is set up in iTunes for the device.
If you didn’t enter the iPhone’s number in iTunes at device activation, or perhaps (as in my case) if the default value wasn’t the iPhone’s number and you clicked OK anyway, such that iTunes doesn’t list the phone number when your iPhone is plugged in, this code will return a null string.
[Above is an edited concatenation of comments I recently posted to another article on this topic at http://www.alexcurylo.com/blog/2008/11/15/snippet-phone-number/]
Here is some more information on a way to get it from iTunes which may be useful for testing purposes.
I had success with such code:
- (NSString *)stringUniqueID {
NSString * result;
CFUUIDRef uuid;
CFStringRef uuidStr;
uuid = CFUUIDCreate(NULL);
assert(uuid != NULL);
uuidStr = CFUUIDCreateString(NULL, uuid);
assert(uuidStr != NULL);
result = [NSString stringWithFormat:#"%#", uuidStr];
assert(result != nil);
NSLog(#"UNIQUE ID %#", result);
CFRelease(uuidStr);
CFRelease(uuid);
return result;
}
You can use MAC address as a unique id. Following link will help you
How can I programmatically get the MAC address of an iphone
Related
What is the best way to uniquely register an iOS Device, which won't be limited by future Apple restrictions?
My current approach to register an iOS device (basically to identify the device uniquely) is that I use the UDID of an iOS device to identify it and register it, and then after recognising it I perform the necessary actions.
The issue is that the UIDevice uniqueIdentifier property is deprecated. There are certain workarounds for that (as discussed in this question) which I'm aware of.
One possibility is to use the MAC address of an iOS device. However, I feel that Apple may restrict access to this information at some point in the future, as well.
Is there any other way (besides accessing the MAC address) to identify an iOS device, which we can rely on for the future?
Using Apple's preferred method of generating a CFUUIDRef is probably the better solution as I feel the MAC address may be removed in the future as well. If you use this and save it to your NSUserdefaults you will have it persist unless the user deletes the app. If you want to have a generated unique ID that you can share between apps or persist between installs you should look at using the UIPasteboard and saving your generated UID with a key that you share between apps.
//Create a unique id as a string
CFUUIDRef theUUID = CFUUIDCreate(NULL);
CFStringRef string = CFUUIDCreateString(NULL, theUUID);
//create a new pasteboard with a unique identifier
UIPasteboard *pasteboard = [UIPasteboard pasteboardWithName:#"youruniquestring" create:YES];
[pasteboard setPersistent:YES];
//save the unique identifier string that we created earlier
[pasteboard setString:((__bridge NSString*)string)];
//accessing it
UIPasteboard *pasteboard = [UIPasteboard pasteboardWithName:#"youruniquestring" create:NO];
NSLog([pasteboard string]);
I have written a brief tutorial here but its basically the lines above: http://www.roostersoftstudios.com/2012/03/26/a-way-to-share-data-between-apps-on-a-given-ios-device/
Unless your application is for managing my Apple devices, it is the wrong approach. As a user, I don't want you to know which device I'm using. I want you to recognize me, whatever the device I use. I want to be able to replace a defective device. Apple will restrict more and more the access to this information.
[edit] I can't see how a MAC address could work. My iOS devices can have multiple.
Another option is dynamically generating your own UUID.
CFUUIDRef uuid = CFUUIDCreate(NULL);
CFStringRef generatedUuidStr = CFUUIDCreateString(NULL, uuid);
CFRelease(uuid);
NSString* uuidStr = [(NSString*)generatedUuidStr autorelease];
You could persist this UUID in NSUserDefaults for install based uniqueness. If device based uniqueness is truly the most important thing (so that after an uninstall and reinstall the id persists) you'll need a mechanism to persist the ID on the device. I think you could look into using the Keychain in order to persist that ID which should persist beyond app uninstall. You can even use an access group when adding the UUID to the keychain so that you could have a suite of apps that use the same UUID.
See apple's security framework reference for more info on saving keychain items and retrieving them.
http://developer.apple.com/library/ios/#DOCUMENTATION/Security/Reference/SecurityFrameworkReference/_index.html
The best approach I've seen is using the keychain. You can generate an UUID with the CFUUIDCreateString function and store it in the keychain. This data stays in the device between installs and restores from backups.
Use the devices Mac address. It's as unique as you could need I think.
How can I programmatically get the MAC address of an iphone
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
UIDevice uniqueIdentifier Deprecated - What To Do Now?
As I expect you are aware of, the uniqueIdentifier in UIDevice is deprecated in iOS5. What I am looking for a basically the same functionality for iOS5.
What I understand from the documentation is that apple wishes that we (developers) create our own UUID (using CFUUIDCreate I guess) and store it in the NSUserDefaults. This, however, makes me shiver a bit and does not at all feel save. Feels a bit pointless to have a UUID in this case.
The reason I need an UUID is because I send of a bunch information including UUID to my servers in the auth process and would like to be able to skip some steps if the server can "guess" whom the user is next time the app gets launched or re-installed or another app implements my library. CFUUIDCreate does not seem to help me with this.
I took a quick look at gekitz as well, but as I understand it, it bases it solely on the MAC address of the Ethernet "card" in the phone. This is not suitable since I have a vague feeling that the MAC address is changeable. The uniqueIdentifier in UIDevice was
An alphanumeric string unique to each device based on various hardware details.
Currenly I wrote this code, which retrieves a UUID. But as soon as I clean in XCode and re-install the app on the phone, I get a new UUID.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *UUID = #"";
if (![defaults valueForKey:#"UUID"])
{
CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault);
CFStringRef UUIDSRef = CFUUIDCreateString(kCFAllocatorDefault, UUIDRef);
UUID = [NSString stringWithFormat:#"%#", UUIDSRef];
[defaults setObject:UUID forKey:#"UUID"];
}
else {
UUID = [defaults valueForKey:#"UUID"];
}
[deviceInformation setObject:UUID forKey:#"UUID"];
To sum it up, my question is the following:
Is there some solid way of creating an UUID that is based upon the device which is "hard" to tamper with and gives me as receiver a little something to depend and trust upon? This does not have to be based on the device, may be based on the app as a App UUID as long as its the same after a re-installation.
So far, the MAC seems to be the only known "stable" way to identify a device.
Have a look at Erica Sadun's UIDevice(Hardware) category, you'll notice that the only useful thing for identification is the MAC.
She also has a UIDevice(IOKit_Extensions) category which does provide IMEI and serial number. However, IOKit is private API. Erica wrote:
As iPhone evangelist Matt Drance tweeted, "IOKit is not public on iPhone. Lack of headers and docs is rarely an oversight."
So using IOKit might get you rejected.
As far as I know there is no way for a user to change the MAC without jailbreaking the device (and then he can do anything he wants anyway). So my suggestion is to ignore the jailbreakers and simply use a UUID based on the MAC.
Warning! MAC address APIs will not work in iOS 7.
I am working on a client iPhone app which allows users to rate various services. There is no registration or login.
The requirement is that a user can not repeatedly rate a service(although can change their rating). As things currently stand the app could be deleted, re-installed and the user could vote again.
We considered using the device id, however a colleague mentioned that Apple recommend against this. If I understand correctly in case a phone was returned to store, re-issued, and the new user then downloaded the same app. Seems like a pretty edge case to me, but I guess could happen within an enterprise.
Is there a smart way to restrict voting to a particular device? Perhaps using the keychain?
Any pointers greatly appreciated.
Its important to note the difference between a UDID and a UUID.
UDID "unique device id" is hardware specific. It never changes for a particular device. For this reason, it has become a privacy concern and Apple is blocking apps that try to use this. As a result, Apple has generated an opt-out-able "device id" hash, particularly for advertisement usage. This new ID hash is called IFA and is available in iOS 6.0+.
UUID "universally unique id" is not hardware specific. It is a hash used to identify a device; but not particularly an absolute value. For example, PhoneGap generates a UUID based on device properties; this is what you get when you do device.uuid. If you delete the app and reinstall, you will get a new id hash. UUID is not being blocked by Apple.
I think the best solution in your case would be to use the IFA, with OpenUDID as a backup for iOS < 6.0.
Here is the code we use. If IFA is not available, get OpenUDID. You must install OpenUDID, read more about that here, https://github.com/ylechelle/OpenUDID.
NSString* uuid = nil;
if ([[UIDevice currentDevice] respondsToSelector:#selector(identifierForVendor)]) {
// IOS 6 new Unique Identifier implementation, IFA
uuid = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
} else {
// Before iOS6 (or if IFA disabled) you shoud use a custom implementation for uuid
// Here I use OpenUDID (you have to import it into your project)
// https://github.com/ylechelle/OpenUDID
NSString* openUDID = [OpenUDID value];
uuid = [OpenUDID value];
}
Is there any way to get own phone number by standard APIs from iPhone SDK?
At the risk of getting negative marks, I want to suggest that the highest ranking solution (currently the first response) violates the latest SDK Agreement as of Nov 5, 2009. Our application was just rejected for using it. Here's the response from Apple:
"For security reasons, iPhone OS restricts an application (including its preferences and data) to a unique location in the file system. This restriction is part of the security feature known as the application's "sandbox." The sandbox is a set of fine-grained controls limiting an application's access to files, preferences, network resources, hardware, and so on."
The device's phone number is not available within your application's container. You will need to revise your application to read only within your directory container and resubmit your binary to iTunes Connect in order for your application to be reconsidered for the App Store.
This was a real disappointment since we wanted to spare the user having to enter their own phone number.
No, there's no legal and reliable way to do this.
If you find a way, it will be disabled in the future, as it has happened with every method before.
Update: capability appears to have been removed by Apple on or around iOS 4
Just to expand on an earlier answer, something like this does it for me:
NSString *num = [[NSUserDefaults standardUserDefaults] stringForKey:#"SBFormattedPhoneNumber"];
Note: This retrieves the "Phone number" that was entered during the iPhone's iTunes activation and can be null or an incorrect value. It's NOT read from the SIM card.
At least that does in 2.1. There are a couple of other interesting keys in NSUserDefaults that may also not last. (This is in my app which uses a UIWebView)
WebKitJavaScriptCanOpenWindowsAutomatically
NSInterfaceStyle
TVOutStatus
WebKitDeveloperExtrasEnabledPreferenceKey
and so on.
Not sure what, if anything, the others do.
Using Private API you can get user phone number on the following way:
extern NSString* CTSettingCopyMyPhoneNumber();
+(NSString *) phoneNumber {
NSString *phone = CTSettingCopyMyPhoneNumber();
return phone;
}
Also include CoreTelephony.framework to your project.
You cannot use iOS APIs alone to capture the phone number (even in a private app with private APIs), as all known methods of doing this have been patched and blocked as of iOS 11. Even if a new exploit is found, Apple has made clear that they will reject any apps from the app store for using private APIs to do this. See #Dylan's answer for details.
However, there is a legal way to capture the phone number without any user data entry. This is similar to what Snapchat does, but easier, as it does not require the user to type in their own phone number.
The idea is to have the app programmatically send a SMS message to a server with the app’s unique installation code. The app can then query the same server to see if it has recently received a SMS message from a device with this unique app installation code. If it has, it can read the phone number that sent it. Here’s a demo video showing the process. As you can see, it works like a charm!
This is not super easy to set up, but it be configured in a few hours at no charge on a free AWS tier with the sample code provided in the tutorial here.
As you probably all ready know if you use the following line of code, your app will be rejected by Apple
NSString *num = [[NSUserDefaults standardUserDefaults] stringForKey:#"SBFormattedPhoneNumber"];
here is a reference
http://ayeapi.blogspot.com/2009/12/sbformatphonenumber-is-lie.html
you can use the following information instead
NSString *phoneName = [[UIDevice currentDevice] name];
NSString *phoneUniqueIdentifier = [[UIDevice currentDevice] uniqueIdentifier];
and so on
#property(nonatomic,readonly,retain) NSString *name; // e.g. "My iPhone"
#property(nonatomic,readonly,retain) NSString *model; // e.g. #"iPhone", #"iPod Touch"
#property(nonatomic,readonly,retain) NSString *localizedModel; // localized version of model
#property(nonatomic,readonly,retain) NSString *systemName; // e.g. #"iPhone OS"
#property(nonatomic,readonly,retain) NSString *systemVersion; // e.g. #"2.0"
#property(nonatomic,readonly) UIDeviceOrientation orientation; // return current device orientation
#property(nonatomic,readonly,retain) NSString *uniqueIdentifier; // a string unique to each device based on various hardware info.
Hope this helps!
To get you phone number you can read a plist file. It will not work on non-jailbroken iDevices:
NSString *commcenter = #"/private/var/wireless/Library/Preferences/com.apple.commcenter.plist";
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:commcenter];
NSString *PhoneNumber = [dict valueForKey:#"PhoneNumber"];
NSLog([NSString stringWithFormat:#"Phone number: %#",PhoneNumber]);
I don't know if Apple allow this but it works on iPhones.
No official API to do it. Using private API you can use following method:
-(NSString*) getMyNumber {
NSLog(#"Open CoreTelephony");
void *lib = dlopen("/Symbols/System/Library/Framework/CoreTelephony.framework/CoreTelephony",RTLD_LAZY);
NSLog(#"Get CTSettingCopyMyPhoneNumber from CoreTelephony");
NSString* (*pCTSettingCopyMyPhoneNumber)() = dlsym(lib, "CTSettingCopyMyPhoneNumber");
NSLog(#"Get CTSettingCopyMyPhoneNumber from CoreTelephony");
if (pCTSettingCopyMyPhoneNumber == nil) {
NSLog(#"pCTSettingCopyMyPhoneNumber is nil");
return nil;
}
NSString* ownPhoneNumber = pCTSettingCopyMyPhoneNumber();
dlclose(lib);
return ownPhoneNumber;
}
It works on iOS 6 without JB and special signing.
As mentioned creker on iOS 7 with JB you need to use entitlements to make it working.
How to do it with entitlements you can find here:
iOS 7: How to get own number via private API?
AppStore will reject it, as it's reaching outside of application container.
Apps should be self-contained in their bundles, and may not read or write data outside the designated container area
Section 2.5.2 :
https://developer.apple.com/app-store/review/guidelines/#software-requirements
My app has a feature which requires identifying each app users. I'm planning making the app sends UDID to my server. Server stores it, for later use.
I don't think that's a personal information, however, I want to know is it approvable or not in Apple's AppStore.
And, including transferring phone numbers. In the case of WhatsApp, it recognizes my friends' numbers automatically. I think that's impossible without some kind of data transfer.
You are allowed to transfer a device's UDID to your servers. That's what it's intended for.
Access to the UDID is grounds for rejection from the App Store. See an alternative to using the UDID at https://stackoverflow.com/a/10037636/1286639
Just make sure that it is no secret what your app does. If it transfers phone numbers to store those on your server then clearly mention this in the app's description or even ask the user for permission the first time.
That actually is a a reason to be rejected I think: not being clear about storing user's data on your own server/service.
Its important to note the difference between a UDID and a UUID.
UDID "unique device id" is hardware specific. It never changes for a particular device. For this reason, it has become a privacy concern and Apple is blocking apps that try to use this. As a result, Apple has generated an opt-out-able "device id" hash, particularly for advertisement usage. This new ID hash is called IFA and is available in iOS 6.0+.
UUID "universally unique id" is not hardware specific. It is a hash used to identify a device; but not particularly an absolute value. For example, PhoneGap generates a UUID based on device properties; this is what you get when you do device.uuid. If you delete the app and reinstall, you will get a new id hash. UUID is not being blocked by Apple.
I think the best solution in your case would be to use the IFA, with OpenUDID as a backup for iOS < 6.0.
Here is the code we use. If IFA is not available, get OpenUDID. [[You must install OpenUDID, read more about that here, https://github.com/ylechelle/OpenUDID.]]
NSString* uuid = nil;
if ([[UIDevice currentDevice] respondsToSelector:#selector(identifierForVendor)]) {
// IOS 6 new Unique Identifier implementation, IFA
uuid = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
} else {
// Before iOS6 (or if IFA disabled) you shoud use a custom implementation for uuid
// Here I use OpenUDID (you have to import it into your project)
// https://github.com/ylechelle/OpenUDID
NSString* openUDID = [OpenUDID value];
uuid = [OpenUDID value];
}