How to show dialogue box of rate app after X times? - ios

i made app with swift in Xcode and i have implemented Rate App Modal in my app but the problem is this its shown when users install the app and open for very first time but i want to show it after 2 or 3 days , so users can view my app and if he likes then he can rate my app. this is my code
let reviewService = ReviewService.shared
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let deadline = DispatchTime.now() + .seconds(120)
DispatchQueue.main.asyncAfter(deadline: deadline) { [weak self] in
self?.reviewService.requestReview()
}
}
and this is my ReviewService.swift file
private init() {}
static let shared = ReviewService()
private let defaults = UserDefaults.standard
private let app = UIApplication.shared
private var lastRequest: Date? {
get {
return defaults.value(forKey: "ReviewService.lastRequest") as? Date
}
set {
defaults.set(newValue, forKey: "ReviewService.lastRequest")
}
}
private var oneWeekAgo: Date {
return Calendar.current.date(byAdding: .day, value: -7, to: Date())!
}
private var shouldRequestReview: Bool {
if lastRequest == nil {
return true
} else if let lastRequest = self.lastRequest, lastRequest < oneWeekAgo {
return true
}
return false
}
func requestReview(isWrittenReview: Bool = false) {
guard shouldRequestReview else { return }
if isWrittenReview {
let appStoreUrl = URL(string: "https://itunes.apple.com/app/idxxxxxxx?action=write-review")!
app.open(appStoreUrl)
} else {
if #available(iOS 10.3, *) {
SKStoreReviewController.requestReview()
} else {
// Fallback on earlier versions
}
}
lastRequest = Date()
}
}

When the code finds that no previous request was saved don’t show the request. Instead save that the last request happened 4 or 5 days ago. Then in a few days the first review request will be shown.
One way to do this would be in the shouldRequestReview getter. If lastRequset is nil make a new post dated request time and save that, then return false.

Related

How to show the rating prompt after seven days of installation in iOS

I want to show the rating prompt after seven days of installation of the new version of the app
import Foundation
import StoreKit
#objc class AppStoreReviewManager: NSObject {
#objc static func requestReviewIfAppropriate(isForced: Bool = false)
{
if isForced {
SKStoreReviewController.requestReview()
return
}
let bundle = Bundle.main
let defaults = UserDefaults.standard
let bundleVersionKey = kCFBundleVersionKey as String
let currentVersion = bundle.object(forInfoDictionaryKey: bundleVersionKey) as? String
let lastVersion = defaults.string(forKey: UserDefaults.Key.lastReviewRequestAppVersion.rawValue)
guard lastVersion == nil || lastVersion != currentVersion else { return }
SKStoreReviewController.requestReview()
defaults.set(currentVersion, forKey: UserDefaults.Key.lastReviewRequestAppVersion.rawValue)
UserDefaultsData.shouldPresentPromptPopUp = false
}
}
public func presentRatingPopUp() {
AppStoreReviewManager.requestReviewIfAppropriate()
if UserDefaultsData.promptDate == nil {
guard let nextPromptDate = Calendar.current.date(byAdding: .day, value: 28, to: Date()) else { return }
UserDefaultsData.promptDate = nextPromptDate
}
}
public func checkFirstInstanceOfRatingPrompt() -\> Bool {
return UserDefaultsData.promptDate == nil
}
public func checkRatingPromptFeasibiltyForExport() {
let promptConsented = JourneyManager.shared.checkForPresentingRatingPrompt()
if checkFirstInstanceOfRatingPrompt() || promptConsented {
presentRatingPopUp()
}
}
Currently the app shows the rating prompt after checking some conditions along with the conditions I want to show the prompt after 7 days of installation of new version of the app.

UITableView scrolling performance problem

