I am currently learning how to ask the user for an AppStore rating in the app. I have read through numerous documentations and reports from Apple and other bloggers, but I still have questions:
I thought that I want to display my request after the user has completed a sequence of actions 3 times. But if the user doesn't want to rate my app right now, can I ask him to rate my app again later? I also read that Apple controls when and how often the review request is displayed (up to 3x / year)
My app has different functions. Therefore it is not guaranteed that the user accesses exactly this function, where I want to show the rating view. Is it therefore possible to call up the rating request at different points in the app and simply leave it up to Apple whether the rating request is also displayed?
Best regards
Edit for #K bakalov :
enum AppReviewRequest {
#AppStorage("runCountSinceLastRequest") static var runCountSinceLastRequest = 0
#AppStorage("lastVersion") static var lastVersion = ""
static let threshold = 4
static let currentVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as! String
static func countUpRequestReview() {
if currentVersion != lastVersion {
if runCountSinceLastRequest < threshold - 1 {
runCountSinceLastRequest += 1
print(runCountSinceLastRequest)
}
}
}
static func requestReviewIfNeeded() {
if currentVersion != lastVersion {
if runCountSinceLastRequest == threshold {
if let scene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene {
SKStoreReviewController.requestReview(in: scene)
lastVersion = currentVersion
runCountSinceLastRequest = 0
}
}
}
}
}
I assume you already read that but if you haven't I am leaving a link for you to check it out: https://developer.apple.com/documentation/storekit/requesting_app_store_reviews
It contains helpful information and guides when and how to prompt the user to leave a review.
Back to your questions.
Yes, you can ask again for review but there is no guarantee that the Review pop-up (alert) will be presented again. It is usually delayed in time and sometimes requires a new update of the app to trigger a new review prompt; That is correct, Apple has control over it but I don't know if its limited to only 3 times a year.
Yes, you can call it as many times as you want and wherever you find it suitable. Although, I highly encourage you to read (if still haven't) the link above. You need to think of a non-intrusive way to ask for review, otherwise you risk the user to dismiss the prompt, even if they like the app.
Just an advice, many apps use an approach with a custom popup "Do you like the app? Yes/No", if "Yes" then request a review using requestReview() call. And/or as mentioned in the article above, you can always use a manual review by redirecting to the AppStore, if you need it as a result of a CTA (tap on a button for example).
Related
I'm relatively new to making apps, and even newer to independent watch apps. As training, I'm making a watch app that I can use to log my water intake throughout the day. I've created a new intent definition file (see image 1) on which I've checked the marks for all the target memberships (the app, the WatchKit app, and the WatchKit extension). Furthermore, the target membership class is a public intent for the WatchKit extension.
When logging my water I execute the following code:
let intent = INManager.intent(drink: item)
INManager.donateShortcuts(withIntent: intent)
and my IntentManager looks like this:
import Foundation
import Intents
class IntentManager {
func intent(drink: Drink) -> LogDrinkIntent {
let intent = LogDrinkIntent()
intent.uuid = drink.id.uuidString
intent.name = drink.name
intent.emoji = drink.emoji
return intent
}
func donateShortcuts(withIntent intent:INIntent) {
var relevantShortcuts: [INRelevantShortcut] = []
if let relevantShortcut = defaultRelevantShortcut(withIntent: intent) {
relevantShortcuts.append(relevantShortcut)
}
INRelevantShortcutStore.default.setRelevantShortcuts(relevantShortcuts) { (error) in
if let error = error {
print("Failed to set relevant shortcuts: \(error))")
} else {
print("Relevant shortcuts set.")
}
}
}
private func defaultRelevantShortcut(withIntent intent: INIntent) -> INRelevantShortcut? {
if let shortcut = INShortcut(intent: intent) {
let relevantShortcut = INRelevantShortcut(shortcut: shortcut)
relevantShortcut.shortcutRole = .action
let template = INDefaultCardTemplate(title: "Log Drink")
relevantShortcut.watchTemplate = template
print("Returning relevant shortcut.")
return relevantShortcut
}
return nil
}
}
When logging a drink the confirmation Returning relevant shortcut. and Relevant shortcuts set. are printed. However, the Siri watch face doesn't update to include a link to my action. I got the code for the IntentManager from this Medium article.
I really appreciate your time and help. I've had a hard time trying to find any details about this functionality and Apple's documentation is imo inferior. Thank you! If you need more details or such, feel free to ask.
Image 1
Let's saddle the horse from behind: Generally speaking, you want to make use of Soup Chef. Now you can categorize Siri suggestions into two sub-groups, being donated shortcuts and relevant shortcuts.
In your specific example of a "water intake" logging app, you should work with donating the INIntent to INIteraction. The reason for that is quite simple: Your suggestion is due to an action a user has committed within your application, not based upon plainly relevance, thus your passage about INRelevantShortcutStore isn't necessary and/or should be replaced with INInteraction.
To re-phrase myself: The issue is that you parse INRelevantShortcutStore as a donation, see here:
func donateShortcuts(withIntent intent:INIntent) {
var relevantShortcuts: [INRelevantShortcut] = []
if let relevantShortcut = defaultRelevantShortcut(withIntent: intent) {
relevantShortcuts.append(relevantShortcut)
}
INRelevantShortcutStore.default.setRelevantShortcuts(relevantShortcuts) { (error) in
if let error = error {
print("Failed to set relevant shortcuts: \(error))")
} else {
print("Relevant shortcuts set.")
}
}
}
... as explained above, that is not the correct usage for INIntent in your specific example.
I highly suggest to read through Soup Chef in general as well as specifically donating shortcuts to Siri (what you want to do!). The documentation is very detailed and explanative!
I am trying to recreate an existing motivational quote app as practice.
On this app, you get a notification which displays the contents, and if you click it the home page provides you with the rest of the quote (screenshots below). I have seen this type of app a lot – they all use collection views and the notification and homepage content always match.
I have tried mimicking this by setting up local notifications for the next 7 days to provide the user with a random quote from my array:
func configureNotifications() {
let shuffled = quotes.shuffled()
for i in 1...7 {
let content = UNMutableNotificationContent()
content.title = “Daily quotes”
content.body = shuffled[i].text
let alertDate = Date().byAdding(days: i) // scheduled for next 7 days added
var alertComponents = Calendar.current.dateComponents([.day, .month, .year], from: alertDate)
alertComponents.hour = 10
Etc
}
func updateQuoteHome() {
guard let selectedQuote = quotes.randomElement() else {
fatalError("Unable to read a quote.")
}
updateLabels(with: selectedQuote)
}
However, once the notification is clicked, or if the user just randomly opens the app, the content displayed is obviously completely different.
What is the method to link these two together – is it even possible with local notification or are these apps utilising push notifications?
Is there something about this in the documentation?
Thank you ~
You should use Push Notifications for this feature. If you have little or no backend experience you can use Firebase Functions and Firebase Messaging to achieve this.
multiple in app purchaseI am working a music player application, the user need to purchase the albums before they start hearing it, I have attached a sample purchase screen. when the user tap it it should move to inApp purchase . The purchase type is non- consumable they need to pay only once.
I am having a table view with list of albums in it, if I purchase the first index album, it is working fine when I move to second index it was stating like already purchased.
I have used SwiftyStoreKit pod for the inappropriate purchase and everything is working as I expected. but I don't know how to handle tableview with n number of cell.
//INAPP purchase swift guy
func getinfo (purchase: RegisterPurchase) {
NetworkActivityIndicatioManager.NetworkoperationStarted()
SwiftyStoreKit.retrieveProductsInfo([bundleId + "." + purchase.rawValue]) { (result) in
NetworkActivityIndicatioManager.NetworkOperationFinished()
self.showAlert(self.alertForProductRetrievalInfo(result))
}
}
func purchase(purchase: RegisterPurchase) {
NetworkActivityIndicatioManager.NetworkoperationStarted()
SwiftyStoreKit.purchaseProduct(bundleId + "." + purchase.rawValue, atomically: true) { result in
NetworkActivityIndicatioManager.NetworkOperationFinished()
if case .success(let purchase) = result {
let downloads = purchase.transaction.downloads
if !downloads.isEmpty {
SwiftyStoreKit.start(downloads)
}
// Deliver content from server, then:
if purchase.needsFinishTransaction {
SwiftyStoreKit.finishTransaction(purchase.transaction)
}
}
if let alert = self.alertForPurchaseResult(result) {
self.showAlert(alert)
}
}
}
Issue is with your product type. If you want to make the product purchasable again, you should use consumable purchase, NOT non-consumable.
Non-consumable product can be purchased only once for an iTunes
account.
For your case, you can change the product type to consumable, then save this purchase in server and link with the user. This way, you can unlock the album if user purchased before.
currently I’m working with iBeacons and by far have managed to scan and display them on screen. But then I was told to scan and display for ONLY the nearest one ( the closest one), and ignore all the others. Can someone give me a rough idea how to get the closest beacon among a bunch of beacons that I have?
I believe that I can’t use the Proximity and Accuracy in this case since they often fluctuate.
Basically I haven't figured out how to work with the logic:
onBeaconsDiscovered() {
this.delegate.didRangeBeaconsInRegion().subscribe( data => {
if (data.beacons !== null) {
let nearestBeacon = // { code for detecting the nearest beacon }
// then show up a notification / alert / toast ...
}
});
}
Please help! I appreciate every idea.
You can see my answer on how to do this in native Swift code here : Swift find closest Beacon by rssi
Here is a direct translation of that Swift code into JavaScript:
var closestBeacon = null;
for (var beacon in data.beacons) {
if (beacon.rssi < 0 && closestBeacon != null && beacon.rssi > closestBeacon.rssi) {
closestBeacon = beacon;
}
}
Keep in mind that the this.delegate.didRangeBeaconsInRegion().subscribe( data => { callback will happen once per second. So if you want to issue an alert based upon the closest beacon, you also need to take care not to to pop up new alerts every single second.
If you want to keep track of beacons you have already handled, you can declare a new object in some global-like scope outside the callback:
var triggeredBeacons = {};
Then before you do any processing on a beacon, construct a string key based on your beacon identifiers, and make sure it has not triggered before:
var beaconIdentifiers = beacon.uuid + " " + beacon.major + " " + beacon.minor;
if (triggeredBeacons[beaconIdentifiers] == true) {
// beacon has already triggered. Ignore it
}
else {
triggeredBeacons[beaconIdentifiers] = true;
// Put your beacon processing here
}
I am trying to implement Speech-to-text feature for watchkit app.
I referred this question which has sample code.
Following is the code I tried:
self.presentTextInputControllerWithSuggestions(["Start it", "Stop it"], allowedInputMode: .Plain, completion: { (selectedAnswers) -> Void in
if selectedAnswers.count > 0 {
if let spokenReply = selectedAnswers[0] as? String {
self.label.setText("\(spokenReply)")
}
}
})
label is a label to display text I speak.
When I run it, it shows the screen where you are supposed to speak (Siri kind of screen) and you have two options on top: ‘Cancel', and ‘Done'. Once I am done speaking, I tap on ‘Done’ but screen doesn’t go away or shows me initial screen, I always have to tap on ‘Cancel’ to go back, and I don’t get any speech data in form of text. I checked it and seems like selectedAnswers is always an empty array, unless I tap on the "Start it"/"Stop it" options.
Can anyone help me with this? I want to show the spoken message on label. I have code inside awakeWithContext method in InterfaceController.swift file, am I supposed to put it somewhere else?
I am using iPhone with iOS 9 beta 2 and watchOS 2 beta on AppleWatch.
Thanks!
You can ask for user input and give him suggestion (see Swift example bellow).
self.presentTextInputControllerWithSuggestions(["suggestion 1", "suggestion 2"] allowedInputMode: .Plain, completion: { (answers) -> Void in
if answers && answers.count > 0 {
if let answer = answers[0] as? String {
println("\answer")
}
}
})
If suggestion is nil it goes directly to dictation. It is not working on the simulator but it is on real watch.
Your approach is correct but something is wrong with your SIRI , try changing the language.
It should work like these.