Skip to content

Instantly share code, notes, and snippets.

@dlo
Last active March 9, 2018 22:00
Show Gist options
  • Save dlo/04d728c1e0ffadd200f62bb6fc60f390 to your computer and use it in GitHub Desktop.
Save dlo/04d728c1e0ffadd200f62bb6fc60f390 to your computer and use it in GitHub Desktop.
Simple review prompt point system for iOS apps, with points and half-lives assigned to in-app actions
import UIKit
import StoreKit
enum StoreReviewValue: Int, Codable {
case high
case medium
case low
var value: NSDecimalNumber {
switch self {
case .high: return 100
case .medium: return 10
case .low: return 1
}
}
}
enum StoreReviewHalfLife {
case hour
case day
case week
case month
var seconds: Int {
switch self {
case .hour: return 60*60
case .day: return StoreReviewHalfLife.hour.seconds * 24
case .week: return StoreReviewHalfLife.day.seconds * 7
case .month: return StoreReviewHalfLife.week.seconds * 4
}
}
}
struct StoreReviewAction: Codable {
var initialValue: StoreReviewValue
var halfLife: Int
var createdOn: Date
var value: NSDecimalNumber {
let now = Date()
let interval = now.timeIntervalSince(createdOn)
let exponent = NSDecimalNumber(value: pow(0.5, interval))
return initialValue.value.multiplying(by: exponent)
}
init(value: StoreReviewValue, halfLife: Int) {
self.initialValue = value
self.halfLife = halfLife
createdOn = Date()
}
}
struct StoreReviewPointsManager {
var actions: [StoreReviewAction] = []
var promptThreshold: NSDecimalNumber!
var data: Data? {
let encoder = JSONEncoder()
return try? encoder.encode(actions)
}
var value: NSDecimalNumber {
return actions.reduce(NSDecimalNumber.zero, {
$1.value.adding($0)
})
}
let fileURL: URL = {
let directories = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
guard let documentsDirectory = directories.first else {
return URL(fileURLWithPath: "/tmp/points.txt")
}
return documentsDirectory.appendingPathComponent("/points.txt")
}()
// MARK: -
init(threshold: NSDecimalNumber) {
let decoder = JSONDecoder()
if let data = try? Data(contentsOf: fileURL),
let _actions = try? decoder.decode([StoreReviewAction].self, from: data) {
actions = _actions
}
promptThreshold = threshold
}
// MARK: -
func save() throws {
try data?.write(to: fileURL)
}
mutating func addAction(value: StoreReviewValue, halfLife: StoreReviewHalfLife) {
actions.append(StoreReviewAction(value: value, halfLife: halfLife.seconds))
if self.value.compare(promptThreshold) == .orderedDescending {
SKStoreReviewController.requestReview()
actions.removeAll()
}
}
}
var manager = StoreReviewPointsManager(threshold: 10)
manager.addAction(value: .high, halfLife: .hour)
@mrh-is
Copy link

mrh-is commented Mar 9, 2018

StoreReviewAction forgot to use its halfLife! Should look more like:

var value: NSDecimalNumber {
    let interval = Date().timeIntervalSince(createdOn)
    let halfLifeCount= interval/(Double(halfLife)*1000)
    let exponent = NSDecimalNumber(value: pow(0.5, interval))
    return initialValue.value.multiplying(by: exponent)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment