-
Notifications
You must be signed in to change notification settings - Fork 85
/
Copy pathCountdownDisplayService.swift
156 lines (118 loc) · 4.04 KB
/
CountdownDisplayService.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//
// CountdownDisplayService.swift
// Lockdown
//
// Created by Aliaksandr Dvoineu on 4.05.23.
// Copyright © 2023 Confirmed Inc. All rights reserved.
//
import CocoaLumberjackSwift
import Foundation
import UIKit
@objc protocol CountdownDisplayDelegate: AnyObject {
func didFinishCountdown()
}
protocol CountdownDisplayService: AnyObject {
var seconds: TimeInterval { get set }
var delegates: [WeakObject<CountdownDisplayDelegate>?] { get set }
func startUpdating(hourLabel: UILabel?, minuteLabel: UILabel?, secondLabel: UILabel)
func startUpdating(button: UIButton)
func pauseUpdating()
func stopAndRemoveLTO()
}
final class BaseCountdownDisplayService: CountdownDisplayService {
static let shared: CountdownDisplayService = BaseCountdownDisplayService(seconds: 60)
private weak var button: UIButton?
private weak var hourLabel: UILabel?
private weak var minuteLabel: UILabel?
private weak var secondLabel: UILabel?
var seconds: TimeInterval
var delegates: [WeakObject<CountdownDisplayDelegate>?] = []
private var timer: Timer?
private init(seconds: TimeInterval) {
self.seconds = seconds
}
func startUpdating(hourLabel: UILabel? = nil, minuteLabel: UILabel? = nil, secondLabel: UILabel) {
self.hourLabel = hourLabel
self.minuteLabel = minuteLabel
self.secondLabel = secondLabel
timer?.invalidate()
runTimer {
DDLogInfo("Started updating LTO labels.")
self.updateLabels()
}
}
func startUpdating(button: UIButton) {
self.button = button
timer?.invalidate()
runTimer {
DDLogInfo("Started updating LTO button.")
DispatchQueue.main.async {
self.updateButton()
}
}
}
func pauseUpdating() {
timer?.invalidate()
timer = nil
}
func stopAndRemoveLTO() {
BasePaywallService.shared.context = .normal
DDLogInfo("Finished countdown. Changing context to \(BasePaywallService.shared.context).")
DDLogInfo("Notifying all delegates.")
self.delegates.forEach { $0?.object?.didFinishCountdown() }
DDLogInfo("Clearing singleton references.")
self.clearAllData()
}
private func runTimer(updateUI: @escaping () -> Void) {
forceUpdate()
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
guard let self else { return }
if self.seconds > 0 {
self.seconds -= 1
updateUI()
} else {
self.stopAndRemoveLTO()
}
}
}
private func forceUpdate() {
updateLabels()
updateButton()
}
private func updateButton() {
let minutes = self.timeString(for: .minute)
let seconds = self.timeString(for: .second)
let timeString = minutes + ":" + seconds
button?.setTitle(timeString, for: .normal)
}
private func updateLabels() {
DispatchQueue.main.async {
self.hourLabel?.text = self.timeString(for: .hour)
self.minuteLabel?.text = self.timeString(for: .minute)
self.secondLabel?.text = self.timeString(for: .second)
}
}
private func timeString(for component: CountdownDisplayComponent) -> String {
let time: Int
switch component {
case .hour:
time = Int(seconds) / 3600
case .minute:
time = Int(seconds) / 60 % 60
case .second:
time = Int(seconds) % 60
}
return String(format:"%02i", time)
}
private func clearAllData() {
pauseUpdating()
delegates = []
button = nil
hourLabel = nil
minuteLabel = nil
secondLabel = nil
}
}
enum CountdownDisplayComponent {
case hour, minute, second
}