• This repository has been archived on 22/Dec/2023
  • Stars
    star
    26
  • Rank 899,404 (Top 19 %)
  • Language
    Swift
  • License
    MIT License
  • Created over 6 years ago
  • Updated over 6 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Events and time based iOS app scenarios made easy.

WaitForIt Build Status Pod version Carthage compatible

WaitForIt makes implementing a common iOS app scenarios a breeze:

  • "Display a tutorial screen only when user launches an app for the first time."
  • "Ask user for a review, but only if he installed the app more then two weeks ago and launched it at least 5 times."
  • "Ask registered user to buy a subscription once every 3 days, but no more then 5 times in total."
  • "Ask user to share some content on Facebook, if he did not do it since 4 days."

Dealing with this kind of logic usually involves manually saving data to UserDefaults and has to be redone from scratch for each scenario.

Usage

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()

Basic example

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.

More conditions

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
}

Custom conditions

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.

Installation

Carthage

In your Cartfile:

github "pawurb/WaitForIt" ~> 2.0.0

Cocoapods

In your Podfile:

platform :ios, '10.0'
use_frameworks!

target 'TargetName' do
  pod 'WaitForIt'
end

Status

Lib is used in production but it is still in an early stage of development. Suggestions on how it could be improved are welcome.

More Repositories

1

rails-pg-extras

Rails PostgreSQL database performance insights. Locks, index usage, buffer cache hit ratios, vacuum stats and more.
Ruby
1,072
star
2

termit

Translations with speech synthesis in your terminal as a ruby gem
Ruby
507
star
3

ecto_psql_extras

Ecto PostgreSQL database performance insights. Locks, index usage, buffer cache hit ratios, vacuum stats and more.
Elixir
351
star
4

rails-brotli-cache

Drop-in enhancement for Rails cache, offering better performance and compression with Brotli algorithm
Ruby
252
star
5

normit

Translations with speech synthesis in your terminal as a node package
JavaScript
239
star
6

activerecord-analyze

Add EXPLAIN ANALYZE to Rails Active Record query objects
Ruby
209
star
7

smart_init

A simple gem for eliminating Ruby initializers boilerplate code, and providing unified service objects API
Ruby
177
star
8

ruby-pg-extras

Ruby PostgreSQL database performance insights. Locks, index usage, buffer cache hit ratios, vacuum stats and more.
Ruby
123
star
9

node-postgres-extras

NodeJS PostgreSQL database performance insights. Locks, index usage, buffer cache hit ratios, vacuum stats and more.
JavaScript
70
star
10

ecto_extras

Ecto helper functions.
Elixir
36
star
11

devloop

An automated test runner for Rails that instantly executes specs based on a recent git diff output.
Ruby
36
star
12

lazyme

A simple gem to help you optimize your shell workflow
Ruby
35
star
13

python-pg-extras

Python PostgreSQL database performance insights. Locks, index usage, buffer cache hit ratios, vacuum stats and more.
Python
35
star
14

Locker

Ethereum Smart Contracts for locking Ether, ERC20 and ERC721 tokens based on time and price conditions
TypeScript
28
star
15

.dotfiles

My development environment settings.
Shell
13
star
16

haskell-pg-extras

Haskell PostgreSQL database performance insights. Locks, index usage, buffer cache hit ratios, vacuum stats and more.
Haskell
9
star
17

ruby-jemalloc-node-yarn

Docker image of Ruby with Jemalloc Node 16 LTS and Yarn
Dockerfile
9
star
18

railsSearchKit

This Chrome extension provides easy access to the search bars every Rails developer needs.
JavaScript
8
star
19

dont_you_count

Disable count queries for selected Active Admin tables.
Ruby
7
star
20

pi-hole-docker-compose

pi-hole-docker-compose
6
star
21

activerecord-implicit-order

Ruby
5
star
22

delegate_it

A drop in replacement for ActiveSupport delegate method in non Rails projects.
Ruby
4
star
23

active-admin-tips

Active Admin tips and performance optimizations in action
Ruby
4
star
24

rust-pg-extras

Rust PostgreSQL database performance insights. Locks, index usage, buffer cache hit ratios, vacuum stats and more.
Rust
3
star
25

Siorbackend

Ruby
1
star
26

abstract_base

Abstract Class pattern Ruby gem
Ruby
1
star
27

FRP_introduction

Comparison between observer and reactive approach to login form validations.
JavaScript
1
star
28

focus.apki.io

Landing page for Focus app
HTML
1
star