UILocalNotifications playing Custom sound - iphone

I implemented local notification in my app but I am just wondering is there a way to play a sound that is not part of the main bundle of iPhone App.
Basically in my app, I want user to record a sound that gets played when the local notification is generated instead of playing a pre-recorded or default sound.
As far as i know this can be implementable because i have seen 2-3 App in app store which is doing the same thing which i want to do
- (void)alertSelector:(NSString *)AlertTitle WithFiringTime:(NSDate *)date
{
UILocalNotification *localNotification = [[[UILocalNotification alloc] init] autorelease];
[localNotification setFireDate:date];
[localNotification setTimeZone:[NSTimeZone defaultTimeZone]];
NSDictionary *data = [NSDictionary dictionaryWithObject:date forKey:#"payload"];
[localNotification setUserInfo:data];[localNotification setAlertBody:AlertTitle];
[localNotification setAlertAction:#"View"]; [localNotification setHasAction:YES];
localNotification.soundName=#"voice.aif";
if (!localNotification)
return;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}

My guess is your sound isn't formatted appropriately to play by the OS. Make sure your sound is formatted in IMA4 format. Also, make sure your file is in your application bundle, and you should be good to go.
For more reference and similar question, see this SO question.
Choose custom sound for Local Notifications
Here is a link for Apple's Local and Push Notification Guide. It explains the sounds in detail and the default sounds that can be used.

From the description of the soundName property -
For this property, specify the filename (including extension) of a
sound resource in the application’s main bundle or
UILocalNotificationDefaultSoundName to request the default system
sound.
It doesn't seem that sounds other than those present in the bundle can be used. Are you sure that the apps are using local notifications with their custom sounds? Maybe they are playing sounds with alerts or something like that.
HTH,
Akshay

notif.soundName = #"sound.caf";
That should work. Make sure the sound is actually in your app’s bundle, is in the correct format (linear PCM or IMA4—pretty much anywhere that explains how to convert sounds for iOS will tell you how to do that), and is under 30 seconds.
You can convert from wav and mp3 using terminal :
afconvert -f caff -d LEI16#44100 -c 1 in.wav sound.caf
'in.wav' is input sound which u want to convert and 'sound.caf' is output file ,change directory to where u want to have the out put file

Make sure your sound file is added to the "Copy Bundle Resources" list under Project Settings->(Target)->Build Phases.
This fixed the issue for me. At first I only got the default sound, even though I specified a custom file name (and the file was added to the project). But after I manually added the file to this list it started working.
I used a system sound from my mac to test with. I converted it using this command in a console:
afconvert /System/Library/Sounds/Basso.aiff ~/Downloads/alert2.caf -d ima4 -f caff -v

Well I was experiencing same Issue , It sounds like this was not supported before or not doable to play custom sound for local notification that is not included in App Bundle. But later this is supported and you can assign custom sounds from App container specifically in Library/Sounds directory . And it worked with me . you can either download sounds from server to this directory or test it by copying a .caf(Apple said other extensions may work) file from bundle to Library/sounds directory and then remove reference for the file from bundle so that you can make sure that if sounds played it is from directory out of bundle . This all works very well , the main issue with me if user force quoted the app it stops working for local notification while it still works with push kit notifications , Im scheduling local one though when push kit push arrives.
Here is the code for testing playing sound from directory :
func copyFileToDirectoryFromBundle()
{
// get reference to library directory
let LibraryDirectoryPaths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.LibraryDirectory, NSSearchPathDomainMask.UserDomainMask, true)
// see what is in there
for path in LibraryDirectoryPaths
{
print("di=\(path)")
}
// prepare for sound directory
let libraryDirectoryPath: AnyObject = LibraryDirectoryPaths[0]
let soundsDirectoryPath = libraryDirectoryPath.stringByAppendingPathComponent("Sounds")
// create sounds directory under library if not there already
if !NSFileManager.defaultManager().fileExistsAtPath(soundsDirectoryPath)
{
do {
try NSFileManager.defaultManager().createDirectoryAtPath(soundsDirectoryPath, withIntermediateDirectories: false, attributes: nil)
print("sounds library created,,,")
} catch let error as NSError {
print("creating sounds directory error = \(error.localizedDescription)");
}
}
else
{
print("Sounds directory is there already under Library")
}
do {
// sounds directory should have been added under library
let directoryContents = try NSFileManager.defaultManager().contentsOfDirectoryAtURL( NSURL(string: libraryDirectoryPath as! String)!, includingPropertiesForKeys: nil, options: [])
print("Library directs=\(directoryContents)")
// prepare for adding subdirectory with file name from bundle
// here is where you should save files if downloaded from server
// here im just moving one from bundle
let subDirectoryPath = directoryContents.last!
if let pathFromBundle = NSBundle.mainBundle().pathForResource("notification", ofType:"caf") {
let myFilePathUnderSounds = "\(subDirectoryPath.path!)\("/notification.caf")"
if !NSFileManager.defaultManager().fileExistsAtPath(myFilePathUnderSounds)
{
do {
print("bundle path=\(pathFromBundle)and datacontainer path=\(subDirectoryPath.path!)")
try NSFileManager.defaultManager().copyItemAtPath(pathFromBundle, toPath:myFilePathUnderSounds )
print("item copied")
} catch let error as NSError {
print(error.localizedDescription);
}
}
else
{
print("Your file from bundle path = \(pathFromBundle) is already there under Sounds")
}
}
else
{
print("Item not found in bundle")
}
// see item there after you add it
let subDirectories = try NSFileManager.defaultManager().contentsOfDirectoryAtURL( subDirectoryPath, includingPropertiesForKeys: nil, options: [])
print("files under sounds")
for fileDirectory in subDirectories
{
print(fileDirectory)
}
} catch let error as NSError {
print(error.localizedDescription)
}
}
After making sure .caf file is under Library/Sounds directory . All you have to do is merely assigning file name with extension to local notification soundName property like if the file is in bundle.

are you cleaning the old notifications with?
UIApplication* app = [UIApplication sharedApplication];
NSArray* oldNotifications = [app scheduledLocalNotifications];
if ([oldNotifications count] > 0) {
[app cancelAllLocalNotifications];
}
else you can, delete the app from the phone (maybe need a reboot too)
the it should work :-)
beside that the code mentioned above should be correct.
and this line localNotification.soundName=#"voice.aif"; should be enough to play custom sounds.
but if you had notification tested first with no sound-file added, the default sound file (UILocalNotificationDefaultSoundName) is registered! and to re-register you have to cancel the previous notification (cancelAllLocalNotifications) or delete the app, to be able to "re"-register your notfication,
beside that, the apple doc says:
Sounds that last longer than 30 seconds are not supported. If you
specify a file with a sound that plays over 30 seconds, the default
sound is played instead.
hope it helps now :-)

