For business reasons the project I'm working on has the requirement to determine which country's itunes store an app has been purchased from.
Knowing where the user is at the time of purchase (via location services etc) is not suitable and they would prefer to have 1 universal binary rather than having to have a separate binary for every store.
Is this something the application can determine at run-time?
Note: the specific answer is NO. You can NOT get the app store country. (As of mid-2010.)
For general readers, the following could be useful ... it is quite a nuisance to collect exactly these code three fragments together:
// to ("usually") get the preferred language from those we supplied in bundle
// [[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0]
// to ("often") get the preferred language regardless
// [ [NSBundle preferredLocalizationsFromArray:[NSLocale ISOCountryCodes]] objectAtIndex:0]
// to ("fairly reliably") get the user's chosen language setting...
// [ [NSLocale preferredLanguages] objectAtIndex:0]
Cheers
Have you tried hidden using in-app purchases to determine the store?
Hmm, could this be regarded as a violation of a customers right to privacy? I would imagine you have already looked at the reporting done back to you from the app store. If it's not in that data, I would imagine you won't be able to get it.
Related
I have an app submitted to the app store that was rejected due to:
2.30 Apps that do not comply with the Mac OS X File System documentation will be rejected
They claim my app is modifying the ~/Library/Preferences/com.apple.spaces.plist file which is unsupported.
My app is in fact modifying that file, but only with NSUserDefaults via: (I'm omitting some code for brevity...)
NSMutableDictionary *spacesDefaults =
[[NSUserDefaults standardUserDefaults] persistentDomainForName:#"com.apple.spaces"];
NSMutableDictionary *dict = [spacesDefaults objectForKey:#"app-bindings"];
NSString *bundleId = [[[NSBundle mainBundle] bundleIdentifier] lowercaseString];
[dict setObject:#"AllSpaces" forKey:bundleId];
[spacesDefaults setObject:dict forKey:#"app-bindings"];
[[NSUserDefaults standardUserDefaults] setPersistentDomain:spacesDefaults
forName:#"com.apple.spaces"];
It seems to me that this falls under the first bullet of "File-System Usage Requirements for the Mac App Store" http://developer.apple.com/library/mac/#releasenotes/General/SubmittingToMacAppStore/_index.html#//apple_ref/doc/uid/TP40010572
* You may use Apple frameworks such as User Defaults, Calendar Store, and Address Book that implicitly write to files in specific locations, including locations is not allowed to access directly.
Does anyone know why this would get rejected? I just don't see it...
Thanks!
This is a really important issue that I bemoaned on the Apple Dev Forums...
And I suppose that filing a Feedback Request might have an effect, someday... But apparently this LAME restriction.. which limits an App's ability to do ANYTHING outside the sandbox.. EVEN IF it's something your user can do... This is a serious step backwards in the ability of third-party apps on the mac to perform SIMPLE, implicit commands by the user.
This seems like a fundamental paradigm shift on Apple's part. Here's the jist of the above forum listing..
NSUserDefaults can be used only for our own app,
and sandboxing will not allow to modify other apps defaults
Additionally, you cannot modify in any way ANY other app's .plist, via XML, or otherwise.
And, no, doing defaults write via a task or terminal on another domain, besides YOUR OWN APP is outlawed as well. Ugh, it's very annoying.
Instead of using a web-service to get current application version number and comparing it in my code for popping up update alert, Is there any way to obtain the application version number from App-Store / i-Tunes directly?
After going through all the comments correct me if i am wrong.
We should not show a Local Notification(as alert) to User regarding availability of new update programatically?
I went though HIG Guidelines, but could not fing such creteria. So little confused in deciding.
Alert updates like this will certainly be against Apple guidelines. For iOS devices, application alerts are displayed by the App store app with a badge displaying the number of updates available. There is nothing a developer needs to do.
If you are worried about the user missing your app update, rest assured that iOS users keep an eye on the app-store app & know that all updates come through it.
However, there are hacky ways by which you can figure out that this is your first run after an update without contacting any web service or iTunes/App store.
One of the hacks known to me:
Fetch the library directory path-
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *libPath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
libPath would be something like - /var/mobile/Applications/8CFB747F-9446-44CB-98B9-C6FF1CF7BF93/Library
This random string before /Library changes with every update. You can save this string in NSUserDefaults, and compare the path with the saved string on every launch. If the strings are found to be different, it implies that this is your first run after the update. Display the update alert! Then update the saved string with the new one.
Yes. You could do an HTTP request to the itunes.apple.com server, while mimicking the User Agent string of iTunes, for your app's URL in the App store, and parse the returned HTML or XML you get back to find the version string, which may or may not be acceptable to Apple's approval team. But that's unlikely to be worth it, and it also presents an uncommon and unexpected user experience.
When setting values in NSUSerDefaults, may other apps that would know my used keys will be able to read my values ? I ask this because I can see that some values that are not mine are accessible, like NSArray* languages = [defaults objectForKey:#"AppleLanguages"];that I would had expected to find elsewhere.
I've just check it on my device - no, user defaults stored in one app are inaccessable from another app.
But as you said exist some system-defined values, that shared by system to all apps.
p.s. Otherwise it will conflict with one of the basic iOS paradigm - sandbox. One separate sandbox for every app.
My iPhone application needs to provide different features in some local markets, so I need to be able to determine where the devices "home" market is or from which store it was downloaded (if applicable).
I've tried almost all variations of NSLocale but I always seem to be able to change one or more of the settings.
I need to disable one tab if they are not in the US/CA for legal reasons, and I'd prefer to keep just the single binary.
Does anyone know how to do this?
Thanks
You could get the country code using
NSString *countryCode = [[NSLocale currentLocale] objectForKey: NSLocaleCountryCode];
But I'd imagine that data is quite easy to fake (it's dependent on the user's settings rather than where they actually live). It depends how secure your app needs to be - I would recommend uploading a separate binary for US/CA users if this is a major concern.
What are accepted methods to reduce iPhone application piracy, which do not violate Apple's evaluation process?
If my application "phones home" to provide the unique device ID on which it runs, what other information would I need to collect (e.g., the Apple ID used to purchase the application) to create a valid registration token that authorizes use of the application? Likewise, what code would I use to access that extra data?
What seem to be the best available technical approaches to this problem, at the present time?
(Please refrain from non-programming answers about how piracy is inevitable, etc. I know piracy is inevitable. I am interested in programming-based answers that discuss how to reduce it. Thanks in advance for your understanding.)
UPDATE
Please visit and read
Thanks to chpwn in the comments.
Code that's way too old! - 11th May 2009
For now there's an easier way to detect if your iPhone application has been cracked for piracy use. This does not involve you to check the iPhone unique IDs against a list of accepted IDs.
Currently there are three things crackers do:
Edit the Info.plist file
Decode the Info.plist from binary to
UTF-8 or ASCII
Add a key-pair to Info.plist{SignerIdentity,
Apple iPhone OS Application Signing}
The last one is easiest to check with this code:
NSBundle *bundle = [NSBundle mainBundle];
NSDictionary *info = [bundle infoDictionary];
if ([info objectForKey: #"SignerIdentity"] != nil)
{ /* do something */ }
Generally we don't have SignerIdentity in any of the App Store applications we build so checking for nil then performing set instructions should make it more difficult for crackers and pirates.
I can't take credit for this so please visit How to Thwart iPhone IPA Crackers. There's loads of information there about piracy on iPhone and how to curb it.
As pointed out by Andrey Tarantsov in the comments, looking for the "SignerIdentity" string in the binary (using an app like HexEdit) and replacing it is pretty easy.
You could encode that string, but then again all you have to do is change one char of it and the app is not going to look for the "SignerIdentity" key anymore but for some other key that probably doesn't exist (therefore is null). That key being null, the app thinks it isn't cracked (since SignerIdentity should be null if the app isn't cracked).
Instead, I'd rather check the size of the info.plist and compare it to a reference value. I noticed Simulator and Devices builds don't have the same info.plist file size. Same goes for Debug, Release and Distribution builds. Therefore, make sure you set the reference value using the info.plist file size for the Device Distribution Build.
How to look for the filesize at launch:
It looks like saving MD5 checksum of Plist and checking CryptID should do well till some time.
Check iTunesMetadata.plist for the date of Purchace as sometimes, when an app is cracked, that date is changed to something outrageous.
Also check to see if the purchacer name field exists. In my experience with cracking apps for personal use, that is usually removed. If anyone knows how the anti dump protection of Temple Run works, you could use that in conjunction with some protection that poedCrackMod can't get (google poedCrackMod create hackulo.us account, go to dev center look for poedCrackMod, install it on iDevice).
Clutch which will not crack things with Temple Run like protection, has a feature called OverDrive intended to silence an app's crack detection. poedCrackMod has LamestPatch, which isn't as good. Also poedCrackMod is an open source bash script that can be reverse engineered. To recap, you have an app that has copy protection that can't be circumvented with clutch / overdrive but can be cracked with poedCrackMod. However poedCrackMod can't circumvent the app's in app piracy checks. It is hard to manually patch integrety checks in the app's executable. So your app is hard to crack.