Skip to content

Commit 751f074

Browse files
committed
Initial Commit
0 parents  commit 751f074

37 files changed

+3199
-0
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.DS_Store
2+
/.build
3+
/Packages
4+
xcuserdata/
5+
/Carthage
6+
ReviewKit.framework.zip

.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>IDEDidComputeMac32BitWarning</key>
6+
<true/>
7+
</dict>
8+
</plist>

Info.plist

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>CFBundleDevelopmentRegion</key>
6+
<string>$(DEVELOPMENT_LANGUAGE)</string>
7+
<key>CFBundleExecutable</key>
8+
<string>$(EXECUTABLE_NAME)</string>
9+
<key>CFBundleIdentifier</key>
10+
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11+
<key>CFBundleInfoDictionaryVersion</key>
12+
<string>6.0</string>
13+
<key>CFBundleName</key>
14+
<string>$(PRODUCT_NAME)</string>
15+
<key>CFBundlePackageType</key>
16+
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
17+
<key>CFBundleShortVersionString</key>
18+
<string>1.0.0</string>
19+
<key>CFBundleVersion</key>
20+
<string>$(CURRENT_PROJECT_VERSION)</string>
21+
</dict>
22+
</plist>

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2020 Simon Mitchell
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Package.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// swift-tools-version:5.1
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "ReviewKit",
8+
products: [
9+
// Products define the executables and libraries produced by a package, and make them visible to other packages.
10+
.library(
11+
name: "ReviewKit",
12+
targets: ["ReviewKit"]),
13+
],
14+
dependencies: [
15+
16+
],
17+
targets: [
18+
.target(
19+
name: "ReviewKit",
20+
dependencies: []),
21+
.testTarget(
22+
name: "ReviewKitTests",
23+
dependencies: ["ReviewKit"]),
24+
]
25+
)