Related

iOS5 Saving images to custom folder, ALAssetsLibrary fail

<><> ----
Aalok has answered this question, I'm just waiting for him to write it up so I can chose that as the correct answer. Until then, along with making the changes he describes below I had to call -- self.library = [[ALAssetsLibrary alloc] init]; -- before every attempt to save my image. As a precaution I also stopped the AVSession running until after the save, when is was restarted (using [session stopRunning]; and [session startRunning];
----- <><>
<><> ----- EDIT 2: You do not need to stop and restart the AV session, tested this thoroughly and it's working perfectly. ----- <><>
<><> ----- EDIT 3: after testing this on my device thoroughly, and it working perfectly, the code is not working once it's been through the review process and put on the store. Two identical devices (2x iPhone 4) running the same OS, one using my dev build, one off the app sotre, the ap store version still has the bug. Giving up with this for now ----- <><>
I'm using the category in this link to try to save to a custom folder:
http://www.touch-code-magazine.com/ios5-saving-photos-in-custom-photo-album-category-for-download/
Now it works some of the time, but not all. In the comments it's been suggested that the following code will detect if the group properties are nil:
if ([group valueForProperty:ALAssetsGroupPropertyURL] == nil)
{
NSLog(#”group properties are nil!”);
} else {
[group addAsset:asset];
}
Which I have, and it does detect if the properties are nil. So all good. What I'm struggling with is at that point setting the properties and saving the images. I'm guessing that at this point we can manually set the albumName and save the image, which I've tried, but the error still occurs.
Any ideas? Thank you.
I have facing the same problem with this same .h and .m file but find one solution for this after working on it for 2 to 3 days and the solution is very simple what I have to change is in .m file in
-(void)saveImage:(UIImage*)image toAlbum:(NSString*)albumName withCompletionBlock:(SaveImageCompletion)completionBlock
and in
-(void)addAssetURL:(NSURL*)assetURL toAlbum:(NSString*)albumName withCompletionBlock:(SaveImageCompletion)completionBlock
methods i just add this before calling the inside methods
//THE CHANGE dispatch_async(dispatch_get_main_queue(),^{ //
[self writeImageToSavedPhotosAlbum:image.CGImage orientation:(ALAssetOrientation)image.imageOrientation completionBlock:^(NSURL* assetURL, NSError* error)
{
//error handling
if (error!=nil)
{
completionBlock(error);
return;
}
//add the asset to the custom photo album
[self addAssetURL: assetURL toAlbum:albumName withCompletionBlock:completionBlock];
}];
//THE CHANGE }); //
AND Same for the other one
Happy Coding :)
EDIT
In second method I add a line below every completionBlock(nil); line
[[NSNotificationCenter defaultCenter] postNotificationName:kSaveSuccess object:nil];
And I use this notification to make sure that image is saved in album too. Till that time I Shows UIActivityIndicator with some text message and after successfully saved image one popup message is shown indicated that image is saved in album with album name. And while this time the UI is unresponsive i.e. user can't do any thing other then pressing home button of device :) ;)
Happy Coding :)

