Skip to content

Commit 870b88f

Browse files
authored
Merge pull request #28 from prolificinteractive/feature/custom_killswitch_check
Feature - Custom Killswitch API Check
2 parents 7443de7 + 2dd608b commit 870b88f

File tree

7 files changed

+148
-48
lines changed

7 files changed

+148
-48
lines changed

Bellerophon.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
Pod::Spec.new do |s|
1010
s.name = "Bellerophon"
11-
s.version = "1.1.0"
11+
s.version = "1.2.0"
1212
s.summary = "Kill Switch"
1313

1414
# This description is used to generate tags and improve search results.

Bellerophon/Bellerophon/Sources/BPManager.swift

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@ import UIKit
1010

1111
/// The Bellerophon manager.
1212
public class BellerophonManager: NSObject {
13-
13+
14+
/// Determines if the killswitch or force update view is presenting.
15+
public var isDisplaying: Bool {
16+
return bellerophonWindow.isKeyWindow
17+
}
18+
1419
// MARK: - Initializers
1520

1621
/// The default initializer.
@@ -78,6 +83,30 @@ public class BellerophonManager: NSObject {
7883
}
7984
}
8085

86+
/// Updates the display with the given status.
87+
///
88+
/// - Parameter status: Kill switch status to check if the kill switch view should be displayed.
89+
@objc public func updateDisplay(_ status: BellerophonObservable) {
90+
if status.forceUpdate() {
91+
displayForceUpdate()
92+
} else if status.apiInactive() {
93+
displayKillSwitch()
94+
} else {
95+
dismissKillSwitchIfNeeded()
96+
}
97+
}
98+
99+
/// Dismisses the kill switch window.
100+
@objc public func dismissKillSwitchIfNeeded() {
101+
guard isDisplaying, let currentEvent = currentEvent else {
102+
return
103+
}
104+
config.delegate?.bellerophonWillDisengage(self, event: currentEvent)
105+
config.allViews().forEach { $0.isHidden = true }
106+
mainWindow?.makeKeyAndVisible()
107+
bellerophonWindow.isHidden = true
108+
}
109+
81110
/**
82111
Use this function to retrieve and handle app status when the app has background mode enabled.
83112

@@ -101,25 +130,7 @@ public class BellerophonManager: NSObject {
101130
}
102131
}
103132

104-
// MARK: internal Methods
105-
106-
internal func handleAppStatus(_ status: BellerophonObservable) {
107-
if status.forceUpdate() {
108-
if let forceUpdateView = config.forceUpdateView {
109-
displayWindow(for: .forceUpdate(view: forceUpdateView))
110-
}
111-
config.delegate?.shouldForceUpdate()
112-
startAutoChecking(status)
113-
} else if status.apiInactive() {
114-
if let killSwitchView = config.killSwitchView {
115-
displayWindow(for: .killSwitch(view: killSwitchView))
116-
}
117-
config.delegate?.shouldKillSwitch()
118-
startAutoChecking(status)
119-
} else {
120-
dismissKillSwitchIfNeeded()
121-
}
122-
}
133+
// MARK: Internal Methods
123134

124135
@objc internal func stopTimer() {
125136
retryTimer?.invalidate()
@@ -130,6 +141,28 @@ public class BellerophonManager: NSObject {
130141
config.delegate?.receivedError(error: error)
131142
}
132143

144+
internal func handleAppStatus(_ status: BellerophonObservable) {
145+
updateDisplay(status)
146+
147+
if (status.forceUpdate() || status.apiInactive()) {
148+
startAutoChecking(status)
149+
}
150+
}
151+
152+
internal func displayForceUpdate() {
153+
if let forceUpdateView = config.forceUpdateView {
154+
displayWindow(for: .forceUpdate(view: forceUpdateView))
155+
}
156+
config.delegate?.shouldForceUpdate()
157+
}
158+
159+
internal func displayKillSwitch() {
160+
if let killSwitchView = config.killSwitchView {
161+
displayWindow(for: .killSwitch(view: killSwitchView))
162+
}
163+
config.delegate?.shouldKillSwitch()
164+
}
165+
133166
internal func displayWindow(for event: BellerophonEvent) {
134167
let view = event.view
135168
currentEvent = event
@@ -140,16 +173,6 @@ public class BellerophonManager: NSObject {
140173
bellerophonWindow.makeKeyAndVisible()
141174
}
142175

143-
internal func dismissKillSwitchIfNeeded() {
144-
guard bellerophonWindow.isKeyWindow, let currentEvent = currentEvent else {
145-
return
146-
}
147-
config.delegate?.bellerophonWillDisengage(self, event: currentEvent)
148-
config.allViews().forEach { $0.isHidden = true }
149-
mainWindow?.makeKeyAndVisible()
150-
bellerophonWindow.isHidden = true
151-
}
152-
153176
internal func startAutoChecking(_ status: BellerophonObservable) {
154177
if retryTimer == nil {
155178
retryTimer = Timer.scheduledTimer(timeInterval: status.retryInterval(),
@@ -159,4 +182,5 @@ public class BellerophonManager: NSObject {
159182
repeats: false)
160183
}
161184
}
185+
162186
}

Bellerophon/Bellerophon/Sources/BPObservable.swift

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
//
88

99
/// The Bellerophon observable.
10-
public protocol BellerophonObservable {
10+
@objc public protocol BellerophonObservable {
1111

1212
// MARK: - Required
1313
/**
@@ -47,9 +47,3 @@ public protocol BellerophonObservable {
4747
func setUserMessage(_ message: String)
4848

4949
}
50-
51-
public extension BellerophonObservable {
52-
53-
func setUserMessage(_ message: String) { }
54-
55-
}

Bellerophon/BellerophonTests/BellerophonResponse.swift

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
@testable import Bellerophon
1010

11-
class BellerophonResponse: BellerophonObservable {
11+
class BellerophonResponse: NSObject, BellerophonObservable {
1212

1313
init(isAPIInactive: Bool, shouldForceUpdate: Bool, interval: TimeInterval, userMessageStr: String) {
1414
self.isAPIInactive = isAPIInactive
@@ -22,19 +22,23 @@ class BellerophonResponse: BellerophonObservable {
2222
var interval: TimeInterval = 0
2323
var userMessageStr: String = ""
2424

25-
func apiInactive() -> Bool {
25+
public func apiInactive() -> Bool {
2626
return isAPIInactive
2727
}
2828

29-
func forceUpdate() -> Bool {
29+
public func forceUpdate() -> Bool {
3030
return shouldForceUpdate
3131
}
3232

33-
func retryInterval() -> TimeInterval {
33+
public func retryInterval() -> TimeInterval {
3434
return interval
3535
}
3636

37-
func userMessage() -> String {
37+
public func userMessage() -> String {
3838
return userMessageStr
3939
}
40+
41+
public func setUserMessage(_ message: String) {
42+
43+
}
4044
}

Bellerophon/BellerophonTests/BellerophonTests.swift

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class BellerophonTests: XCTestCase {
1313

1414
lazy var mockManager = MockBPManager(config: BellerophonConfig(window: UIWindow(),
1515
killSwitchView: UIView(),
16-
forceUpdateView: nil,
16+
forceUpdateView: UIView(),
1717
delegate: self))
1818

1919
enum ResponseCases: Int {
@@ -49,6 +49,9 @@ class BellerophonTests: XCTestCase {
4949
shouldKillSwitchIsCalled = false
5050

5151
mockManager.displayKillSwitchIsCalled = false
52+
mockManager.displayForceUpdateIsCalled = false
53+
mockManager.displayKillSwitchFunctionIsCalled = false
54+
mockManager.displayForceUpdateFunctionIsCalled = false
5255
mockManager.dismissKillSwitchIfNeededIsCalled = false
5356
mockManager.startAutoCheckingIsCalled = false
5457
}
@@ -144,7 +147,7 @@ class BellerophonTests: XCTestCase {
144147
// Turn on kill switch first
145148
currentIdx = ResponseCases.KillSwitchOnForceUpdateOff.rawValue
146149
mockManager.checkAppStatus()
147-
150+
148151
// when
149152
// Turn off kill switch after
150153
currentIdx = ResponseCases.KillSwitchOffForceUpdateOff.rawValue
@@ -155,6 +158,57 @@ class BellerophonTests: XCTestCase {
155158
XCTAssertTrue(shouldKillSwitchIsCalled)
156159
}
157160

161+
func test_updateDisplay_withResponseOfKillSwitchOffForceUpdateOn() {
162+
// given
163+
let status = BellerophonResponse(isAPIInactive: false, shouldForceUpdate: true, interval: 0, userMessageStr: "")
164+
165+
// when
166+
mockManager.updateDisplay(status)
167+
168+
// then
169+
XCTAssertFalse(mockManager.displayKillSwitchFunctionIsCalled, "Internal func displayWindowIfPossible for killswitch should not be called")
170+
XCTAssertTrue(mockManager.displayForceUpdateFunctionIsCalled, "Internal func displayWindowIfPossible for force update should be called")
171+
XCTAssertFalse(mockManager.dismissKillSwitchIfNeededIsCalled, "Internal func displayWindowIfPossible for killswitch should not be called")
172+
}
173+
174+
func test_updateDisplay_withResponseOfKillSwitchOnKillSwitchOff() {
175+
// given
176+
let status = BellerophonResponse(isAPIInactive: true, shouldForceUpdate: false, interval: 0, userMessageStr: "")
177+
178+
// when
179+
mockManager.updateDisplay(status)
180+
181+
// then
182+
XCTAssertTrue(mockManager.displayKillSwitchFunctionIsCalled, "Internal func displayWindowIfPossible for killswitch should be called")
183+
XCTAssertFalse(mockManager.displayForceUpdateFunctionIsCalled, "Internal func displayWindowIfPossible for force update should not be called")
184+
XCTAssertFalse(mockManager.dismissKillSwitchIfNeededIsCalled, "Internal func displayWindowIfPossible for killswitch should not be called")
185+
}
186+
187+
func test_updateDisplay_withResponseOfKillSwitchOnForceUpdateOn() {
188+
// given
189+
let status = BellerophonResponse(isAPIInactive: true, shouldForceUpdate: true, interval: 0, userMessageStr: "")
190+
191+
// when
192+
mockManager.updateDisplay(status)
193+
194+
// then
195+
XCTAssertFalse(mockManager.displayKillSwitchFunctionIsCalled, "Internal func displayWindowIfPossible for killswitch should not be called")
196+
XCTAssertTrue(mockManager.displayForceUpdateFunctionIsCalled, "Internal func displayWindowIfPossible for force update should be called")
197+
XCTAssertFalse(mockManager.dismissKillSwitchIfNeededIsCalled, "Internal func displayWindowIfPossible for killswitch should not be called")
198+
}
199+
200+
func test_updateDisplay_withResponseOfKillSwitchOffKillSwitchOff() {
201+
// given
202+
let status = BellerophonResponse(isAPIInactive: false, shouldForceUpdate: false, interval: 0, userMessageStr: "")
203+
204+
// when
205+
mockManager.updateDisplay(status)
206+
207+
// then
208+
XCTAssertFalse(mockManager.displayKillSwitchFunctionIsCalled, "Internal func displayWindowIfPossible for killswitch should be called")
209+
XCTAssertFalse(mockManager.displayForceUpdateFunctionIsCalled, "Internal func displayWindowIfPossible for force update should not be called")
210+
XCTAssertTrue(mockManager.dismissKillSwitchIfNeededIsCalled, "Internal func displayWindowIfPossible for killswitch should not be called")
211+
}
158212
}
159213

160214
extension BellerophonTests: BellerophonManagerDelegate {

Bellerophon/BellerophonTests/MockBPManager.swift

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,16 @@
99
@testable import Bellerophon
1010

1111
class MockBPManager: BellerophonManager {
12-
12+
1313
var displayKillSwitchIsCalled: Bool!
1414
var displayForceUpdateIsCalled: Bool!
1515
var startAutoCheckingIsCalled: Bool!
1616
var dismissKillSwitchIfNeededIsCalled: Bool!
17-
17+
18+
var displayForceUpdateFunctionIsCalled: Bool!
19+
var displayKillSwitchFunctionIsCalled: Bool!
20+
var updateDisplayIsCalled: Bool!
21+
1822
override func displayWindow(for event: BellerophonEvent) {
1923
switch event {
2024
case .killSwitch:
@@ -25,12 +29,28 @@ class MockBPManager: BellerophonManager {
2529
displayKillSwitchIsCalled = false
2630
}
2731
}
32+
33+
override func updateDisplay(_ status: BellerophonObservable) {
34+
updateDisplayIsCalled = true
35+
super.updateDisplay(status)
36+
}
37+
38+
override func displayForceUpdate() {
39+
displayForceUpdateFunctionIsCalled = true
40+
super.displayForceUpdate()
41+
}
42+
43+
override func displayKillSwitch() {
44+
displayKillSwitchFunctionIsCalled = true
45+
super.displayKillSwitch()
46+
}
2847

2948
override func startAutoChecking(_ status: BellerophonObservable) {
3049
startAutoCheckingIsCalled = true
3150
}
3251

3352
override func dismissKillSwitchIfNeeded() {
3453
dismissKillSwitchIfNeededIsCalled = true
54+
super.dismissKillSwitchIfNeeded()
3555
}
3656
}

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ class BellerophonModel: BellerophonObservable { }
8686
@objc func userMessage() -> String {
8787
...
8888
}
89+
90+
@objc func setUserMessage(_ message: String) {
91+
...
92+
}
8993
```
9094

9195
6 - Now that you have your model, you are ready to implement the `BellerophonManagerDelegate` methods in your App Delegate.
@@ -170,4 +174,4 @@ Bellerophon is Copyright (c) 2015 Prolific Interactive. It may be redistributed
170174

171175
![prolific](https://s3.amazonaws.com/prolificsitestaging/logos/Prolific_Logo_Full_Color.png)
172176

173-
Bellerophon is maintained and funded by Prolific Interactive. The names and logos are trademarks of Prolific Interactive.
177+
Bellerophon is maintained and funded by Prolific Interactive. The names and logos are trademarks of Prolific Interactive.

0 commit comments

Comments
 (0)