README.md

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
# ReviewKit
2+
3+
<p>
4+
<a href="https://github.com/Carthage/Carthage">
5+
<img alt="Carthage Compatible" src="https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat">
6+
</a>
7+
<a href="https://swift.org/blog/swift-5-1-released/">
8+
<img alt="Swift 5.0" src="http://img.shields.io/badge/swift-5.1-brightgreen.svg">
9+
</a>
10+
<a href="https://github.com/simonmitchell/ReviewKit/blob/master/README.md">
11+
<img alt="MIT" src="https://img.shields.io/badge/license-MIT-brightgreen.svg">
12+
</a>
13+
</p>
14+
15+
ReviewKit is a Swift package/framework that provides logic designed to prompt users at the ideal moment for a review of your app. At a basic level it works by beginning an "App Session" and then sending "Actions" – which contain a "Score" and a few other properties – to the main controller object. This allows the library to track things such as:
16+
17+
- The last date a review prompt was requested.
18+
- The last session in which a review prompt was requested.
19+
- The last version of the app for which a review prompt was requested.
20+
- The average score of each session.
21+
- The total number of sessions a user has had.
22+
23+
This library **DOES NOT** provide any kind of review UI, it was primarily designed with iOS in mind – which Apple don't allow custom review prompts on – although you can easily configure it to show your own review prompt on other platforms. The reason that it doesn't provide any UI is due to Apple's strict shutdown and rejection of app's which provide their own UI to "filter" bad reviews out.
24+
25+
This library attempts to provide some of that filtering behind the scenes by requiring a strict set of criteria ([All configurable](#configuration)) before requesting a review prompt to be shown. It also provides default timeouts between subsequent prompts which is particularly important when using `SKStoreReviewController` as Apple do not provide any kind of timeout for it's method.
26+
27+
Usage on other platforms is outlined [below](#other-platforms)
28+
29+
## Installation
30+
31+
### CocoaPods
32+
[Carthage](https://github.com/Carthage/Carthage) is a package manager which either builds projects and provides you with binaries or uses pre-built frameworks from release tags in GitHub. To add ReviewKit to your project, simply specify it in your `Cartfile`:
33+
34+
```ogdl
35+
github "simonmitchell/ReviewKit" ~> 1.0.0
36+
```
37+
38+
### Swift Package Manager
39+
[Swift Package Manager](https://swift.org/package-manager/) is apple's dependency manager for distributing Swift code. It is embedded into Xcode, but can also be used in the command line.
40+
41+
To add ReviewKit to your project simply add it to your dependencies array:
42+
43+
```swift
44+
dependencies: [
45+
.package(url: "https://github.com/simonmitchell/ReviewKit.git", .upToNextMajor(from: "1.0.0"))
46+
]
47+
```
48+
49+
### Manual
50+
Manual installation (as of iOS 13) requires a paid developer license in order to run on a physical device.
51+
52+
1. Download, or checkout in Git the source code.
53+
1. Drag the ReviewKit.xcodeproj file into your own project.
54+
1. Drag ReviewKit.framework from the project navigator into the "Frameworks, Libraries and Embedded Content" section of your project.
55+
1. Make sure "Embed" is set to "Embed & Sign"
56+
57+
## Usage
58+
59+
### Starting a Session
60+
To start a new app session, simply call:
61+
62+
```swift
63+
RequestController.startSession(version: .init(major: 1, minor: 0, patch: 0))
64+
```
65+
66+
It is very important you call this before logging actions, otherwise they will be stored under a default session with app version `1.0.0`
67+
68+
### Logging an action
69+
To log an action, then call
70+
71+
```swift
72+
requestController.log(action: .init(score: 50, showReview: true, isBad: false)
73+
```
74+
75+
| Property | Description |
76+
|---|---|
77+
| score | The score of the action, positive actions should have a positive score, and negative should have a negative. The scale is up to you as a user of this library. |
78+
| showReview | Whether a review can be shown directly after this action has occured, this should be true if you deem the action to be important enough to have a review shown after it. Good examples may be: The user has just finished a checkout process, or the user has just completed a level of your game. |
79+
| isBad | Whether an action was so bad, that it would entirely put the user off your app and therefore you may not want to show them a review at all, or even for a few sessions or cooldown period afterwards. |
80+
81+
### Resetting
82+
If for some reason you want to reset all sessions, and start from scratch entirely you can call:
83+
84+
```swift
85+
requestController.reset()
86+
```
87+
88+
### Providing custom storage for the session history
89+
By default all historical review and session information is stored in `UserDefaults.standard` however there is a `ReviewRequestControllerStorage` protocol which you can implement and then set on the controller object like so:
90+
91+
```swift
92+
requestController.storage = myCustomStorageInstance
93+
```
94+
You may want to do this if you want to sync the session history and prompt history between multiple devices or platforms.
95+
96+
### Showing Custom Prompts
97+
98+
```swift
99+
var reviewRequester: ReviewRequester?
100+
```
101+
102+
You can control the review prompt that is shown by implementing the `ReviewRequester` protocol in your code, and setting this property. This defaults to `AppStoreReviewRequester` on supported operating systems which is a wrapper that calls `SKStoreReviewController.requestReview()`.
103+
104+
⚠️ **If you are using this library on an operating system which doesn't support `SKStoreReviewController` you MUST provide a value for this property.** ⚠️
105+
106+
## Configuration
107+
108+
### Score
109+
110+
```swift
111+
var scoreThreshold: Double = 100.0
112+
```
113+
The score which the current session has to reach before the review prompt is shown.
114+
115+
### Average Score
116+
117+
```swift
118+
var averageScoreThreshold: (score: Double, sessions: Int) = (score: 75, sessions: 3)
119+
```
120+
This determines the threshold for the average score that must have been achieved over the given number of previous sessions for the review prompt to be shown. This can be disabled if `sessions` is set to (or below) `0`.
121+
122+
### Score Bounds
123+
124+
```swift
125+
var scoreBounds: ClosedRange<Double>? = -200.0...200.0
126+
```
127+
This can be used to bound the score for the current session, it stops the current sessions score from getting too out of hand in either a negative or positive direction. This can be disabled by setting it to `nil`.
128+
129+
### Initial Request Timeout
130+
131+
```swift
132+
var initialRequestTimeout: Timeout = Timeout(sessions: 2, duration: 4 * .day, operation: .and)
133+
```
134+
This defines the minumum app usage from first launch, in sessions and/or time interval to display a review prompt to the user. This can be disabled by setting both `sessions` and `duration` to 0. Values are non-inclusive so the above means the app user must be on their third session and having used the app for over (to the second) 4 days.
135+
136+
### Subsequent Request Timeout
137+
138+
```swift
139+
var reviewRequestTimeout: Timeout = Timeout(sessions: 4, duration: 8 * .week, operation: .and)
140+
```
141+
This defines the minumum app usage in sessions and/or time interval since the last time a review prompt was displayed to the user. This can be disabled by setting both `sessions` and `duration` to 0. Values are non-inclusive so the above means the app user must be on their 5th session after the previous prompt and having used the app for an additional (to the second) 8 weeks.
142+
143+
### Version Difference
144+
145+
```swift
146+
var reviewVersionTimeout: Version? = Version(major: 0, minor: 0, patch: 1)
147+
```
148+
The minimum version change that must have taken place since the last review was prompted before another is shown.
149+
150+
### Disable During a "bad" Session
151+
152+
```swift
153+
var disabledForBadSession: Bool = true
154+
```
155+
Setting this to false will allow the review prompt to be shown even if the current session was previously marked as "bad"
156+
157+
### Bad Session Timeout
158+
159+
```swift
160+
var badSessionTimeout: Timeout = Timeout(sessions: 2, duration: 2 * .day, operation: .or)
161+
```
162+
The minimum number of sessions and/or time since the last bad session occured.
163+
164+
### Other Platforms
165+
If your platform doesn't have Foundation's `UserDefaults` you can provide your own storage using:
166+
167+
```swift
168+
requestController.storage = myCustomStorageInstance
169+
```
170+
171+
If your platform doesn't support `SKStoreReviewController` you can provide a custom request controller using:
172+
173+
```swift
174+
requestController.reviewRequester = myCustomRequester
175+
```

ReviewKit.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//
2+
// ReviewKit.h
3+
// ReviewKit
4+
//
5+
// Created by Simon Mitchell on 18/03/2020.
6+
// Copyright © 2020 Simon Mitchell. All rights reserved.
7+
//
8+
9+
#import <Foundation/Foundation.h>
10+
11+
//! Project version number for ReviewKit.
12+
FOUNDATION_EXPORT double ReviewKitVersionNumber;
13+
14+
//! Project version string for ReviewKit.
15+
FOUNDATION_EXPORT const unsigned char ReviewKitVersionString[];
16+
17+
// In this header, you should import all the public headers of your framework using statements like #import <ReviewKit/PublicHeader.h>
18+
19+

0 commit comments

Comments
 (0)