iCloud only seems to work on start up

I'm trying to create an iCloud app that uses Core Data. To do so, I use a single instance of UIManagedDocument. For persistentStoreOptions, I set "Database.data" for NSPersistentStoreUbiquitousContentNameKey and -iCloudURL-/CoreData_Logs for NSPersistentStoreUbiquitousContentURLKey. As by Apple suggested, I open the document like so:
if ([[NSFileManager defaultManager] fileExistsAtPath:[urlOfDatabase path]])
{
[_database openWithCompletionHandler:^(BOOL success) { ... }];
}
else
{
[_database saveToURL:urlOfDatabase
forSaveOperation:UIDocumentSaveForCreating
completionHandler:^(BOOL success) { ... }];
}
Also, I registered for NSPersistentStoreDidImportUbiquitousContentChangesNotifications, and merge the context from the notification in that method.
To test the app, I use an iPad and an iPhone. When I run it on one device, add some data, and open it up on the other device, the correct data is displayed. If I however add data while the app is running on both devices, it either doesn't update (I've waited for at least 10 minutes once) or it updates after a few minutes but only with a whole bunch of error messages: "The item failed to download.".
Did I miss something (hopefully obvious) in my set up?

Display encrypted file using QuickLook framework or UiDocumentInteractionController

I have an encrypted word/excel/pdf file locally stored which I need to preview in my iPad app. I understand that QLPreviewController or UiDocumentInteractionController could be used to preview these files. I can very well use this
- (id <QLPreviewItem>) previewController: (QLPreviewController *) controller previewItemAtIndex: (NSInteger) index {
return [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:[documents objectAtIndex:index] ofType:nil]];
}
But the file is encrypted and when I decrypt it I would get hold of NSData object. How do I go about loading NSData in either of these.
Also I understand that I can very well store the NSData back as a local file and load it in Preview. But there is a constraint of not storing the unencrypted file locally.
If someone has already accomplished this and can help me out here it will be greatly appreciated.
Thanks
AJ
Since you are using Quick Look, your options are limited. You must give Quick Look an NSURL, which means it must be on the file system (or the Internet). Fortunately, this shouldn't be much of a problem. iOS devices use hardware-level encryption. When your file is encrypted, only your app has the key to decrypt it. So, your file will still be encrypted, but it will also be readable by your app and only your app.
Here's what you do:
Decrypt your file into an NSData object, which you've already done.
Write the file to a location that will not get uploaded to iCloud nor backed up by iTunes. The tmp directory is probably the best choice. The code looks something like this:
NSData * data = // Your decrypted file data.
NSString * fileName = // Whatever you want to name your file.
NSString * path = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
NSURL * url = [NSURL URLWithString:path];
NSError * error = nil;
BOOL success = [data writeToURL:url
options:NSDataWritingFileProtectionComplete
error:&error];
if (success) {
// Give the URL to Quick Look.
}
else {
// An error happened. See the 'error' object for the details.
}
At this point you have an NSURL which you can use with Quick Look. Don't forget to delete the decrypted file when you are done with it.
There are a few things to note about on-disk encryption:
It is only supported on iOS 4.0+.
It may not work on "older" devices.
The user must have an active passcode.
If you use NSDataWritingFileProtectionComplete, the file is not accessible while the device is locked. If you need to access the file while the app is locked, then you should use NSDataWritingFileProtectionCompleteUnlessOpen or NSFileProtectionCompleteUntilFirstUserAuthentication instead. This will still give you great protection, even if the device is stolen and jailbroken. Be aware, though, that these encryption options are only available on iOS 5.0+
For more details for on-disk encryption, check out the iOS App Programming Guide
After doing some digging, I found out that QLPreviewController is using UIWebView underneath, and calls the loadRequest: to load the requested file.
Another way to accomplish what you desire is to make a private Category on UIWebView,
and use method swizzling to override the loadRequest: method, and call instead the loadData:MIMEType:textEncodingName:baseURL: method.
Beware that:
1) In low-memory scenarios (i.e. large files) a black screen with
"Error to load the document" appears, if that concerns you. (The
unhacked QLPreviewController knows how to handle these scenarios
very well and present the document).
2) I'm not sure Apple are going
to approve this kind of hack, although no private APIs are used
here.
code:
#implementation UIWebView (QLHack)
- (void)MyloadRequest:(NSURLRequest *)request
{
// Check somehow that it's the call of your QLPreviewController
// If not, just call the original method.
if (!insideQLPreviewController)
{
// Call original implementation
[self MyloadRequest:request];
}
else
{
// Load the real data you want
[self loadData:data MIMEType:mimeType textEncodingName:nil baseURL:someURL];
}
}
+ (void)load
{
method_exchangeImplementations(class_getInstanceMethod(self, #selector(loadRequest:)), class_getInstanceMethod(self, #selector(MyloadRequest:)));
}
#end
Actually, writing a file to a tmp directory is still insecure. The other alternative is to use UIWebView with NSURLProtocol and allow decrypting this data on the fly.
One way could be.
use Temp Dir , Save File in Temp , Make NSURL From that Temp File and Display and then Delete that temp Dir after that.
Thanks.

App store link for "rate/review this app"

I want to put a "rate/review this app" feature into my app.
Is there a way to link directly to the screen in the app store where they review the app? So the customer doesn't have to click through the main app link. Thanks.
EDIT: starting a bounty on this due to the lack of response. Just to make sure it is crystal clear: I am aware that I can link to my app's page in the store, and ask the user to click from there to the "review this app" screen. The question is whether it is possible to link directly to the "review this app" screen so they don't have to click through anything.
For versions lower than iOS 7 use the old one:
itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=YOUR_APP_ID
This works on my end (Xcode 5 - iOS 7 - Device!):
itms-apps://itunes.apple.com/app/idYOUR_APP_ID
For iOS 8 or later:
itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=YOUR_APP_ID&onlyLatestVersion=true&pageNumber=0&sortOrdering=1&type=Purple+Software
Code snippet (you can just copy & paste it):
#define YOUR_APP_STORE_ID 545174222 //Change this one to your ID
static NSString *const iOS7AppStoreURLFormat = #"itms-apps://itunes.apple.com/app/id%d";
static NSString *const iOSAppStoreURLFormat = #"itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=%d";
[NSURL URLWithString:[NSString stringWithFormat:([[UIDevice currentDevice].systemVersion floatValue] >= 7.0f)? iOS7AppStoreURLFormat: iOSAppStoreURLFormat, YOUR_APP_STORE_ID]]; // Would contain the right link
Update:
Swift 5.1, Xcode 11
Tested on Real Device iOS 13.0 (Guarantee to work)
import StoreKit
func rateApp() {
if #available(iOS 10.3, *) {
SKStoreReviewController.requestReview()
} else {
let appID = "Your App ID on App Store"
let urlStr = "https://itunes.apple.com/app/id\(appID)" // (Option 1) Open App Page
let urlStr = "https://itunes.apple.com/app/id\(appID)?action=write-review" // (Option 2) Open App Review Page
guard let url = URL(string: urlStr), UIApplication.shared.canOpenURL(url) else { return }
if #available(iOS 10.0, *) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
} else {
UIApplication.shared.openURL(url) // openURL(_:) is deprecated from iOS 10.
}
}
}
EDIT: iOS 11 Solution
This is the solution to my original answer (see below). When using the iOS 11 the following link format will work:
https://itunes.apple.com/us/app/appName/idAPP_ID?mt=8&action=write-review
Simply replace APP_ID with your specific app ID. The key to make the link work is the country code. The link above uses the us code but it actually doesn't matter which code is used. The user will automatically be redirected to his store.
iOS 11 Update:
It seems that none of the solutions presented in the other answers to get directly to the Review Page works on iOS 11.
The problem most likely is, that an app page in the iOS 11 App Store app does NOT have a Review Tab anymore. Instead the reviews are now located directly below the description and the screenshots. Of course it could still be possible to reach this section directly (e.g. with some kind of anchor), but it seems that this is not supported / intended by Apple.
Using one of the following links does not work anymore. They still bring the users to the App Store app but only to a blank page:
itms-apps://itunes.apple.com/app/idYOUR_APP_ID?action=write-review
itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=YOUR_APP_ID&onlyLatestVersion=true&pageNumber=0&sortOrdering=1&type=Purple+Software
Everyone how still uses these links should update their apps ASAP, because referring the users to a blank App Store page is most likely not what you intended.
Links which do not refer to the Review page but to the App page, still work however, e.g.
itms-apps://itunes.apple.com/app/idYOUR_APP_ID (same as above, but without write-review parameter)
So, you can still get the users to your apps Store page, but not directly to the review section anymore. Users now have to scroll down to the review section manually to leave their feedback.
Without a question this a "great and awesome benefit for User Experience and will help developers to engage users to leave high quality reviews without annoying them". Well done Apple...
Everything, written above is correct. Just a sample to insert into the app and change {YOUR APP ID} to actual app id, taken from iTunesconnect to show the Review page. Please note, as it was commented above, that it is not working on the Simulator - just the device.
Correcting because of iOS 7 changes.
Correcting for iOS 10+ openURL changes
For iOS 13.6+ review URL is accessible with the one, used before version 6.0. It drives directly to the review page. Code updated
NSString * appId = #"{YOUR APP ID}";
NSString * theUrl = [NSString stringWithFormat:#"itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=%#&onlyLatestVersion=true&pageNumber=0&sortOrdering=1&type=Purple+Software",appId];
int vers = (int) [[UIDevice currentDevice].systemVersion integerValue];
if (vers > 6 && vers < 12 ) theUrl = [NSString stringWithFormat:#"itms-apps://itunes.apple.com/app/id%#",appId];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:theUrl] options:#{} completionHandler:nil];
All above approaches are correct, but nowadays using SKStoreProductViewController leads to better user experience. To use it you need to do the following:
implement SKStoreProductViewControllerDelegate protocol in your app delegate
add required productViewControllerDidFinish method:
- (void)productViewControllerDidFinish:(SKStoreProductViewController *)viewController {
[viewController dismissViewControllerAnimated: YES completion: nil];
}
Check if SKStoreProductViewController class is available and either show it or switch to the App Store:
extern NSString* cAppleID; // must be defined somewhere...
if ([SKStoreProductViewController class] != nil) {
SKStoreProductViewController* skpvc = [[SKStoreProductViewController new] autorelease];
skpvc.delegate = self;
NSDictionary* dict = [NSDictionary dictionaryWithObject: cAppleID forKey: SKStoreProductParameterITunesItemIdentifier];
[skpvc loadProductWithParameters: dict completionBlock: nil];
[[self _viewController] presentViewController: skpvc animated: YES completion: nil];
}
else {
static NSString* const iOS7AppStoreURLFormat = #"itms-apps://itunes.apple.com/app/id%#";
static NSString* const iOSAppStoreURLFormat = #"itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=%#";
NSString* url = [[NSString alloc] initWithFormat: ([[UIDevice currentDevice].systemVersion floatValue] >= 7.0f) ? iOS7AppStoreURLFormat : iOSAppStoreURLFormat, cAppleID];
[[UIApplication sharedApplication] openURL: [NSURL URLWithString: url]];
}
Solution for iOS 11
Short App Store URLs do not correctly open the "write a review" interface in the new iOS 11 App Store. For example, this does not work:
https://itunes.apple.com/app/id333903271?mt=8&action=write-review
The workaround is to include a two-letter country code and app name in the URL, such as this:
https://itunes.apple.com/us/app/twitter/id333903271?mt=8&action=write-review
or
itms-apps://itunes.apple.com/us/app/twitter/id333903271?mt=8&action=write-review
You can get the full URL of your app from here: https://linkmaker.itunes.apple.com/
This successfully opens the "write a review" interface in the iOS 11 App Store.
Edit: As #Theo mentions below, the country code does not need to be localized and the app name in the URL does not need to be updated if the app name changes.
Hopefully Apple will fix this soon for the shorter URL. See rdar://34498138
Swift 2 version
func jumpToAppStore(appId: String) {
let url = "itms-apps://itunes.apple.com/app/id\(appId)"
UIApplication.sharedApplication().openURL(NSURL(string: url)!)
}
All previous links no more direct to "Reviews" tab,
This link would direct to "Reviews Tab" directly:
​
https://itunes.apple.com/app/viewContentsUserReviews?id=AppID
or
​
itms-apps://itunes.apple.com/app/viewContentsUserReviews?id=AppID
There is a new way to do this in iOS 11+ (new app store). You can open the "Write a Review" dialog directly.
iOS 11 example:
itms-apps://itunes.apple.com/us/app/id1137397744?action=write-review
or
https://itunes.apple.com/us/app/id1137397744?action=write-review
Notes:
A country code is required (/us/). It can be any country code, doesn't matter.
Change the app id (1137397744) to your app id (get it from iTunes URL).
If you want to support older iOS version (pre 11), you'll want some conditional logic to only link this way if the OS version is great than or equal to 11.
Using this URL was the perfect solution for me. It takes the user directly to the Write a Review section. Credits to #Joseph Duffy. MUST TRY
URL = itms-apps://itunes.apple.com/gb/app/idYOUR_APP_ID_HERE?action=write-review&mt=8
Replace YOUR_APP_ID_HERE with your AppId
For a sample code try this :
Swift 3, Xcode 8.2.1 :
let openAppStoreForRating = "itms-apps://itunes.apple.com/gb/app/id1136613532?action=write-review&mt=8"
if let url = URL(string: openAppStoreForRating), UIApplication.shared.canOpenURL(url) {
UIApplication.shared.openURL(url)
} else {
showAlert(title: "Cannot open AppStore",message: "Please select our app from the AppStore and write a review for us. Thanks!!")
}
Here showAlert is a custom function for an UIAlertController.
You can use this link in your url launcher function
https://apps.apple.com/app/APP_ID?action=write-review
In iOS7 the URL that switch ur app to App Store for rate and review has changed:
itms-apps://itunes.apple.com/app/idAPP_ID
Where APP_ID need to be replaced with your Application ID.
For iOS 6 and older, URL in previous answers are working fine.
Source: Appirater
Enjoy Coding..!!
Starting from iOS 10.3 you can attach action=write-review query item to your https://itunes.apple.com/... and https://appsto.re/... URLs. On iOS 10.3 and up it will open Write a review automatically, while on lower iOS releases will fall back to the app's App Store page.
iOS 11 update:
Use Apple's linkmaker: linkmaker.itunes.apple.com
and append &action=write-review, seems to be the most safe way to go.
iOS 4 has ditched the "Rate on Delete" function.
For the time being the only way to rate an application is via iTunes.
Edit: Links can be generated to your applications via iTunes Link Maker. This site has a tutorial.
NSString *url = [NSString stringWithFormat:#"https://itunes.apple.com/us/app/kidsworld/id906660185?ls=1&mt=8"];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]];
Swift 2 version that actually takes you to the review page for your app on both iOS 8 and iOS 9:
let appId = "YOUR_APP_ID"
let url = "itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=\(appId)"
UIApplication.sharedApplication().openURL(NSURL(string: url)!)
For >= iOS8: (Simplified #EliBud's answer).
#define APP_STORE_ID 1108885113
- (void)rateApp{
static NSString *const iOSAppStoreURLFormat = #"itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=%d";
NSURL *appStoreURL = [NSURL URLWithString:[NSString stringWithFormat:iOSAppStoreURLFormat, APP_STORE_ID]];
if ([[UIApplication sharedApplication] canOpenURL:appStoreURL]) {
[[UIApplication sharedApplication] openURL:appStoreURL];
}
}
I'm having the same issue in iOS 10 and I could open the iTunes rate section calling:
http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=YOUR_APP_ID&pageNumber=0&sortOrdering=2&type=Purple+Software&mt=7
Basically it changed the last url var to "mt=7"
Cheers
quote from Apple Developer Documentation
In addition, you can continue to include a persistent link in the
settings or configuration screens of your app that deep-links to your
App Store product page. To automatically open a page on which users
can write a review in the App Store, append the query parameter
action=write-review to your product URL.
So the URL would be the following:
itms-apps://itunes.apple.com/app/idYOUR_APP_ID?action=write-review
let rateUrl = "itms-apps://itunes.apple.com/app/idYOUR_APP_ID?action=write-review"
if UIApplication.shared.canOpenURL(rateUrl) {
UIApplication.shared.openURL(rateUrl)
}
Link to any App in the AppStore via SKStoreProductViewController
It is easy to link to your app at the app store via SKStoreProductViewController. But I struggled a little bit, so I decided to show here the whole process and some code necessary. This technique also makes sure that always the correct store will be used (important for localized apps).
To present the product screen of any app of the app store within your app with any of your apps ViewControllers follow this steps:
Add the StoreKit.framework in your project settings (Target, Build Phases -> Link Binary With Libraries
Import StoreKit into the ViewController class
Make your ViewController conforms this protocol
SKStoreProductViewControllerDelegate
Create the method to present the StoreView with the product screen you want
Dismiss the StoreView
But most important: This - for some reason - does not work in the simulator - you have to build and install on a real device with internet connectivity.
Adding the StorKit.framework to your project:
SWIFT 4: This is the code according to the described steps ahead:
// ----------------------------------------------------------------------------------------
// 2. Import StoreKit into the ViewController class
// ----------------------------------------------------------------------------------------
import StoreKit
// ...
// within your ViewController
// ----------------------------------------------------------------------------------------
// 4. Create the method to present the StoreView with the product screen you want
// ----------------------------------------------------------------------------------------
func showStore() {
// Define parameter for product (here with ID-Number)
let parameter : Dictionary<String, Any> = [SKStoreProductParameterITunesItemIdentifier : NSNumber(value: 742562928)]
// Create a SKStoreProduktViewController instance
let storeViewController : SKStoreProductViewController = SKStoreProductViewController()
// set Delegate
storeViewController.delegate = self
// load product
storeViewController.loadProduct(withParameters: parameter) { (success, error) in
if success == true {
// show storeController
self.present(storeViewController, animated: true, completion: nil)
} else {
print("NO SUCCESS LOADING PRODUCT SCREEN")
print("Error ? : \(error?.localizedDescription)")
}
}
}
// ...
// ----------------------------------------------------------------------------------------
// 3. Make your ViewController conforming the protocol SKStoreProductViewControllerDelegate
// ----------------------------------------------------------------------------------------
extension ViewController : SKStoreProductViewControllerDelegate {
// ----------------------------------------------------------------------------------------
// 5. Dismiss the StoreView
// ----------------------------------------------------------------------------------------
func productViewControllerDidFinish(_ viewController: SKStoreProductViewController) {
print("RECEIVED a FINISH-Message from SKStoreProduktViewController")
viewController.dismiss(animated: true, completion: nil)
}
}
Here is the code that I am using in my app;
-(void)rateApp {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:[#"itms-apps://itunes.apple.com/app/" stringByAppendingString: #"id547101139"]]];
}
The accepted answer failed to load the "Reviews" tab. I found below method to load the "Review" tab without "Details" tab.
[[UIApplication sharedApplication] openURL:[NSURL URLWithString: #"itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id={APP_ID}&pageNumber=0&sortOrdering=2&mt=8"]];
Replace {APP_ID} with your app apps store app id.
SWIFT 3
fileprivate func openAppStore() {
let appId = "YOUR_APP_ID"
let url_string = "itms-apps://itunes.apple.com/app/id\(appId)"
if let url = URL(string: url_string) {
UIApplication.shared.openURL(url)
}
}
This works fine on iOS 9 - 11.
Haven't tested on earlier versions.
[NSURL URLWithString:#"https://itunes.apple.com/app/idXXXXXXXXXX?action=write-review"];
Starting in iOS 10.3:
import StoreKit
func someFunction() {
SKStoreReviewController.requestReview()
}
but its has been just released with 10.3, so you will still need some fallback method for older versions as described above
Swift 5 Tested in iOS14
Opens the review window with 5 stars selected
private func openReviewInAppStore() {
let rateUrl = "itms-apps://itunes.apple.com/app/idYOURAPPID?action=write-review"
if UIApplication.shared.canOpenURL(URL.init(string: rateUrl)!) {
UIApplication.shared.open(URL.init(string: rateUrl)!, options: [:], completionHandler: nil)
}
}
If your app has been approved for Beta and it's not live then the app review link is available but it won't be live to leave reviews.
Log into iTunes Connect
Click My Apps
Click the App Icon your interested in
Make sure your on the App Store page
Go toApp Information section (it should automatically take you there)
At the bottom of that page there is a blue link that says View on App Store. Click it and it will open to an a blank page. Copy what's in the url bar at the top of the page and that's your app reviews link. It will be live once the app is live.
Know you apple app id, it is the numeric digits in your itunes app url after id field.
Something like this : https://itunes.apple.com/app/id148688859, then 148688859 is your app id.
Then, Redirect to this url using your correct app id : https://itunes.apple.com/app/idYOUR_APP_ID?action=write-review.

How can I reset the NSUserDefaults data in the iPhone simulator?

I've added NSUserDefaults data retrieval to my app, which is pretty nice. But for testing I would like to reset all the data I added to the defaults database, so that everything is in the state when the user launches the app the first time.
I tried to call:
[NSUserDefaults resetStandardUserDefaults];
but that doesn't do anything. The defaults are still saved and can be retrieved.
You want NSUserDefaults removePersistentDomainForName. This will remove all user defaults for the application:
NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier];
[[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain];
For more information on the NSUserDefaults class see the Apple docs.
Alternately, if all you are concerned with is data in the iOS Simulator, you can do that via iOS Simulator > Reset Content and Settings.
The easiest way is to remove the app from the simulator-- just like you'd remove it from a real phone, by tapping (clicking) and holding until the icons start vibrating. That removes all app data, and the next time you install from Xcode it's like the first time.
If you have other app data you need to keep, you have a couple of options.
One way would be to have some debug code that calls removeObjectForKey: on each of your defaults keys.
The other is to find the directory where the simulator copy is installed, and remove the file containing the preferences. Use this to find the app:
ls -ld ~/Library/Application\ Support/iPhone\ Simulator/User/Applications/*/*.app
The full path to your app will contain directory whose name is a UUID. In that directory, look in Library/Preferences for the preferences file. Remove that, and user preferences are gone.
You may find out what you have "written" to userdefaults for the app are all inside a file
delete this .plist file works:
user name/Library/Preferences/com.theAppYouAreHandling.plist
In Swift 2.0 the following 1 line will reset all the NSUserDefaults for an app:
NSUserDefaults.standardUserDefaults().removePersistentDomainForName(NSBundle.mainBundle().bundleIdentifier!)
Actually, this may not be suitable in every circumstance, but since I keep no information of value in the simulator, I just Reset Content and Settings from the iPhone menu, from within the simulator itself.
Here's the swift version:
let domainName = NSBundle.mainBundle().bundleIdentifier!
NSUserDefaults.standardUserDefaults().removePersistentDomainForName(domainName)
Til Xcode 6 and iOS 8 Simulator the location of the plist file changed.
I found the *.plist in the following directory:
[1] /Users/SOME-USERNAME/Library/Developer/CoreSimulator/Devices/SOME-DEVICE-ID/data/Library/Preferences/SP.UserDefaultsTest.plist
Deleting the located file from path [1] manually and the NSUserDefaults are gone.
In the simulator top menu:
Simulator -> Reset Content and Settings...
You can use removePersistentDomainForName method available with NSUserDefaults Class.
Syntax :
- (void)removePersistentDomainForName:(NSString *)domainName
Example :
NSString *strAppBundleId = [[NSBundle mainBundle] bundleIdentifier];
[[NSUserDefaults standardUserDefaults] removePersistentDomainForName:strAppBundleId];
If you're doing this in a unittest, you may want to keep the state of your app in the current simulator instead of inadvertently wiping it every time you also run your unittests. One way to do this is to simply hang on to the old values for your app domain in setUp() and then restore them in tearDown():
class MyClass_Tests: XCTestCase {
static let domainName = Bundle.main.bundleIdentifier!
static var oldUserDefaults: [String : Any]?
override class func setUp() {
super.setUp()
// Hang onto this so we don't inadvertently wipe the app's state while running tests.
oldUserDefaults = UserDefaults.standard.persistentDomain(forName: domainName)
}
override class func tearDown() {
// Restore the old values.
UserDefaults.standard.setPersistentDomain(oldUserDefaults!, forName: domainName)
super.tearDown()
}
override func setUp() {
super.setUp()
// Wipe the state for each test.
UserDefaults.standard.removePersistentDomain(forName: MyClass_Tests.domainName)
}
override func tearDown() {
super.tearDown()
}
}
You can find UserDefaults in following path in Finder, delete the .plist
~/Users/<USER>/Library/Developer/CoreSimulator/Devices/<DEVICE_ID>/data/Containers/Data/Application/<APP_ID>/Library/Preferences/<APP_BUNDLE_ID>.plist
Path components to replace:
1. <USER> = MAC user name
2. <DEVICE_ID> = Device/Simulator Identifier, e.g., 999271B8-FAA6-41DE-9864-4111F422ED12
3. <APP_ID> = Application identifier, e.g., 69928AEF-BCD5-413A-B06F-BC4A07080D62
4. <APP_BUNDLE_ID> = Your apps bundle identifier, e.g., com.company.appname.plist