TestsTested | ✓ |
LangLanguage | SwiftSwift |
License | MIT |
ReleasedLast Release | Oct 2017 |
SwiftSwift Version | 3.0 |
SPMSupports SPM | ✗ |
Maintained by Pawel Urbanek.
WaitForIt makes implementing a common iOS app scenarios a breeze:
Dealing with this kind of logic usually involves manually saving data to UserDefaults
and has to be redone from scratch for each scenario.
WaitForIt provides a simple declarative API allowing you to handle most of the possible scenarios without worrying about underlaying implementation.
ScenarioProtocol
has the following properties which can be used to define when a scenario should be executed:
protocol ScenarioProtocol {
// minimum number of scenario events needed to be trigerred before scenario can be executed
static var minEventsRequired: Int? { get set }
// maximum number of scenario events which can be trigerred before scenario stops executing
static var maxEventsPermitted: Int? { get set }
// maximum number of times that scenario can be executed
static var maxExecutionsPermitted: Int? { get set }
// minimum time interval, after the first scenario event was trigerred, before the scenario can be executed
static var minSecondsSinceFirstEvent: TimeInterval? { get set }
// minimum time interval, after the last scenario event was trigerred, before the scenario can be executed
static var minSecondsSinceLastEvent: TimeInterval? { get set }
// minimum time interval before scenario can be executed again after previous execution
static var minSecondsBetweenExecutions: TimeInterval? { get set }
// custom conditions closure
static var customConditions: (() -> Bool)? { get set }
}
Scenario is a simple struct which implements a single config
function. You use it to configure values determining when a given scenario will execute.
You can operate on a scenario struct using static methods:
// increment scenario specific event counter
static func triggerEvent()
// try to execute a scenario (it counts as executed only if bool param passed into a block was `true`)
static func tryToExecute(completion: @escaping (Bool) -> Void)
// reset scenario event and execution counters
static func reset()
Let’s say you want to display a tutorial screen only once:
import WaitForIt
struct ShowTutorial: ScenarioProtocol {
static func config() {
maxExecutionsPermitted = 1
}
}
// In ViewController.swift
func viewDidLoad() {
super.viewDidLoad()
ShowTutorial.tryToExecute { didExecute in
if didExecute {
self.showTutorial()
}
}
}
That’s it! You no longer need to deal with UserDefaults
yourself. Just configure a struct with correct execution conditions, and lib takes care of the rest. When all the conditions for your scenario are fulfilled, bool value passed inside the tryToExecute
block will be true
.
Let’s try a bit more complex scenario. You want to ask user to buy a subscription, if he installed an app at least 1 week ago and turned it on at least 5 times. You want to ask him once every 2 days but no more then 4 times in total:
import WaitForIt
struct AskToSubscribe: ScenarioProtocol {
static func config() {
minEventsRequired = 5
minSecondsSinceFirstEvent = 604 800 // seconds in one week
maxExecutionsPermitted = 4
minSecondsBetweenExecutions = 172 800 // seconds in two days
}
}
// In AppDelegate.swift
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
AskToSubscribe.triggerEvent()
AskToSubscribe.tryToExecute { didExecute in
if didExecute {
self.askToSubscribe()
}
}
return true
}
If time and event count based conditions are not enough for your scenario you can also define a custom conditions closure. It will be evaluated every time you try to execute a scenario:
struct ShowLowBrightnessAlertOnce: ScenarioProtocol {
static func config() {
customConditions = {
return UIScreen.main.brightness < 0.3
}
maxExecutionsPermitted = 1
}
}
Even more complex stories could be implemented if you decided to mix conditions from more then one scenario struct. Of course you could also scatter event triggers and scenario executions throughout the app, they don’t need to be in the same file.
Implementation is based upon standard UserDefaults
so data will not persist if app is reinstalled. UserDefaults
key names are generated with struct names, so renaming the struct will reset all its data. You can also reset persisted data using reset()
method.
Lib is used in production but it is still in an early stage of development. Suggestions on how it could be improved are welcome.