I am currently working as a 5 month junior ios developer.
The project I'm working on is an application that shows the prices of 70 cryptocurrencies realtime with websocket connection.
we used websocket connection, UItableview, UITableViewDiffableDataSource, NSDiffableDataSourceSnapshot while developing the application.
But right now there are problems such as slowdown scrolling or not stop scroling and UI locking while scrolling in the tableview because too much data is processed at the same time.
after i check cpu performance with timer profiler I came to the conclusion that updateDataSource and updateUI functions exhausting the main thread.
func updateDataSource(model: [PairModel]) {
var snapshot = DiffableDataSourceSnapshot()
let diff = model.difference(from: snapshot.itemIdentifiers)
let currentIdentifiers = snapshot.itemIdentifiers
guard let newIdentifiers = currentIdentifiers.applying(diff) else {
return
}
snapshot.appendSections([.first])
snapshot.deleteItems(currentIdentifiers)
snapshot.appendItems(newIdentifiers)
dataSource?.apply(snapshot, animatingDifferences: false, completion: nil)
}
func updateUI(data: SocketData) {
guard let newData = data.data else { return }
guard let current = data.data?.price else { return }
guard let closed = data.data?.lastDayClosePrice else { return }
let dailyChange = ((current - closed)/closed)*100
DispatchQueue.main.async { [self] in
if model.filter({ $0.symbol == newData.pairSymbol }).first != nil {
let index = model.enumerated().first(where: { $0.element.symbol == newData.pairSymbol})
guard let location = index?.offset else { return }
model[location].price = current
model[location].dailyPercent = dailyChange
if calculateLastSignalTime(alertDate: model[location].alertDate) > 0 {
//Do Nothing
} else {
model[location].alertDate = ""
model[location].alertType = ""
}
if let text = allSymbolsView.searchTextField.text {
if text != "" {
filteredModel = model.filter({ $0.name.contains(text) || $0.symbol.contains(text) })
updateDataSource(model: filteredModel)
} else {
filteredModel = model
updateDataSource(model: filteredModel)
}
}
}
delegate?.pricesChange(data: self.model)
}
}
Regards.
ALL of your code is running on the main thread. You have to wrap your entire updateUI function inside a DispatchQueue.global(qos:), and then wrap your dataSource.apply(snapshot) line inside a DispatchQueue.main.async. the dataSource.apply(snapshot) line is the only UI work you're doing in all that code you posted.

User Defaults Swift

I have hint view (tooltip). And I want it display in my app 1 time per download app. When user downloading app this tooltip is showing and then dismiss. When user delete app and again downloading tooltip should work again.
let options: AMTooltipViewOptions = .init(textColor: Color.guideSubTitle,
textBoxBackgroundColor: Color.guideScreenBackground,
textBoxCornerRadius: 8,
lineColor: Color.guideScreenBackground,
lineHeight: 15,
dotSize: 0,
focusViewRadius: 15,
focustViewVerticalPadding: 0,
focustViewHorizontalPadding: 0)
AMTooltipView(options: options,
message: Localizable.scan_open_from_gallery + "\n" + Localizable.scan_clear,
focusView: content.openGalleryBtn, target: self)
and I have key
public var hintView: Bool {
get {
return setting.bool(forKey: Key.hintView)
}
set {
setting.set(false, forKey: Key.hintView)
}
}
How can I control when user deletes app and again download it
Store a bool in UserDefaults. Once the user uninstalls the app, the data will be deleted.
in your AppDelegate.swift
let DEFAULTS = UserDefaults.standard
var isUserFirstTime = !DEFAULTS.bool(forKey: "isUserFirstLogin") // by default it will store false, so when the user opens the app for first time, isUserFirstTime = true.
then inside your didFinishLaunchingWithOptions function
if isUserFirstTime {
// your code here to show toolbar
} else {
// dont show toolbar
}
// once you have completed the operation, set the key to true.
DEFAULTS.set(true, forKey: "isUserFirstLogin")
Change your getter and setter for hintView like below
public var hintView: Bool {
get {
return setting.bool(forKey: Key.hintView)
}
set {
setting.set(true, forKey: Key.hintView)
setting.synchronize()
}
}
And now use your hintView variable like below for showing and hiding the toolbar.
//it will always returns false for first time when you install new app.
if hintView {
print("Hide Toolbar")
}
else {
//set flag to true for first time install application.
hintView = true
print("Show Toolbar")
}
I hope it will more clear to you
import Foundation
import AMTooltip
class HintViewController {
let userDefaults: UserDefaults = .standard
let wasLaunchedBefore: Bool
var isFirstLaunch: Bool {
return !wasLaunchedBefore
}
init() {
let key = "wasLaunchBefore"
let wasLaunchedBefore = userDefaults.bool(forKey: key)
self.wasLaunchedBefore = wasLaunchedBefore
if !wasLaunchedBefore {
userDefaults.set(true, forKey: key)
}
}
func showHintView(message: String!, focusView: UIView, target: UIViewController) {
let options: AMTooltipViewOptions = .init(textColor: Color.guideSubTitle,
textBoxBackgroundColor: Color.guideScreenBackground,
textBoxCornerRadius: 8,
lineColor: Color.guideScreenBackground,
lineHeight: 15,
dotSize: 0,
focusViewRadius: 15,
focustViewVerticalPadding: 0,
focustViewHorizontalPadding: 0)
AMTooltipView(options: options, message: message, focusView: focusView, target: target)
}
}

