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.
Related
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.
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.
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.
Don't worry, I'm not trying to hack someone else's app, if that's what you're thinking =).
I want to have 2 versions of my app, a free version and a deluxe version. My plan was to use an in-app purchase to enable the deluxe version by setting a boolean value in the plist file.
My question is: is this secure or is it easily circumvented? And if it is not secure, can someone suggest a simple alternative? I don't want to download additional content, I would rather keep all of the functionality within the app and enable it somehow.
Edit: I don't mean the application plist file, but something like the user defaults file.
You should store this in the keychain, this is what I'll do. The keychain is far more secure than a .plist or the user defaults (which are .plists, too, as far as I know). Have a look at SFHFKeychainUtils, you should be able to use this or just implement a better method exactly for the need to save a simple bool.
It is easy to edit the com.something.plist without jailbreaking. With a free tool* you can browse your device, you can also edit and save these files. If you store your inapp purchase something like this:
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"com.example.pack1"];
[[NSUserDefaults standardUserDefaults] synchronize];
then this will be written to the plist:
<key>com.example.pack1</key>
<true/>
If you name your packages like this: pack1, pack2 etc., and somebody edits your plist (copy/pasting the first key), he/she could use the locked feature easily.
A not too hard to implement method would be to save like this:
[[NSUserDefaults standardUserDefaults] setValue:[self sha1ValueForKey:#"com.example.pack1"]
forKey:#"com.example.pack1"];
[[NSUserDefaults standardUserDefaults] synchronize];
where -sha1ValueForKey: is
-(NSString *)sha1ValueForKey:(NSString *)key {
return [self sha1:[NSString stringWithFormat:#"<SALT>%#", key]];
}
You have to change <SALT> to something.
You can find -sha1: here: http://www.makebetterthings.com/iphone/how-to-get-md5-and-sha1-in-objective-c-ios-sdk/
After this you can verify if the key matches the hashed value.
If somebody wants to hack your plist he/she has to know your hashing mechanism and salt.
This is not the safest way to protect your application but it is easy to implement.
*iExplorer
EDIT:
The suggested method only protects - somewhat - your IAP if the user doesn't have access to the hashed value. If someone gets it from somewhere, it is easy to copy that data to the plist. If the SALT is device dependent copying is useless.
I would recommend reading up on verifying in-app purchases. It sounds to me like you are trying to roll your own in-app purchase verification system which may be wrought with issues you might not have thought of yet. You have to be careful with your user's purchases that they will behave the same in your application as they will in any other, lest ye lose their trust (and future sales!)
Instead of worrying about the Info.plist file, why not just set a preference? Somewhere in your code, this would give you your boolean value:
[[NSUserDefaults standardUserDefaults] boolForKey:#"someKey"];
If the value doesn't exist, the result will be nil. This code sets the value:
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"someKey"];
Plus, these values will be backed up in iTunes, so if the user moves their backup to a new iPhone or simply restores from backup, the values will be restored.
I don't have an answer, but it seems that editing your plist file dynamically is not possible, if I trust this subject :
You can not edit you're info.plist
file dynamically. When you submit your
app to The App Store, your app bundle,
which includes info.plist, can't
change because the signature created
when you compile you app is based on
the bundle.
Any pirate has a jail-broken iPhone
Any jail-broken device offers full file system access via tools like PhoneDisk, etc
Any file system access allows people to change the values in your applications .plist file
Game over.
Now, its not trivial to wrapper that up for the script kiddies but then again its not that hard either.
Storing state in defaults is no more nor less safe from privacy than having two versions of your app. Pirates will either pirate the deluxe version, or they'll pirate the unified version with the flag set.
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.