Swift 4: Checking if week, month, 2 months, etc. has passed since initial launch using Calendar

I'm trying to include timers so that a button appears a week, a month, 2 months, etc. after the initial launch date of the app.
I've used Date.addingTimeInterval (see code below), but it doesn't seem as effective for longer periods of time (week/months). The Swift documentation recommends using Calendar, but I'm not sure how to do that.
override func viewDidLoad() {
super.viewDidLoad()
if installedDate == nil {
installedDate = Date()
let timer = Calendar.current
Button1.isHidden = true
Button2.isHidden = true
Button3.isHidden = true
}
else {
print("Not first run")
print(installedDate!)
let Button1Date = Date().addingTimeInterval(604800) //for after 1 week
let Button2Date = Date().addingTimeInterval(2592000) //for after 1 month
let Button3Date = Date().addingTimeInterval(5184000) //for after 2 months
if installedDate == Button1Date {
Button1.isHidden = false
}
if installedDate == Button2Date {
Button2.isHidden = false
}
if installedDate == Button3Date {
Button.isHidden = false
}
}
}
var installedDate: Date? {
get {
return UserDefaults.standard.object(forKey: "installedDateKey") as? Date
}
set {
UserDefaults.standard.set(newValue, forKey: "installedDateKey")
}
}
What is a more efficient way of comparing the launch date to the expected date I need?
From what I understand of your question, you want a button to display if they have had the app for more than a certain amount of time. If that is the case, I've written some code that will be helpful to you:
let timeSinceInstalled = -(installedDate ?? Date()).timeIntervalSinceNow
Button1.isHidden = timeSinceInstalled < 604800
Button2.isHidden = timeSinceInstalled < 2592000
Button3.isHidden = timeSinceInstalled < 5184000
You can actually replace most of the code inside of your viewDidLoad() function, even the part where you check if installedDate == nil. Here is your updated code:
override func viewDidLoad() {
super.viewDidLoad()
let timeSinceInstalled = -(installedDate ?? Date()).timeIntervalSinceNow
Button1.isHidden = timeSinceInstalled < 604800
Button2.isHidden = timeSinceInstalled < 2592000
Button3.isHidden = timeSinceInstalled < 5184000
}
var installedDate: Date? {
get {
return UserDefaults.standard.object(forKey: "installedDateKey") as? Date
}
set {
UserDefaults.standard.set(newValue, forKey: "installedDateKey")
}
}
If you want to set the title to "You have none available" when the button should not be clickable, here is the code for that:
override func viewDidLoad() {
super.viewDidLoad()
let timeSinceInstalled = -(installedDate ?? Date()).timeIntervalSinceNow
setTitle(Button1, timeSinceInstalled < 604800)
setTitle(Button2, timeSinceInstalled < 2592000)
setTitle(Button3, timeSinceInstalled < 5184000)
}
func setTitle(_ button: UIButton, _ statement: Bool) {
button.setTitle(statement ? "You have none available" : "Click me", for: .normal)
button.isEnabled = !statement
}
var installedDate: Date? {
get {
return UserDefaults.standard.object(forKey: "installedDateKey") as? Date
}
set {
UserDefaults.standard.set(newValue, forKey: "installedDateKey")
}
}

An array of a value not being properly displayed in the label

I am using xcode 8 and swift 3. I have made a mini game where you have to answer questions as fast as you can. You are timed by a stopwatch. I have the code to actually store the times(eg 23 seconds) but instead of storing the time, it replaces it with "Label". My stopwatch label is called down and the label that displays the values is called resultLabel I have been told the problem is when i set the text of the label. Any ideas on where i need to fix it. An example is ["Label", "Label", "Label"]
Stopwatch code:
if timerSecond != nil {
timerSecond?.invalidate()
timerSecond = nil
} else {
timerSecond = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
self.currentTime += 1
let minutePortion = String(format: "%02d", self.currentTime / 60 )
let secondsPortion = String(format: "%02d", self.currentTime % 60 )
self.down.text = "\(minutePortion):\(secondsPortion)"
}
}
extension for User Defaults:
extension UserDefaults {
var timesSecond: [String] {
get {
if let times = UserDefaults.standard.object(forKey: "times") as? [String] {
return times
} else {
return []
}
}
set {
UserDefaults.standard.set(newValue, forKey: "times")
}
}
}
Button code to display the array of values:
let arrayOfTimes = UserDefaults.standard.timesSecond
resultLabel.text = "\(arrayOfTimes)"
code for storing the data:
UserDefaults.standard.timesSecond.append(resultLabel.text!)

Resources